From 1acc0aee74aa6a77a33d999febd90962a63234ba Mon Sep 17 00:00:00 2001 From: Roomote Date: Sat, 8 Aug 2026 01:04:27 +0000 Subject: [PATCH 01/10] fix(provider): support Azure OpenAI resource endpoints --- src/api/providers/__tests__/openai.spec.ts | 126 +++++++++++------- src/api/providers/openai.ts | 3 +- .../settings/providers/OpenAICompatible.tsx | 24 +++- .../__tests__/OpenAICompatible.spec.tsx | 44 +++++- webview-ui/src/i18n/locales/en/settings.json | 3 + 5 files changed, 148 insertions(+), 52 deletions(-) diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index e8146a999a..5fd4a57631 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -3,7 +3,7 @@ import { OpenAiHandler, getOpenAiModels } from "../openai" import { ApiHandlerOptions } from "../../../shared/api" import { Anthropic } from "@anthropic-ai/sdk" -import OpenAI from "openai" +import OpenAI, { AzureOpenAI } from "openai" import { openAiModelInfoSaneDefaults, DEEP_SEEK_DEFAULT_TEMPERATURE } from "@roo-code/types" import { Package } from "../../../shared/package" import { makeApiHandlerOptions } from "../../../test-utils/api" @@ -20,60 +20,63 @@ const mockCreate = vitest.fn() vitest.mock("openai", () => { const mockConstructor = vitest.fn() - return { - __esModule: true, - default: mockConstructor.mockImplementation(function () { - return { - chat: { - completions: { - create: mockCreate.mockImplementation(async (options) => { - if (!options.stream) { - return { - id: "test-completion", - choices: [ - { - message: { role: "assistant", content: "Test response", refusal: null }, - finish_reason: "stop", - index: 0, - }, - ], - usage: { - prompt_tokens: 10, - completion_tokens: 5, - total_tokens: 15, + const mockAzureConstructor = vitest.fn() + function createClient() { + return { + chat: { + completions: { + create: mockCreate.mockImplementation(async (options) => { + if (!options.stream) { + return { + id: "test-completion", + choices: [ + { + message: { role: "assistant", content: "Test response", refusal: null }, + finish_reason: "stop", + index: 0, }, - } + ], + usage: { + prompt_tokens: 10, + completion_tokens: 5, + total_tokens: 15, + }, } + } - return asyncStreamFrom([ - { - choices: [ - { - delta: { content: "Test response" }, - index: 0, - }, - ], - usage: null, - }, - { - choices: [ - { - delta: {}, - index: 0, - }, - ], - usage: { - prompt_tokens: 10, - completion_tokens: 5, - total_tokens: 15, + return asyncStreamFrom([ + { + choices: [ + { + delta: { content: "Test response" }, + index: 0, + }, + ], + usage: null, + }, + { + choices: [ + { + delta: {}, + index: 0, }, + ], + usage: { + prompt_tokens: 10, + completion_tokens: 5, + total_tokens: 15, }, - ]) - }), - }, + }, + ]) + }), }, - } - }), + }, + } + } + return { + __esModule: true, + default: mockConstructor.mockImplementation(createClient), + AzureOpenAI: mockAzureConstructor.mockImplementation(createClient), } }) @@ -126,6 +129,31 @@ describe("OpenAiHandler", () => { timeout: MOCK_TIMEOUT_MS, }) }) + + it.each([ + ["https://resource.openai.azure.com", "https://resource.openai.azure.com/openai"], + ["https://resource.openai.azure.com/", "https://resource.openai.azure.com/openai"], + ["https://resource.openai.azure.com/openai", "https://resource.openai.azure.com/openai"], + ["https://resource.openai.azure.com/openai/", "https://resource.openai.azure.com/openai"], + ])("normalizes Azure OpenAI base URL %s", (openAiBaseUrl, expectedBaseUrl) => { + new OpenAiHandler({ ...mockOptions, openAiBaseUrl }) + + expect(vi.mocked(AzureOpenAI)).toHaveBeenLastCalledWith( + expect.objectContaining({ baseURL: expectedBaseUrl }), + ) + }) + + it("normalizes reverse-proxy URLs when Azure mode is enabled", () => { + new OpenAiHandler({ + ...mockOptions, + openAiBaseUrl: "https://models.example.com/azure/", + openAiUseAzure: true, + }) + + expect(vi.mocked(AzureOpenAI)).toHaveBeenLastCalledWith( + expect.objectContaining({ baseURL: "https://models.example.com/azure/openai" }), + ) + }) }) describe("createMessage", () => { diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 9545068794..90f453c5d8 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -60,8 +60,9 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl } else if (isAzureOpenAi) { // Azure API shape slightly differs from the core API shape: // https://github.com/openai/openai-node?tab=readme-ov-file#microsoft-azure-openai + const azureBaseURL = `${baseURL.replace(/\/openai\/?$/, "").replace(/\/$/, "")}/openai` this.client = new AzureOpenAI({ - baseURL, + baseURL: azureBaseURL, apiKey, apiVersion: this.options.azureApiVersion || azureOpenAiDefaultApiVersion, defaultHeaders: headers, diff --git a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx index 8b11c128c7..1d636e3e76 100644 --- a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx +++ b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx @@ -42,6 +42,18 @@ export const OpenAICompatible = ({ simplifySettings, }: OpenAICompatibleProps) => { const { t } = useAppTranslation() + const isAzureOpenAi = (() => { + if (apiConfiguration?.openAiUseAzure) { + return true + } + + try { + const host = new URL(apiConfiguration?.openAiBaseUrl || "").host + return host === "azure.com" || host.endsWith(".azure.com") + } catch { + return false + } + })() const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion) @@ -129,7 +141,11 @@ export const OpenAICompatible = ({ value={apiConfiguration?.openAiBaseUrl || ""} type="url" onInput={handleInputChange("openAiBaseUrl")} - placeholder={t("settings:placeholders.baseUrl")} + placeholder={ + isAzureOpenAi + ? t("settings:providers.azureOpenAiBaseUrlPlaceholder") + : t("settings:placeholders.baseUrl") + } className="w-full"> @@ -147,12 +163,18 @@ export const OpenAICompatible = ({ defaultModelId="gpt-4o" models={openAiModels} modelIdKey="openAiModelId" + label={isAzureOpenAi ? t("settings:providers.azureOpenAiDeploymentName") : undefined} serviceName="OpenAI" serviceUrl="https://platform.openai.com" organizationAllowList={organizationAllowList} errorMessage={modelValidationError} simplifySettings={simplifySettings} /> + {isAzureOpenAi && ( +
+ {t("settings:providers.azureOpenAiDeploymentNameDescription")} +
+ )} ({ })) // Mock other components +const { mockModelPicker } = vi.hoisted(() => ({ mockModelPicker: vi.fn() })) + vi.mock("../../ModelPicker", () => ({ - ModelPicker: () =>
Model Picker
, + ModelPicker: (props: any) => { + mockModelPicker(props) + return
Model Picker
+ }, })) vi.mock("../../R1FormatSetting", () => ({ @@ -144,6 +149,43 @@ describe("OpenAICompatible Component - includeMaxTokens checkbox", () => { }) }) + describe("Azure OpenAI guidance", () => { + it.each([ + { openAiBaseUrl: "https://resource.openai.azure.com/" }, + { openAiBaseUrl: "https://models.example.com", openAiUseAzure: true }, + ])("shows Azure-specific endpoint and deployment guidance", (apiConfiguration) => { + render( + , + ) + + expect(screen.getByPlaceholderText("settings:providers.azureOpenAiBaseUrlPlaceholder")).toBeInTheDocument() + expect(mockModelPicker).toHaveBeenLastCalledWith( + expect.objectContaining({ label: "settings:providers.azureOpenAiDeploymentName" }), + ) + expect(screen.getByText("settings:providers.azureOpenAiDeploymentNameDescription")).toBeInTheDocument() + }) + + it("keeps generic OpenAI-compatible guidance for non-Azure endpoints", () => { + render( + , + ) + + expect(screen.getByPlaceholderText("settings:placeholders.baseUrl")).toBeInTheDocument() + expect(mockModelPicker).toHaveBeenLastCalledWith(expect.objectContaining({ label: undefined })) + expect( + screen.queryByText("settings:providers.azureOpenAiDeploymentNameDescription"), + ).not.toBeInTheDocument() + }) + }) + describe("Initial State", () => { it("should show checkbox as checked when includeMaxTokens is true", () => { const apiConfiguration: Partial = { diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 2aacc322f0..14a7476a75 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -575,6 +575,9 @@ "openAiApiKey": "OpenAI API Key", "apiKey": "API Key", "openAiBaseUrl": "Base URL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Azure deployment name", + "azureOpenAiDeploymentNameDescription": "Enter the deployment name from Azure AI Studio, not the underlying model name.", "getOpenAiApiKey": "Get OpenAI API Key", "mistralApiKey": "Mistral API Key", "getMistralApiKey": "Get Mistral / Codestral API Key", From 646693d06025da0eddd6af0e0695ece5ac97e592 Mon Sep 17 00:00:00 2001 From: Roomote Date: Sat, 8 Aug 2026 01:05:50 +0000 Subject: [PATCH 02/10] test(provider): keep Azure constructor mock focused --- src/api/providers/__tests__/openai.spec.ts | 99 +++++++++++----------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index 5fd4a57631..4844ffedc3 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -21,62 +21,61 @@ const mockCreate = vitest.fn() vitest.mock("openai", () => { const mockConstructor = vitest.fn() const mockAzureConstructor = vitest.fn() - function createClient() { - return { - chat: { - completions: { - create: mockCreate.mockImplementation(async (options) => { - if (!options.stream) { - return { - id: "test-completion", - choices: [ - { - message: { role: "assistant", content: "Test response", refusal: null }, - finish_reason: "stop", - index: 0, + return { + __esModule: true, + default: mockConstructor.mockImplementation(function () { + return { + chat: { + completions: { + create: mockCreate.mockImplementation(async (options) => { + if (!options.stream) { + return { + id: "test-completion", + choices: [ + { + message: { role: "assistant", content: "Test response", refusal: null }, + finish_reason: "stop", + index: 0, + }, + ], + usage: { + prompt_tokens: 10, + completion_tokens: 5, + total_tokens: 15, }, - ], - usage: { - prompt_tokens: 10, - completion_tokens: 5, - total_tokens: 15, - }, + } } - } - return asyncStreamFrom([ - { - choices: [ - { - delta: { content: "Test response" }, - index: 0, - }, - ], - usage: null, - }, - { - choices: [ - { - delta: {}, - index: 0, + return asyncStreamFrom([ + { + choices: [ + { + delta: { content: "Test response" }, + index: 0, + }, + ], + usage: null, + }, + { + choices: [ + { + delta: {}, + index: 0, + }, + ], + usage: { + prompt_tokens: 10, + completion_tokens: 5, + total_tokens: 15, }, - ], - usage: { - prompt_tokens: 10, - completion_tokens: 5, - total_tokens: 15, }, - }, - ]) - }), + ]) + }), + }, }, - }, - } - } - return { - __esModule: true, - default: mockConstructor.mockImplementation(createClient), - AzureOpenAI: mockAzureConstructor.mockImplementation(createClient), + } + }), + AzureOpenAI: mockAzureConstructor, } }) From 3a121b8bbb06ff5443ff4e04cd1a6144dc977407 Mon Sep 17 00:00:00 2001 From: Roomote Date: Sat, 8 Aug 2026 01:26:29 +0000 Subject: [PATCH 03/10] test(settings): snapshot Azure OpenAI guidance --- .../OpenAICompatible.visual.fixture.tsx | 68 +++++++++++++++++++ .../__tests__/OpenAICompatible.visual.tsx | 16 +++++ 2 files changed, 84 insertions(+) create mode 100644 webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx create mode 100644 webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx new file mode 100644 index 0000000000..f8af0ed3fd --- /dev/null +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx @@ -0,0 +1,68 @@ +/* v8 ignore file -- Playwright component fixture is covered by the visual test. */ +import React from "react" +import { QueryClient, QueryClientProvider } from "@tanstack/react-query" + +import { type ProviderSettings } from "@roo-code/types" + +import { TranslationContext as AppTranslationContext } from "@/i18n/TranslationContext" +import { TranslationContext as PlaywrightTranslationContext } from "@src/i18n/TranslationContext" +import { TooltipProvider } from "@src/components/ui/tooltip" +import { OpenAICompatible } from "../OpenAICompatible" + +const translations: Record = { + "settings:providers.openAiBaseUrl": "Base URL", + "settings:providers.azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "settings:providers.apiKey": "API Key", + "settings:placeholders.apiKey": "Enter API key", + "settings:providers.azureOpenAiDeploymentName": "Azure deployment name", + "settings:providers.azureOpenAiDeploymentNameDescription": + "Enter the deployment name from Azure AI Studio, not the underlying model name.", + "settings:modelPicker.simplifiedExplanation": "You can adjust detailed model settings later.", + "settings:modelInfo.enableR1Format": "Enable R1 model parameters", + "settings:modelInfo.enableR1FormatTips": "Enable this only for R1-compatible models.", + "settings:modelInfo.enableStreaming": "Enable streaming", + "settings:includeMaxOutputTokens": "Include max output tokens", + "settings:includeMaxOutputTokensDescription": "Send the configured maximum output token limit.", + "settings:modelInfo.useAzure": "Use Azure", + "settings:modelInfo.azureApiVersion": "Set Azure API version", +} + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false }, + }, +}) + +const apiConfiguration: ProviderSettings = { + apiProvider: "openai", + openAiBaseUrl: "", + openAiModelId: "my-gpt4o-deployment", + openAiUseAzure: true, +} + +export const OpenAICompatibleAzureFixture = () => ( + translations[key] ?? key, + i18n: null as unknown as typeof import("../../../../i18n/setup").default, + }}> + translations[key] ?? key, + i18n: null as unknown as typeof import("../../../../i18n/setup").default, + }}> + + +
+ {}} + organizationAllowList={{ allowAll: true, providers: {} }} + simplifySettings + /> +
+
+
+
+
+) diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx new file mode 100644 index 0000000000..19ffb898de --- /dev/null +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx @@ -0,0 +1,16 @@ +import React from "react" + +import { expect, test } from "../../../../../playwright/coverage-fixture" +import { OpenAICompatibleAzureFixture } from "./OpenAICompatible.visual.fixture" + +test("renders Azure OpenAI endpoint and deployment guidance in the VS Code dark theme", async ({ mount, page }) => { + await page.evaluate(() => Object.assign(globalThis, { z: undefined })) + const component = await mount() + + await component.evaluate(async () => { + await document.fonts.ready + await new Promise((resolve) => requestAnimationFrame(() => resolve())) + }) + + await expect(component).toHaveScreenshot("openai-compatible-azure-guidance-dark.png") +}) From dfd281e66b7956766605633680b89600df5c798f Mon Sep 17 00:00:00 2001 From: Roomote Date: Sat, 8 Aug 2026 01:29:10 +0000 Subject: [PATCH 04/10] test(settings): focus Azure guidance snapshot --- .../providers/__tests__/OpenAICompatible.visual.fixture.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx index f8af0ed3fd..ed81023091 100644 --- a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx @@ -53,7 +53,7 @@ export const OpenAICompatibleAzureFixture = () => ( }}> -
+
{}} From 6cf5a957af5f378508927f54391ddba0959b4e3c Mon Sep 17 00:00:00 2001 From: Roomote Date: Sat, 8 Aug 2026 01:31:33 +0000 Subject: [PATCH 05/10] test(settings): trim Azure guidance snapshot --- .../providers/__tests__/OpenAICompatible.visual.fixture.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx index ed81023091..1c2d1a5389 100644 --- a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx @@ -53,7 +53,7 @@ export const OpenAICompatibleAzureFixture = () => ( }}> -
+
{}} From d3688f55384178977f70585df1c4b919e4ef4bd0 Mon Sep 17 00:00:00 2001 From: Roomote Date: Sat, 8 Aug 2026 01:34:10 +0000 Subject: [PATCH 06/10] test(settings): add Azure guidance visual baseline --- .../openai-compatible-azure-guidance-dark.png | Bin 0 -> 30063 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 webview-ui/src/components/settings/providers/__tests__/__screenshots__/openai-compatible-azure-guidance-dark.png diff --git a/webview-ui/src/components/settings/providers/__tests__/__screenshots__/openai-compatible-azure-guidance-dark.png b/webview-ui/src/components/settings/providers/__tests__/__screenshots__/openai-compatible-azure-guidance-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..5838212a1dfe62029c8687b62af772a341124181 GIT binary patch literal 30063 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 Date: Sat, 8 Aug 2026 01:39:19 +0000 Subject: [PATCH 07/10] test(settings): explain CT bundle setup --- .../settings/providers/__tests__/OpenAICompatible.visual.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx index 19ffb898de..0a522ca0b3 100644 --- a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx @@ -4,6 +4,7 @@ import { expect, test } from "../../../../../playwright/coverage-fixture" import { OpenAICompatibleAzureFixture } from "./OpenAICompatible.visual.fixture" test("renders Azure OpenAI endpoint and deployment guidance in the VS Code dark theme", async ({ mount, page }) => { + // The full provider bundle leaves a bare Zod reference after CT tree-shaking. await page.evaluate(() => Object.assign(globalThis, { z: undefined })) const component = await mount() From fd41164ea9fcbbb3f207d2d8cf48c918312ec05c Mon Sep 17 00:00:00 2001 From: Roomote Date: Sat, 8 Aug 2026 17:50:22 +0000 Subject: [PATCH 08/10] fix(settings): translate Azure OpenAI guidance --- webview-ui/src/i18n/locales/ca/settings.json | 3 +++ webview-ui/src/i18n/locales/de/settings.json | 3 +++ webview-ui/src/i18n/locales/es/settings.json | 3 +++ webview-ui/src/i18n/locales/fr/settings.json | 3 +++ webview-ui/src/i18n/locales/hi/settings.json | 3 +++ webview-ui/src/i18n/locales/id/settings.json | 3 +++ webview-ui/src/i18n/locales/it/settings.json | 3 +++ webview-ui/src/i18n/locales/ja/settings.json | 3 +++ webview-ui/src/i18n/locales/ko/settings.json | 3 +++ webview-ui/src/i18n/locales/nl/settings.json | 3 +++ webview-ui/src/i18n/locales/pl/settings.json | 3 +++ webview-ui/src/i18n/locales/pt-BR/settings.json | 3 +++ webview-ui/src/i18n/locales/ru/settings.json | 3 +++ webview-ui/src/i18n/locales/tr/settings.json | 3 +++ webview-ui/src/i18n/locales/vi/settings.json | 3 +++ webview-ui/src/i18n/locales/zh-CN/settings.json | 3 +++ webview-ui/src/i18n/locales/zh-TW/settings.json | 3 +++ 17 files changed, 51 insertions(+) diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 49331e3707..73827b1ec1 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "Clau API d'OpenAI", "apiKey": "Clau API", "openAiBaseUrl": "URL base", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Nom de desplegament d'Azure", + "azureOpenAiDeploymentNameDescription": "Introduïu el nom del desplegament d'Azure AI Studio, no el nom del model subjacent.", "getOpenAiApiKey": "Obtenir clau API d'OpenAI", "mistralApiKey": "Clau API de Mistral", "getMistralApiKey": "Obtenir clau API de Mistral / Codestral", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 5a8c05551f..8078037525 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "OpenAI API-Schlüssel", "apiKey": "API-Schlüssel", "openAiBaseUrl": "Basis-URL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Azure-Bereitstellungsname", + "azureOpenAiDeploymentNameDescription": "Gib den Bereitstellungsnamen aus Azure AI Studio ein, nicht den Namen des zugrunde liegenden Modells.", "getOpenAiApiKey": "OpenAI API-Schlüssel erhalten", "mistralApiKey": "Mistral API-Schlüssel", "getMistralApiKey": "Mistral / Codestral API-Schlüssel erhalten", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 3f99fc1b14..e629e43b50 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "Clave API de OpenAI", "apiKey": "Clave API", "openAiBaseUrl": "URL base", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Nombre de implementación de Azure", + "azureOpenAiDeploymentNameDescription": "Introduce el nombre de la implementación de Azure AI Studio, no el nombre del modelo subyacente.", "getOpenAiApiKey": "Obtener clave API de OpenAI", "mistralApiKey": "Clave API de Mistral", "getMistralApiKey": "Obtener clave API de Mistral / Codestral", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index ac0e6afb22..6048e2274c 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "Clé API OpenAI", "apiKey": "Clé API", "openAiBaseUrl": "URL de base", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Nom du déploiement Azure", + "azureOpenAiDeploymentNameDescription": "Saisissez le nom du déploiement défini dans Azure AI Studio, et non le nom du modèle sous-jacent.", "getOpenAiApiKey": "Obtenir la clé API OpenAI", "mistralApiKey": "Clé API Mistral", "getMistralApiKey": "Obtenir la clé API Mistral / Codestral", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index b720a5db83..28d0b8699b 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "OpenAI API कुंजी", "apiKey": "API कुंजी", "openAiBaseUrl": "बेस URL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Azure परिनियोजन नाम", + "azureOpenAiDeploymentNameDescription": "अंतर्निहित मॉडल नाम के बजाय Azure AI Studio में दिया गया परिनियोजन नाम दर्ज करें।", "getOpenAiApiKey": "OpenAI API कुंजी प्राप्त करें", "mistralApiKey": "Mistral API कुंजी", "getMistralApiKey": "Mistral / Codestral API कुंजी प्राप्त करें", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index c46cc5acf1..bf049395c4 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "OpenAI API Key", "apiKey": "API Key", "openAiBaseUrl": "Base URL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Nama deployment Azure", + "azureOpenAiDeploymentNameDescription": "Masukkan nama deployment dari Azure AI Studio, bukan nama model dasarnya.", "getOpenAiApiKey": "Dapatkan OpenAI API Key", "mistralApiKey": "Mistral API Key", "getMistralApiKey": "Dapatkan Mistral / Codestral API Key", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index ff00dacca7..577a74a77a 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "Chiave API OpenAI", "apiKey": "Chiave API", "openAiBaseUrl": "URL base", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Nome della distribuzione Azure", + "azureOpenAiDeploymentNameDescription": "Inserisci il nome della distribuzione configurato in Azure AI Studio, non il nome del modello sottostante.", "getOpenAiApiKey": "Ottieni chiave API OpenAI", "mistralApiKey": "Chiave API Mistral", "getMistralApiKey": "Ottieni chiave API Mistral / Codestral", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index cdcb377cc9..1113ac32a6 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "OpenAI APIキー", "apiKey": "APIキー", "openAiBaseUrl": "ベースURL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Azure デプロイ名", + "azureOpenAiDeploymentNameDescription": "基盤モデル名ではなく、Azure AI Studio のデプロイ名を入力してください。", "getOpenAiApiKey": "OpenAI APIキーを取得", "mistralApiKey": "Mistral APIキー", "getMistralApiKey": "Mistral / Codestral APIキーを取得", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 4a7845ac2a..27e8493bd4 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -495,6 +495,9 @@ "apiKey": "API 키", "openAiApiKey": "OpenAI API 키", "openAiBaseUrl": "기본 URL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Azure 배포 이름", + "azureOpenAiDeploymentNameDescription": "기반 모델 이름이 아닌 Azure AI Studio의 배포 이름을 입력하세요.", "getOpenAiApiKey": "OpenAI API 키 받기", "mistralApiKey": "Mistral API 키", "getMistralApiKey": "Mistral / Codestral API 키 받기", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 768018c3ef..394cdd48f2 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -495,6 +495,9 @@ "apiKey": "API-sleutel", "openAiApiKey": "OpenAI API-sleutel", "openAiBaseUrl": "Basis-URL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Naam van Azure-implementatie", + "azureOpenAiDeploymentNameDescription": "Voer de implementatienaam uit Azure AI Studio in, niet de naam van het onderliggende model.", "getOpenAiApiKey": "OpenAI API-sleutel ophalen", "mistralApiKey": "Mistral API-sleutel", "getMistralApiKey": "Mistral / Codestral API-sleutel ophalen", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 37b37df875..864e4ffde1 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -495,6 +495,9 @@ "apiKey": "Klucz API", "openAiApiKey": "Klucz API OpenAI", "openAiBaseUrl": "URL bazowy", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Nazwa wdrożenia Azure", + "azureOpenAiDeploymentNameDescription": "Wprowadź nazwę wdrożenia z Azure AI Studio, a nie nazwę bazowego modelu.", "getOpenAiApiKey": "Uzyskaj klucz API OpenAI", "mistralApiKey": "Klucz API Mistral", "getMistralApiKey": "Uzyskaj klucz API Mistral / Codestral", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index c3b91d6b58..a1948a0218 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -495,6 +495,9 @@ "apiKey": "Chave de API", "openAiApiKey": "Chave de API OpenAI", "openAiBaseUrl": "URL Base", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Nome da implantação do Azure", + "azureOpenAiDeploymentNameDescription": "Insira o nome da implantação configurado no Azure AI Studio, não o nome do modelo subjacente.", "getOpenAiApiKey": "Obter chave de API OpenAI", "mistralApiKey": "Chave de API Mistral", "getMistralApiKey": "Obter chave de API Mistral / Codestral", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index c428b31ec1..f36fe62539 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -495,6 +495,9 @@ "apiKey": "API-ключ", "openAiApiKey": "OpenAI API-ключ", "openAiBaseUrl": "Базовый URL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Имя развёртывания Azure", + "azureOpenAiDeploymentNameDescription": "Введите имя развёртывания из Azure AI Studio, а не имя базовой модели.", "getOpenAiApiKey": "Получить OpenAI API-ключ", "mistralApiKey": "Mistral API-ключ", "getMistralApiKey": "Получить Mistral / Codestral API-ключ", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index bccb1c08aa..9099677679 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "OpenAI API Anahtarı", "apiKey": "API Anahtarı", "openAiBaseUrl": "Temel URL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Azure dağıtım adı", + "azureOpenAiDeploymentNameDescription": "Temel model adı yerine Azure AI Studio'daki dağıtım adını girin.", "getOpenAiApiKey": "OpenAI API Anahtarı Al", "mistralApiKey": "Mistral API Anahtarı", "getMistralApiKey": "Mistral / Codestral API Anahtarı Al", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 6b20b9a9a9..c66b236165 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "Khóa API OpenAI", "apiKey": "Khóa API", "openAiBaseUrl": "URL cơ sở", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Tên triển khai Azure", + "azureOpenAiDeploymentNameDescription": "Nhập tên triển khai trong Azure AI Studio, không phải tên mô hình cơ sở.", "getOpenAiApiKey": "Lấy khóa API OpenAI", "mistralApiKey": "Khóa API Mistral", "getMistralApiKey": "Lấy khóa API Mistral / Codestral", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index c206c26108..22742e0e0e 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -495,6 +495,9 @@ "openAiApiKey": "OpenAI API 密钥", "apiKey": "API 密钥", "openAiBaseUrl": "OpenAI 基础 URL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Azure 部署名称", + "azureOpenAiDeploymentNameDescription": "请输入 Azure AI Studio 中的部署名称,而不是底层模型名称。", "getOpenAiApiKey": "获取 OpenAI API 密钥", "mistralApiKey": "Mistral API 密钥", "getMistralApiKey": "获取 Mistral / Codestral API 密钥", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 64eb5e0b29..4255a2e697 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -522,6 +522,9 @@ "openAiApiKey": "OpenAI API 金鑰", "apiKey": "API 金鑰", "openAiBaseUrl": "基礎 URL", + "azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", + "azureOpenAiDeploymentName": "Azure 部署名稱", + "azureOpenAiDeploymentNameDescription": "請輸入 Azure AI Studio 中的部署名稱,而不是底層模型名稱。", "getOpenAiApiKey": "取得 OpenAI API 金鑰", "mistralApiKey": "Mistral API 金鑰", "getMistralApiKey": "取得 Mistral/Codestral API 金鑰", From 1afa5f8b16f6aef53866145b9434e22109fbf7f4 Mon Sep 17 00:00:00 2001 From: Elliott de Launay Date: Sun, 9 Aug 2026 03:29:00 +0000 Subject: [PATCH 09/10] fix(providers): correct and deduplicate Azure OpenAI detection --- packages/types/src/providers/openai.ts | 16 ++++++++++ src/api/providers/__tests__/openai.spec.ts | 22 +++++++++++-- src/api/providers/openai.ts | 6 ++-- .../settings/providers/OpenAICompatible.tsx | 14 ++------ .../__tests__/OpenAICompatible.spec.tsx | 17 ++++++++++ .../OpenAICompatible.visual.fixture.tsx | 30 ++++++++---------- .../__tests__/OpenAICompatible.visual.tsx | 17 ++++++++++ .../openai-compatible-azure-guidance-dark.png | Bin 30063 -> 32386 bytes 8 files changed, 88 insertions(+), 34 deletions(-) diff --git a/packages/types/src/providers/openai.ts b/packages/types/src/providers/openai.ts index 5f114e41f6..16f16c8849 100644 --- a/packages/types/src/providers/openai.ts +++ b/packages/types/src/providers/openai.ts @@ -705,3 +705,19 @@ export const azureOpenAiDefaultApiVersion = "2024-08-01-preview" export const OPENAI_NATIVE_DEFAULT_TEMPERATURE = 0 export const OPENAI_AZURE_AI_INFERENCE_PATH = "/models/chat/completions" + +/** + * Returns true when the base URL and/or flag indicate an Azure OpenAI endpoint. + * Azure AI Inference endpoints (*.services.ai.azure.com) return false — the + * backend routes those through the plain OpenAI client, not AzureOpenAI. + */ +export function isAzureOpenAiBaseUrl(baseUrl?: string, useAzure?: boolean): boolean { + if (useAzure) return true + + try { + const host = new URL(baseUrl ?? "").host + return (host === "azure.com" || host.endsWith(".azure.com")) && !host.endsWith(".services.ai.azure.com") + } catch { + return false + } +} diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index 4844ffedc3..a14043c2c2 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -4,7 +4,11 @@ import { OpenAiHandler, getOpenAiModels } from "../openai" import { ApiHandlerOptions } from "../../../shared/api" import { Anthropic } from "@anthropic-ai/sdk" import OpenAI, { AzureOpenAI } from "openai" -import { openAiModelInfoSaneDefaults, DEEP_SEEK_DEFAULT_TEMPERATURE } from "@roo-code/types" +import { + openAiModelInfoSaneDefaults, + DEEP_SEEK_DEFAULT_TEMPERATURE, + azureOpenAiDefaultApiVersion, +} from "@roo-code/types" import { Package } from "../../../shared/package" import { makeApiHandlerOptions } from "../../../test-utils/api" import { asyncStreamFrom, collectStream } from "../../../test-utils/stream" @@ -138,7 +142,13 @@ describe("OpenAiHandler", () => { new OpenAiHandler({ ...mockOptions, openAiBaseUrl }) expect(vi.mocked(AzureOpenAI)).toHaveBeenLastCalledWith( - expect.objectContaining({ baseURL: expectedBaseUrl }), + expect.objectContaining({ + baseURL: expectedBaseUrl, + apiKey: mockOptions.openAiApiKey, + apiVersion: azureOpenAiDefaultApiVersion, + defaultHeaders: expect.any(Object), + timeout: MOCK_TIMEOUT_MS, + }), ) }) @@ -150,7 +160,13 @@ describe("OpenAiHandler", () => { }) expect(vi.mocked(AzureOpenAI)).toHaveBeenLastCalledWith( - expect.objectContaining({ baseURL: "https://models.example.com/azure/openai" }), + expect.objectContaining({ + baseURL: "https://models.example.com/azure/openai", + apiKey: mockOptions.openAiApiKey, + apiVersion: azureOpenAiDefaultApiVersion, + defaultHeaders: expect.any(Object), + timeout: MOCK_TIMEOUT_MS, + }), ) }) }) diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 90f453c5d8..6f98d2b450 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -5,6 +5,7 @@ import axios from "axios" import { type ModelInfo, azureOpenAiDefaultApiVersion, + isAzureOpenAiBaseUrl, openAiModelInfoSaneDefaults, DEEP_SEEK_DEFAULT_TEMPERATURE, OPENAI_AZURE_AI_INFERENCE_PATH, @@ -40,8 +41,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl const baseURL = this.options.openAiBaseUrl || "https://api.openai.com/v1" const apiKey = this.options.openAiApiKey ?? "not-provided" const isAzureAiInference = this._isAzureAiInference(this.options.openAiBaseUrl) - const urlHost = this._getUrlHost(this.options.openAiBaseUrl) - const isAzureOpenAi = urlHost === "azure.com" || urlHost.endsWith(".azure.com") || options.openAiUseAzure + const isAzureOpenAi = isAzureOpenAiBaseUrl(this.options.openAiBaseUrl, options.openAiUseAzure) const headers = { ...DEFAULT_HEADERS, @@ -60,7 +60,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl } else if (isAzureOpenAi) { // Azure API shape slightly differs from the core API shape: // https://github.com/openai/openai-node?tab=readme-ov-file#microsoft-azure-openai - const azureBaseURL = `${baseURL.replace(/\/openai\/?$/, "").replace(/\/$/, "")}/openai` + const azureBaseURL = `${baseURL.replace(/\/openai\/?$/i, "").replace(/\/$/, "")}/openai` this.client = new AzureOpenAI({ baseURL: azureBaseURL, apiKey, diff --git a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx index 1d636e3e76..f9a021812b 100644 --- a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx +++ b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx @@ -10,6 +10,7 @@ import { type OrganizationAllowList, type ExtensionMessage, azureOpenAiDefaultApiVersion, + isAzureOpenAiBaseUrl, openAiModelInfoSaneDefaults, } from "@roo-code/types" @@ -42,18 +43,7 @@ export const OpenAICompatible = ({ simplifySettings, }: OpenAICompatibleProps) => { const { t } = useAppTranslation() - const isAzureOpenAi = (() => { - if (apiConfiguration?.openAiUseAzure) { - return true - } - - try { - const host = new URL(apiConfiguration?.openAiBaseUrl || "").host - return host === "azure.com" || host.endsWith(".azure.com") - } catch { - return false - } - })() + const isAzureOpenAi = isAzureOpenAiBaseUrl(apiConfiguration?.openAiBaseUrl, apiConfiguration?.openAiUseAzure) const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion) diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx index 9b1f61883a..db820707a4 100644 --- a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx @@ -184,6 +184,23 @@ describe("OpenAICompatible Component - includeMaxTokens checkbox", () => { screen.queryByText("settings:providers.azureOpenAiDeploymentNameDescription"), ).not.toBeInTheDocument() }) + + it("keeps generic OpenAI-compatible guidance for Azure AI Inference endpoints", () => { + render( + , + ) + + expect(mockModelPicker).toHaveBeenLastCalledWith(expect.objectContaining({ label: undefined })) + expect( + screen.queryByText("settings:providers.azureOpenAiDeploymentNameDescription"), + ).not.toBeInTheDocument() + }) }) describe("Initial State", () => { diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx index 1c2d1a5389..a84a3a056f 100644 --- a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.fixture.tsx @@ -8,25 +8,23 @@ import { TranslationContext as AppTranslationContext } from "@/i18n/TranslationC import { TranslationContext as PlaywrightTranslationContext } from "@src/i18n/TranslationContext" import { TooltipProvider } from "@src/components/ui/tooltip" import { OpenAICompatible } from "../OpenAICompatible" +import enSettings from "@/i18n/locales/en/settings.json" -const translations: Record = { - "settings:providers.openAiBaseUrl": "Base URL", - "settings:providers.azureOpenAiBaseUrlPlaceholder": "https://.openai.azure.com/openai", - "settings:providers.apiKey": "API Key", - "settings:placeholders.apiKey": "Enter API key", - "settings:providers.azureOpenAiDeploymentName": "Azure deployment name", - "settings:providers.azureOpenAiDeploymentNameDescription": - "Enter the deployment name from Azure AI Studio, not the underlying model name.", - "settings:modelPicker.simplifiedExplanation": "You can adjust detailed model settings later.", - "settings:modelInfo.enableR1Format": "Enable R1 model parameters", - "settings:modelInfo.enableR1FormatTips": "Enable this only for R1-compatible models.", - "settings:modelInfo.enableStreaming": "Enable streaming", - "settings:includeMaxOutputTokens": "Include max output tokens", - "settings:includeMaxOutputTokensDescription": "Send the configured maximum output token limit.", - "settings:modelInfo.useAzure": "Use Azure", - "settings:modelInfo.azureApiVersion": "Set Azure API version", +function flattenTranslations(obj: Record, prefix = "settings:"): Record { + const result: Record = {} + for (const [key, value] of Object.entries(obj)) { + const fullKey = `${prefix}${key}` + if (typeof value === "string") { + result[fullKey] = value + } else if (value !== null && typeof value === "object" && !Array.isArray(value)) { + Object.assign(result, flattenTranslations(value as Record, `${fullKey}.`)) + } + } + return result } +const translations = flattenTranslations(enSettings as Record) + const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx index 0a522ca0b3..17a98118bd 100644 --- a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.visual.tsx @@ -8,6 +8,23 @@ test("renders Azure OpenAI endpoint and deployment guidance in the VS Code dark await page.evaluate(() => Object.assign(globalThis, { z: undefined })) const component = await mount() + await component.evaluate((element) => { + const { document } = element.ownerDocument.defaultView! + document.documentElement.className = "vscode-dark" + document.body.className = "vscode-dark" + document.body.dataset.vscodeThemeId = "Default Dark Modern" + }) + + await expect + .poll(() => + component.evaluate((element) => { + return getComputedStyle(element.ownerDocument.body) + .getPropertyValue("--vscode-editor-background") + .trim() + }), + ) + .toBe("#1e1e1e") + await component.evaluate(async () => { await document.fonts.ready await new Promise((resolve) => requestAnimationFrame(() => resolve())) diff --git a/webview-ui/src/components/settings/providers/__tests__/__screenshots__/openai-compatible-azure-guidance-dark.png b/webview-ui/src/components/settings/providers/__tests__/__screenshots__/openai-compatible-azure-guidance-dark.png index 5838212a1dfe62029c8687b62af772a341124181..11e4289cc7ee2e085a79644617f7b4a60d4332ec 100644 GIT binary patch literal 32386 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 literal 30063 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 Date: Sun, 9 Aug 2026 18:27:47 +0000 Subject: [PATCH 10/10] fix: align Azure AI Inference routing guidance --- packages/types/src/providers/openai.ts | 17 ++++++++++++++++- src/api/providers/__tests__/openai.spec.ts | 10 ++++++++++ src/api/providers/openai.ts | 4 ++-- .../__tests__/OpenAICompatible.spec.tsx | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/packages/types/src/providers/openai.ts b/packages/types/src/providers/openai.ts index 16f16c8849..b090509bc2 100644 --- a/packages/types/src/providers/openai.ts +++ b/packages/types/src/providers/openai.ts @@ -706,17 +706,32 @@ export const OPENAI_NATIVE_DEFAULT_TEMPERATURE = 0 export const OPENAI_AZURE_AI_INFERENCE_PATH = "/models/chat/completions" +/** + * Returns true when the base URL belongs to Azure AI Inference. + * These endpoints use the regular OpenAI client and expect a model identifier, + * even when the Azure compatibility flag is enabled. + */ +export function isAzureAiInferenceBaseUrl(baseUrl?: string): boolean { + try { + const host = new URL(baseUrl ?? "").host + return host.endsWith(".services.ai.azure.com") + } catch { + return false + } +} + /** * Returns true when the base URL and/or flag indicate an Azure OpenAI endpoint. * Azure AI Inference endpoints (*.services.ai.azure.com) return false — the * backend routes those through the plain OpenAI client, not AzureOpenAI. */ export function isAzureOpenAiBaseUrl(baseUrl?: string, useAzure?: boolean): boolean { + if (isAzureAiInferenceBaseUrl(baseUrl)) return false if (useAzure) return true try { const host = new URL(baseUrl ?? "").host - return (host === "azure.com" || host.endsWith(".azure.com")) && !host.endsWith(".services.ai.azure.com") + return host === "azure.com" || host.endsWith(".azure.com") } catch { return false } diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index a14043c2c2..38550533a5 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -897,6 +897,16 @@ describe("OpenAiHandler", () => { expect(azureHandler.getModel().id).toBe(azureOptions.openAiModelId) }) + it("should keep Azure AI Inference precedence when Azure mode is enabled", () => { + vi.mocked(OpenAI).mockClear() + vi.mocked(AzureOpenAI).mockClear() + + new OpenAiHandler({ ...azureOptions, openAiUseAzure: true }) + + expect(vi.mocked(OpenAI)).toHaveBeenCalled() + expect(vi.mocked(AzureOpenAI)).not.toHaveBeenCalled() + }) + it("should handle streaming responses with Azure AI Inference Service", async () => { const azureHandler = new OpenAiHandler(azureOptions) const systemPrompt = "You are a helpful assistant." diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 6f98d2b450..5b4476fdef 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -5,6 +5,7 @@ import axios from "axios" import { type ModelInfo, azureOpenAiDefaultApiVersion, + isAzureAiInferenceBaseUrl, isAzureOpenAiBaseUrl, openAiModelInfoSaneDefaults, DEEP_SEEK_DEFAULT_TEMPERATURE, @@ -521,8 +522,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl } protected _isAzureAiInference(baseUrl?: string): boolean { - const urlHost = this._getUrlHost(baseUrl) - return urlHost.endsWith(".services.ai.azure.com") + return isAzureAiInferenceBaseUrl(baseUrl) } /** diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx index db820707a4..196d067755 100644 --- a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx @@ -201,6 +201,24 @@ describe("OpenAICompatible Component - includeMaxTokens checkbox", () => { screen.queryByText("settings:providers.azureOpenAiDeploymentNameDescription"), ).not.toBeInTheDocument() }) + + it("keeps generic guidance when Azure AI Inference uses the Azure compatibility flag", () => { + render( + , + ) + + expect(screen.getByPlaceholderText("settings:placeholders.baseUrl")).toBeInTheDocument() + expect(mockModelPicker).toHaveBeenLastCalledWith(expect.objectContaining({ label: undefined })) + }) }) describe("Initial State", () => {