From 79e5301415c5752f924678ebf1aa0a5ad8017fc5 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Tue, 9 Dec 2025 10:57:54 +0000 Subject: [PATCH] feat: Add Azure AI Foundry support for Anthropic provider - Add anthropicUseAzureFoundry and anthropicAzureDeploymentName settings - Update AnthropicHandler to use Azure deployment names when configured - Add UI controls for Azure Foundry configuration - Add translations for all supported locales - Add comprehensive tests for Azure Foundry functionality Fixes #9940 --- packages/types/src/provider-settings.ts | 2 + src/api/providers/__tests__/anthropic.spec.ts | 66 +++++++++++++++++++ src/api/providers/anthropic.ts | 10 ++- .../settings/providers/Anthropic.tsx | 32 +++++++++ webview-ui/src/i18n/locales/ca/settings.json | 3 + webview-ui/src/i18n/locales/de/settings.json | 3 + webview-ui/src/i18n/locales/en/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 + .../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 + .../src/i18n/locales/zh-CN/settings.json | 3 + .../src/i18n/locales/zh-TW/settings.json | 3 + 22 files changed, 163 insertions(+), 1 deletion(-) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index d713a47d6b..7a795b9594 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -199,6 +199,8 @@ const anthropicSchema = apiModelIdProviderModelSchema.extend({ anthropicBaseUrl: z.string().optional(), anthropicUseAuthToken: z.boolean().optional(), anthropicBeta1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. + anthropicUseAzureFoundry: z.boolean().optional(), // Enable Azure AI Foundry mode + anthropicAzureDeploymentName: z.string().optional(), // Override model ID with Azure deployment name }) const claudeCodeSchema = apiModelIdProviderModelSchema.extend({ diff --git a/src/api/providers/__tests__/anthropic.spec.ts b/src/api/providers/__tests__/anthropic.spec.ts index 8b229212d4..3d18083897 100644 --- a/src/api/providers/__tests__/anthropic.spec.ts +++ b/src/api/providers/__tests__/anthropic.spec.ts @@ -237,6 +237,72 @@ describe("AnthropicHandler", () => { expect(model.info.supportsPromptCache).toBe(true) }) + describe("Azure Foundry support", () => { + it("should use Azure deployment name when Azure Foundry mode is enabled", () => { + const handler = new AnthropicHandler({ + apiKey: "test-api-key", + apiModelId: "claude-3-5-sonnet-20241022", + anthropicUseAzureFoundry: true, + anthropicAzureDeploymentName: "my-claude-deployment", + }) + const model = handler.getModel() + expect(model.id).toBe("my-claude-deployment") + // Should still use the original model info + expect(model.info.maxTokens).toBe(8192) + expect(model.info.contextWindow).toBe(200_000) + }) + + it("should use original model ID when Azure Foundry is enabled but no deployment name provided", () => { + const handler = new AnthropicHandler({ + apiKey: "test-api-key", + apiModelId: "claude-3-5-sonnet-20241022", + anthropicUseAzureFoundry: true, + }) + const model = handler.getModel() + expect(model.id).toBe("claude-3-5-sonnet-20241022") + }) + + it("should use original model ID when Azure Foundry is disabled even with deployment name", () => { + const handler = new AnthropicHandler({ + apiKey: "test-api-key", + apiModelId: "claude-3-5-sonnet-20241022", + anthropicUseAzureFoundry: false, + anthropicAzureDeploymentName: "my-claude-deployment", + }) + const model = handler.getModel() + expect(model.id).toBe("claude-3-5-sonnet-20241022") + }) + + it("should handle Azure deployment name with thinking model", () => { + const handler = new AnthropicHandler({ + apiKey: "test-api-key", + apiModelId: "claude-3-7-sonnet-20250219:thinking", + anthropicUseAzureFoundry: true, + anthropicAzureDeploymentName: "azure-thinking-model", + }) + const model = handler.getModel() + expect(model.id).toBe("azure-thinking-model") + // Should still have the thinking model betas + expect(model.betas).toEqual(["output-128k-2025-02-19"]) + }) + + it("should work with Azure Foundry and 1M context beta together", () => { + const handler = new AnthropicHandler({ + apiKey: "test-api-key", + apiModelId: "claude-sonnet-4-5", + anthropicUseAzureFoundry: true, + anthropicAzureDeploymentName: "azure-sonnet-4-5", + anthropicBeta1MContext: true, + }) + const model = handler.getModel() + expect(model.id).toBe("azure-sonnet-4-5") + // Should still apply 1M context settings + expect(model.info.contextWindow).toBe(1000000) + expect(model.info.inputPrice).toBe(6.0) + expect(model.info.outputPrice).toBe(22.5) + }) + }) + it("honors custom maxTokens for thinking models", () => { const handler = new AnthropicHandler({ apiKey: "test-api-key", diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 2bf48d2562..fca2002e4c 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -336,8 +336,16 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa // reasoning model and that reasoning is required to be enabled. // The actual model ID honored by Anthropic's API does not have this // suffix. + const baseId = id === "claude-3-7-sonnet-20250219:thinking" ? "claude-3-7-sonnet-20250219" : id + + // If Azure Foundry mode is enabled and a deployment name is provided, use it + const finalId = + this.options.anthropicUseAzureFoundry && this.options.anthropicAzureDeploymentName + ? this.options.anthropicAzureDeploymentName + : baseId + return { - id: id === "claude-3-7-sonnet-20250219:thinking" ? "claude-3-7-sonnet-20250219" : id, + id: finalId, info, betas: id === "claude-3-7-sonnet-20250219:thinking" ? ["output-128k-2025-02-19"] : undefined, ...params, diff --git a/webview-ui/src/components/settings/providers/Anthropic.tsx b/webview-ui/src/components/settings/providers/Anthropic.tsx index 46a239a3fd..0539ed1a5b 100644 --- a/webview-ui/src/components/settings/providers/Anthropic.tsx +++ b/webview-ui/src/components/settings/providers/Anthropic.tsx @@ -21,6 +21,7 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro const selectedModel = useSelectedModel(apiConfiguration) const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl) + const [azureFoundryEnabled, setAzureFoundryEnabled] = useState(!!apiConfiguration?.anthropicUseAzureFoundry) // Check if the current model supports 1M context beta const supports1MContextBeta = @@ -86,6 +87,37 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro )} + {anthropicBaseUrlSelected && ( +
+ { + setAzureFoundryEnabled(checked) + setApiConfigurationField("anthropicUseAzureFoundry", checked) + if (!checked) { + setApiConfigurationField("anthropicAzureDeploymentName", "") + } + }}> + {t("settings:providers.anthropicUseAzureFoundry")} + + {azureFoundryEnabled && ( + <> + + + +
+ {t("settings:providers.anthropicAzureDeploymentNameDescription")} +
+ + )} +
+ )} {supports1MContextBeta && (