@@ -15,8 +15,7 @@ import { convertToSimpleMessages } from "../transform/simple-format"
1515import { ApiStream , ApiStreamUsageChunk } from "../transform/stream"
1616import { BaseProvider } from "./base-provider"
1717import { XmlMatcher } from "../../utils/xml-matcher"
18-
19- const DEEP_SEEK_DEFAULT_TEMPERATURE = 0.6
18+ import { DEEP_SEEK_DEFAULT_TEMPERATURE } from "./constants"
2019
2120export const defaultHeaders = {
2221 "HTTP-Referer" : "https://github.com/RooVetGit/Roo-Cline" ,
@@ -25,6 +24,8 @@ export const defaultHeaders = {
2524
2625export interface OpenAiHandlerOptions extends ApiHandlerOptions { }
2726
27+ const AZURE_AI_INFERENCE_PATH = "/models/chat/completions"
28+
2829export class OpenAiHandler extends BaseProvider implements SingleCompletionHandler {
2930 protected options : OpenAiHandlerOptions
3031 private client : OpenAI
@@ -35,17 +36,19 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
3536
3637 const baseURL = this . options . openAiBaseUrl ?? "https://api.openai.com/v1"
3738 const apiKey = this . options . openAiApiKey ?? "not-provided"
38- let urlHost : string
39-
40- try {
41- urlHost = new URL ( this . options . openAiBaseUrl ?? "" ) . host
42- } catch ( error ) {
43- // Likely an invalid `openAiBaseUrl`; we're still working on
44- // proper settings validation.
45- urlHost = ""
46- }
39+ const isAzureAiInference = this . _isAzureAiInference ( this . options . openAiBaseUrl )
40+ const urlHost = this . _getUrlHost ( this . options . openAiBaseUrl )
41+ const isAzureOpenAi = urlHost === "azure.com" || urlHost . endsWith ( ".azure.com" ) || options . openAiUseAzure
4742
48- if ( urlHost === "azure.com" || urlHost . endsWith ( ".azure.com" ) || options . openAiUseAzure ) {
43+ if ( isAzureAiInference ) {
44+ // Azure AI Inference Service (e.g., for DeepSeek) uses a different path structure
45+ this . client = new OpenAI ( {
46+ baseURL,
47+ apiKey,
48+ defaultHeaders,
49+ defaultQuery : { "api-version" : this . options . azureApiVersion || "2024-05-01-preview" } ,
50+ } )
51+ } else if ( isAzureOpenAi ) {
4952 // Azure API shape slightly differs from the core API shape:
5053 // https://github.com/openai/openai-node?tab=readme-ov-file#microsoft-azure-openai
5154 this . client = new AzureOpenAI ( {
@@ -64,6 +67,8 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
6467 const modelUrl = this . options . openAiBaseUrl ?? ""
6568 const modelId = this . options . openAiModelId ?? ""
6669 const enabledR1Format = this . options . openAiR1FormatEnabled ?? false
70+ const isAzureAiInference = this . _isAzureAiInference ( modelUrl )
71+ const urlHost = this . _getUrlHost ( modelUrl )
6772 const deepseekReasoner = modelId . includes ( "deepseek-reasoner" ) || enabledR1Format
6873 const ark = modelUrl . includes ( ".volces.com" )
6974 if ( modelId . startsWith ( "o3-mini" ) ) {
@@ -132,7 +137,10 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
132137 requestOptions . max_tokens = modelInfo . maxTokens
133138 }
134139
135- const stream = await this . client . chat . completions . create ( requestOptions )
140+ const stream = await this . client . chat . completions . create (
141+ requestOptions ,
142+ isAzureAiInference ? { path : AZURE_AI_INFERENCE_PATH } : { } ,
143+ )
136144
137145 const matcher = new XmlMatcher (
138146 "think" ,
@@ -185,7 +193,10 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
185193 : [ systemMessage , ...convertToOpenAiMessages ( messages ) ] ,
186194 }
187195
188- const response = await this . client . chat . completions . create ( requestOptions )
196+ const response = await this . client . chat . completions . create (
197+ requestOptions ,
198+ this . _isAzureAiInference ( modelUrl ) ? { path : AZURE_AI_INFERENCE_PATH } : { } ,
199+ )
189200
190201 yield {
191202 type : "text" ,
@@ -212,12 +223,16 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
212223
213224 async completePrompt ( prompt : string ) : Promise < string > {
214225 try {
226+ const isAzureAiInference = this . _isAzureAiInference ( this . options . openAiBaseUrl )
215227 const requestOptions : OpenAI . Chat . Completions . ChatCompletionCreateParamsNonStreaming = {
216228 model : this . getModel ( ) . id ,
217229 messages : [ { role : "user" , content : prompt } ] ,
218230 }
219231
220- const response = await this . client . chat . completions . create ( requestOptions )
232+ const response = await this . client . chat . completions . create (
233+ requestOptions ,
234+ isAzureAiInference ? { path : AZURE_AI_INFERENCE_PATH } : { } ,
235+ )
221236 return response . choices [ 0 ] ?. message . content || ""
222237 } catch ( error ) {
223238 if ( error instanceof Error ) {
@@ -233,19 +248,24 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
233248 messages : Anthropic . Messages . MessageParam [ ] ,
234249 ) : ApiStream {
235250 if ( this . options . openAiStreamingEnabled ?? true ) {
236- const stream = await this . client . chat . completions . create ( {
237- model : modelId ,
238- messages : [
239- {
240- role : "developer" ,
241- content : `Formatting re-enabled\n${ systemPrompt } ` ,
242- } ,
243- ...convertToOpenAiMessages ( messages ) ,
244- ] ,
245- stream : true ,
246- stream_options : { include_usage : true } ,
247- reasoning_effort : this . getModel ( ) . info . reasoningEffort ,
248- } )
251+ const methodIsAzureAiInference = this . _isAzureAiInference ( this . options . openAiBaseUrl )
252+
253+ const stream = await this . client . chat . completions . create (
254+ {
255+ model : modelId ,
256+ messages : [
257+ {
258+ role : "developer" ,
259+ content : `Formatting re-enabled\n${ systemPrompt } ` ,
260+ } ,
261+ ...convertToOpenAiMessages ( messages ) ,
262+ ] ,
263+ stream : true ,
264+ stream_options : { include_usage : true } ,
265+ reasoning_effort : this . getModel ( ) . info . reasoningEffort ,
266+ } ,
267+ methodIsAzureAiInference ? { path : AZURE_AI_INFERENCE_PATH } : { } ,
268+ )
249269
250270 yield * this . handleStreamResponse ( stream )
251271 } else {
@@ -260,7 +280,12 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
260280 ] ,
261281 }
262282
263- const response = await this . client . chat . completions . create ( requestOptions )
283+ const methodIsAzureAiInference = this . _isAzureAiInference ( this . options . openAiBaseUrl )
284+
285+ const response = await this . client . chat . completions . create (
286+ requestOptions ,
287+ methodIsAzureAiInference ? { path : AZURE_AI_INFERENCE_PATH } : { } ,
288+ )
264289
265290 yield {
266291 type : "text" ,
@@ -289,6 +314,18 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
289314 }
290315 }
291316 }
317+ private _getUrlHost ( baseUrl ?: string ) : string {
318+ try {
319+ return new URL ( baseUrl ?? "" ) . host
320+ } catch ( error ) {
321+ return ""
322+ }
323+ }
324+
325+ private _isAzureAiInference ( baseUrl ?: string ) : boolean {
326+ const urlHost = this . _getUrlHost ( baseUrl )
327+ return urlHost . endsWith ( ".services.ai.azure.com" )
328+ }
292329}
293330
294331export async function getOpenAiModels ( baseUrl ?: string , apiKey ?: string ) {
0 commit comments