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 && (