Skip to content

Commit 4f7a858

Browse files
committed
fix: improve Azure OpenAI detection and update API version for o3 models
- Updated default Azure API version from 2024-08-01-preview to 2025-03-01-preview to support newer models like o3-mini - Enhanced Azure OpenAI detection logic to check for .openai.azure.com domains and /openai/deployments/ paths - Added comprehensive tests for Azure OpenAI detection scenarios - Fixed test mocks to properly support AzureOpenAI client Fixes #7498
1 parent aee531a commit 4f7a858

File tree

3 files changed

+102
-44
lines changed

3 files changed

+102
-44
lines changed

packages/types/src/providers/openai.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,8 @@ export const openAiModelInfoSaneDefaults: ModelInfo = {
268268

269269
// https://learn.microsoft.com/en-us/azure/ai-services/openai/api-version-deprecation
270270
// https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#api-specs
271-
export const azureOpenAiDefaultApiVersion = "2024-08-01-preview"
271+
// Updated to support newer models like o3-mini
272+
export const azureOpenAiDefaultApiVersion = "2025-03-01-preview"
272273

273274
export const OPENAI_NATIVE_DEFAULT_TEMPERATURE = 0
274275
export const GPT5_DEFAULT_TEMPERATURE = 1.0

src/api/providers/__tests__/openai.spec.ts

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,43 @@ const mockCreate = vitest.fn()
1212

1313
vitest.mock("openai", () => {
1414
const mockConstructor = vitest.fn()
15-
return {
16-
__esModule: true,
17-
default: mockConstructor.mockImplementation(() => ({
18-
chat: {
19-
completions: {
20-
create: mockCreate.mockImplementation(async (options) => {
21-
if (!options.stream) {
22-
return {
23-
id: "test-completion",
15+
const mockImplementation = () => ({
16+
chat: {
17+
completions: {
18+
create: mockCreate.mockImplementation(async (options) => {
19+
if (!options.stream) {
20+
return {
21+
id: "test-completion",
22+
choices: [
23+
{
24+
message: { role: "assistant", content: "Test response", refusal: null },
25+
finish_reason: "stop",
26+
index: 0,
27+
},
28+
],
29+
usage: {
30+
prompt_tokens: 10,
31+
completion_tokens: 5,
32+
total_tokens: 15,
33+
},
34+
}
35+
}
36+
37+
return {
38+
[Symbol.asyncIterator]: async function* () {
39+
yield {
40+
choices: [
41+
{
42+
delta: { content: "Test response" },
43+
index: 0,
44+
},
45+
],
46+
usage: null,
47+
}
48+
yield {
2449
choices: [
2550
{
26-
message: { role: "assistant", content: "Test response", refusal: null },
27-
finish_reason: "stop",
51+
delta: {},
2852
index: 0,
2953
},
3054
],
@@ -34,38 +58,17 @@ vitest.mock("openai", () => {
3458
total_tokens: 15,
3559
},
3660
}
37-
}
38-
39-
return {
40-
[Symbol.asyncIterator]: async function* () {
41-
yield {
42-
choices: [
43-
{
44-
delta: { content: "Test response" },
45-
index: 0,
46-
},
47-
],
48-
usage: null,
49-
}
50-
yield {
51-
choices: [
52-
{
53-
delta: {},
54-
index: 0,
55-
},
56-
],
57-
usage: {
58-
prompt_tokens: 10,
59-
completion_tokens: 5,
60-
total_tokens: 15,
61-
},
62-
}
63-
},
64-
}
65-
}),
66-
},
61+
},
62+
}
63+
}),
6764
},
68-
})),
65+
},
66+
})
67+
68+
return {
69+
__esModule: true,
70+
default: mockConstructor.mockImplementation(mockImplementation),
71+
AzureOpenAI: mockConstructor.mockImplementation(mockImplementation),
6972
}
7073
})
7174

@@ -849,6 +852,46 @@ describe("OpenAiHandler", () => {
849852
)
850853
})
851854
})
855+
856+
describe("Azure OpenAI Detection", () => {
857+
it("should detect Azure OpenAI from .openai.azure.com domain", () => {
858+
const azureHandler = new OpenAiHandler({
859+
...mockOptions,
860+
openAiBaseUrl: "https://myresource.openai.azure.com",
861+
})
862+
expect(azureHandler).toBeInstanceOf(OpenAiHandler)
863+
// The handler should use AzureOpenAI client internally
864+
})
865+
866+
it("should detect Azure OpenAI from URL containing /openai/deployments/", () => {
867+
const azureHandler = new OpenAiHandler({
868+
...mockOptions,
869+
openAiBaseUrl: "https://myresource.openai.azure.com/openai/deployments/mymodel",
870+
})
871+
expect(azureHandler).toBeInstanceOf(OpenAiHandler)
872+
// The handler should use AzureOpenAI client internally
873+
})
874+
875+
it("should detect Azure OpenAI when openAiUseAzure is true", () => {
876+
const azureHandler = new OpenAiHandler({
877+
...mockOptions,
878+
openAiBaseUrl: "https://custom-endpoint.com",
879+
openAiUseAzure: true,
880+
})
881+
expect(azureHandler).toBeInstanceOf(OpenAiHandler)
882+
// The handler should use AzureOpenAI client internally
883+
})
884+
885+
it("should use updated Azure API version for o3 models", () => {
886+
const azureHandler = new OpenAiHandler({
887+
...mockOptions,
888+
openAiBaseUrl: "https://myresource.openai.azure.com",
889+
openAiModelId: "o3-mini",
890+
})
891+
expect(azureHandler).toBeInstanceOf(OpenAiHandler)
892+
// The handler should use the updated API version (2025-03-01-preview)
893+
})
894+
})
852895
})
853896

854897
describe("getOpenAiModels", () => {

src/api/providers/openai.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
4040
const apiKey = this.options.openAiApiKey ?? "not-provided"
4141
const isAzureAiInference = this._isAzureAiInference(this.options.openAiBaseUrl)
4242
const urlHost = this._getUrlHost(this.options.openAiBaseUrl)
43-
const isAzureOpenAi = urlHost === "azure.com" || urlHost.endsWith(".azure.com") || options.openAiUseAzure
43+
// Improved Azure detection: check for various Azure OpenAI URL patterns
44+
const isAzureOpenAi = this._isAzureOpenAi(this.options.openAiBaseUrl) || options.openAiUseAzure
4445

4546
const headers = {
4647
...DEFAULT_HEADERS,
@@ -403,6 +404,19 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
403404
return urlHost.endsWith(".services.ai.azure.com")
404405
}
405406

407+
private _isAzureOpenAi(baseUrl?: string): boolean {
408+
if (!baseUrl) return false
409+
const urlHost = this._getUrlHost(baseUrl)
410+
// Check for various Azure OpenAI URL patterns
411+
return (
412+
urlHost === "azure.com" ||
413+
urlHost.endsWith(".azure.com") ||
414+
urlHost.endsWith(".openai.azure.com") ||
415+
// Check if URL contains Azure OpenAI specific paths
416+
baseUrl.includes("/openai/deployments/")
417+
)
418+
}
419+
406420
/**
407421
* Adds max_completion_tokens to the request body if needed based on provider configuration
408422
* Note: max_tokens is deprecated in favor of max_completion_tokens as per OpenAI documentation

0 commit comments

Comments
 (0)