diff --git a/packages/types/src/providers/anthropic.ts b/packages/types/src/providers/anthropic.ts index 62e377c7e54..40a3d885d84 100644 --- a/packages/types/src/providers/anthropic.ts +++ b/packages/types/src/providers/anthropic.ts @@ -7,6 +7,27 @@ export type AnthropicModelId = keyof typeof anthropicModels export const anthropicDefaultModelId: AnthropicModelId = "claude-sonnet-4-5" export const anthropicModels = { + "claude-sonnet-4-6": { + maxTokens: 64_000, // Overridden to 8k if `enableReasoningEffort` is false. + contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07' + supportsImages: true, + supportsPromptCache: true, + inputPrice: 3.0, // $3 per million input tokens (≤200K context) + outputPrice: 15.0, // $15 per million output tokens (≤200K context) + cacheWritesPrice: 3.75, // $3.75 per million tokens + cacheReadsPrice: 0.3, // $0.30 per million tokens + supportsReasoningBudget: true, + // Tiered pricing for extended context (requires beta flag 'context-1m-2025-08-07') + tiers: [ + { + contextWindow: 1_000_000, // 1M tokens with beta flag + inputPrice: 6.0, // $6 per million input tokens (>200K context) + outputPrice: 22.5, // $22.50 per million output tokens (>200K context) + cacheWritesPrice: 7.5, // $7.50 per million tokens (>200K context) + cacheReadsPrice: 0.6, // $0.60 per million tokens (>200K context) + }, + ], + }, "claude-sonnet-4-5": { maxTokens: 64_000, // Overridden to 8k if `enableReasoningEffort` is false. contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07' diff --git a/packages/types/src/providers/bedrock.ts b/packages/types/src/providers/bedrock.ts index 008961b3018..575db6984af 100644 --- a/packages/types/src/providers/bedrock.ts +++ b/packages/types/src/providers/bedrock.ts @@ -27,6 +27,30 @@ export const bedrockModels = { maxCachePoints: 4, cachableFields: ["system", "messages", "tools"], }, + "anthropic.claude-sonnet-4-6-20260114-v1:0": { + maxTokens: 8192, + contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07' + supportsImages: true, + supportsPromptCache: true, + supportsReasoningBudget: true, + inputPrice: 3.0, // $3 per million input tokens (≤200K context) + outputPrice: 15.0, // $15 per million output tokens (≤200K context) + cacheWritesPrice: 3.75, // $3.75 per million tokens + cacheReadsPrice: 0.3, // $0.30 per million tokens + minTokensPerCachePoint: 1024, + maxCachePoints: 4, + cachableFields: ["system", "messages", "tools"], + // Tiered pricing for extended context (requires beta flag 'context-1m-2025-08-07') + tiers: [ + { + contextWindow: 1_000_000, // 1M tokens with beta flag + inputPrice: 6.0, // $6 per million input tokens (>200K context) + outputPrice: 22.5, // $22.50 per million output tokens (>200K context) + cacheWritesPrice: 7.5, // $7.50 per million tokens (>200K context) + cacheReadsPrice: 0.6, // $0.60 per million tokens (>200K context) + }, + ], + }, "amazon.nova-pro-v1:0": { maxTokens: 5000, contextWindow: 300_000, @@ -499,6 +523,7 @@ export const BEDROCK_REGIONS = [ export const BEDROCK_1M_CONTEXT_MODEL_IDS = [ "anthropic.claude-sonnet-4-20250514-v1:0", "anthropic.claude-sonnet-4-5-20250929-v1:0", + "anthropic.claude-sonnet-4-6-20260114-v1:0", "anthropic.claude-opus-4-6-v1", ] as const @@ -506,12 +531,14 @@ export const BEDROCK_1M_CONTEXT_MODEL_IDS = [ // As of Nov 2025, AWS supports Global Inference for: // - Claude Sonnet 4 // - Claude Sonnet 4.5 +// - Claude Sonnet 4.6 // - Claude Haiku 4.5 // - Claude Opus 4.5 // - Claude Opus 4.6 export const BEDROCK_GLOBAL_INFERENCE_MODEL_IDS = [ "anthropic.claude-sonnet-4-20250514-v1:0", "anthropic.claude-sonnet-4-5-20250929-v1:0", + "anthropic.claude-sonnet-4-6-20260114-v1:0", "anthropic.claude-haiku-4-5-20251001-v1:0", "anthropic.claude-opus-4-5-20251101-v1:0", "anthropic.claude-opus-4-6-v1", diff --git a/packages/types/src/providers/openrouter.ts b/packages/types/src/providers/openrouter.ts index c8168e60244..834f40528e2 100644 --- a/packages/types/src/providers/openrouter.ts +++ b/packages/types/src/providers/openrouter.ts @@ -38,6 +38,7 @@ export const OPEN_ROUTER_PROMPT_CACHING_MODELS = new Set([ "anthropic/claude-3.7-sonnet:thinking", "anthropic/claude-sonnet-4", "anthropic/claude-sonnet-4.5", + "anthropic/claude-sonnet-4.6", "anthropic/claude-opus-4", "anthropic/claude-opus-4.1", "anthropic/claude-opus-4.5", @@ -75,6 +76,7 @@ export const OPEN_ROUTER_REASONING_BUDGET_MODELS = new Set([ "anthropic/claude-opus-4.6", "anthropic/claude-sonnet-4", "anthropic/claude-sonnet-4.5", + "anthropic/claude-sonnet-4.6", "anthropic/claude-haiku-4.5", "google/gemini-2.5-pro-preview", "google/gemini-2.5-pro", diff --git a/packages/types/src/providers/vercel-ai-gateway.ts b/packages/types/src/providers/vercel-ai-gateway.ts index 43a94a0697c..ac633747ba2 100644 --- a/packages/types/src/providers/vercel-ai-gateway.ts +++ b/packages/types/src/providers/vercel-ai-gateway.ts @@ -14,6 +14,7 @@ export const VERCEL_AI_GATEWAY_PROMPT_CACHING_MODELS = new Set([ "anthropic/claude-opus-4.5", "anthropic/claude-opus-4.6", "anthropic/claude-sonnet-4", + "anthropic/claude-sonnet-4.6", "openai/gpt-4.1", "openai/gpt-4.1-mini", "openai/gpt-4.1-nano", @@ -55,6 +56,7 @@ export const VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS = new Set([ "anthropic/claude-opus-4.5", "anthropic/claude-opus-4.6", "anthropic/claude-sonnet-4", + "anthropic/claude-sonnet-4.6", "google/gemini-1.5-flash", "google/gemini-1.5-pro", "google/gemini-2.0-flash", diff --git a/packages/types/src/providers/vertex.ts b/packages/types/src/providers/vertex.ts index 55e5648011b..2f8a05602a4 100644 --- a/packages/types/src/providers/vertex.ts +++ b/packages/types/src/providers/vertex.ts @@ -263,6 +263,27 @@ export const vertexModels = { }, ], }, + "claude-sonnet-4-6@20260114": { + maxTokens: 8192, + contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07' + supportsImages: true, + supportsPromptCache: true, + inputPrice: 3.0, // $3 per million input tokens (≤200K context) + outputPrice: 15.0, // $15 per million output tokens (≤200K context) + cacheWritesPrice: 3.75, // $3.75 per million tokens + cacheReadsPrice: 0.3, // $0.30 per million tokens + supportsReasoningBudget: true, + // Tiered pricing for extended context (requires beta flag 'context-1m-2025-08-07') + tiers: [ + { + contextWindow: 1_000_000, // 1M tokens with beta flag + inputPrice: 6.0, // $6 per million input tokens (>200K context) + outputPrice: 22.5, // $22.50 per million output tokens (>200K context) + cacheWritesPrice: 7.5, // $7.50 per million tokens (>200K context) + cacheReadsPrice: 0.6, // $0.60 per million tokens (>200K context) + }, + ], + }, "claude-haiku-4-5@20251001": { maxTokens: 8192, contextWindow: 200_000, @@ -491,6 +512,7 @@ export const vertexModels = { export const VERTEX_1M_CONTEXT_MODEL_IDS = [ "claude-sonnet-4@20250514", "claude-sonnet-4-5@20250929", + "claude-sonnet-4-6@20260114", "claude-opus-4-6", ] as const diff --git a/src/api/providers/__tests__/anthropic-vertex.spec.ts b/src/api/providers/__tests__/anthropic-vertex.spec.ts index 3d9798fde91..dc284ab7543 100644 --- a/src/api/providers/__tests__/anthropic-vertex.spec.ts +++ b/src/api/providers/__tests__/anthropic-vertex.spec.ts @@ -899,6 +899,21 @@ describe("VertexHandler", () => { expect(model.betas).toContain("context-1m-2025-08-07") }) + it("should enable 1M context for Claude Sonnet 4.6 when beta flag is set", () => { + const handler = new AnthropicVertexHandler({ + apiModelId: "claude-sonnet-4-6@20260114", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + vertex1MContext: true, + }) + + const model = handler.getModel() + expect(model.info.contextWindow).toBe(1_000_000) + expect(model.info.inputPrice).toBe(6.0) + expect(model.info.outputPrice).toBe(22.5) + expect(model.betas).toContain("context-1m-2025-08-07") + }) + it("should not enable 1M context when flag is disabled", () => { const handler = new AnthropicVertexHandler({ apiModelId: VERTEX_1M_CONTEXT_MODEL_IDS[0], diff --git a/src/api/providers/__tests__/anthropic.spec.ts b/src/api/providers/__tests__/anthropic.spec.ts index 7a107edbc8b..3731f3a068b 100644 --- a/src/api/providers/__tests__/anthropic.spec.ts +++ b/src/api/providers/__tests__/anthropic.spec.ts @@ -187,6 +187,28 @@ describe("AnthropicHandler", () => { // Verify API expect(mockCreate).toHaveBeenCalled() }) + + it("should include 1M context beta header for Claude Sonnet 4.6 when enabled", async () => { + const sonnet46Handler = new AnthropicHandler({ + apiKey: "test-api-key", + apiModelId: "claude-sonnet-4-6", + anthropicBeta1MContext: true, + }) + + const stream = sonnet46Handler.createMessage(systemPrompt, [ + { + role: "user", + content: [{ type: "text" as const, text: "Hello" }], + }, + ]) + + for await (const _chunk of stream) { + // Consume stream + } + + const requestOptions = mockCreate.mock.calls[mockCreate.mock.calls.length - 1]?.[1] + expect(requestOptions?.headers?.["anthropic-beta"]).toContain("context-1m-2025-08-07") + }) }) describe("completePrompt", () => { @@ -286,6 +308,18 @@ describe("AnthropicHandler", () => { expect(model.info.supportsReasoningBudget).toBe(true) }) + it("should handle Claude 4.6 Sonnet model correctly", () => { + const handler = new AnthropicHandler({ + apiKey: "test-api-key", + apiModelId: "claude-sonnet-4-6", + }) + const model = handler.getModel() + expect(model.id).toBe("claude-sonnet-4-6") + expect(model.info.maxTokens).toBe(64000) + expect(model.info.contextWindow).toBe(200000) + expect(model.info.supportsReasoningBudget).toBe(true) + }) + it("should enable 1M context for Claude 4.5 Sonnet when beta flag is set", () => { const handler = new AnthropicHandler({ apiKey: "test-api-key", @@ -297,6 +331,18 @@ describe("AnthropicHandler", () => { expect(model.info.inputPrice).toBe(6.0) expect(model.info.outputPrice).toBe(22.5) }) + + it("should enable 1M context for Claude 4.6 Sonnet when beta flag is set", () => { + const handler = new AnthropicHandler({ + apiKey: "test-api-key", + apiModelId: "claude-sonnet-4-6", + anthropicBeta1MContext: true, + }) + const model = handler.getModel() + expect(model.info.contextWindow).toBe(1000000) + expect(model.info.inputPrice).toBe(6.0) + expect(model.info.outputPrice).toBe(22.5) + }) }) describe("reasoning block filtering", () => { diff --git a/src/api/providers/__tests__/bedrock.spec.ts b/src/api/providers/__tests__/bedrock.spec.ts index 115cb9fb405..4c45a623251 100644 --- a/src/api/providers/__tests__/bedrock.spec.ts +++ b/src/api/providers/__tests__/bedrock.spec.ts @@ -701,6 +701,21 @@ describe("AwsBedrockHandler", () => { expect(model.info.contextWindow).toBe(1_000_000) }) + it("should apply 1M tier pricing when awsBedrock1MContext is true for Claude Sonnet 4.6", () => { + const handler = new AwsBedrockHandler({ + apiModelId: "anthropic.claude-sonnet-4-6-20260114-v1:0", + awsAccessKey: "test", + awsSecretKey: "test", + awsRegion: "us-east-1", + awsBedrock1MContext: true, + }) + + const model = handler.getModel() + expect(model.info.contextWindow).toBe(1_000_000) + expect(model.info.inputPrice).toBe(6.0) + expect(model.info.outputPrice).toBe(22.5) + }) + it("should use default context window when awsBedrock1MContext is false for Claude Sonnet 4", () => { const handler = new AwsBedrockHandler({ apiModelId: BEDROCK_1M_CONTEXT_MODEL_IDS[0], diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index b2b158f0956..1786a105a5e 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -64,10 +64,11 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa // Filter out non-Anthropic blocks (reasoning, thoughtSignature, etc.) before sending to the API const sanitizedMessages = filterNonAnthropicBlocks(messages) - // Add 1M context beta flag if enabled for supported models (Claude Sonnet 4/4.5, Opus 4.6) + // Add 1M context beta flag if enabled for supported models (Claude Sonnet 4/4.5/4.6, Opus 4.6) if ( (modelId === "claude-sonnet-4-20250514" || modelId === "claude-sonnet-4-5" || + modelId === "claude-sonnet-4-6" || modelId === "claude-opus-4-6") && this.options.anthropicBeta1MContext ) { @@ -80,6 +81,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa } switch (modelId) { + case "claude-sonnet-4-6": case "claude-sonnet-4-5": case "claude-sonnet-4-20250514": case "claude-opus-4-6": @@ -145,6 +147,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa // Then check for models that support prompt caching switch (modelId) { + case "claude-sonnet-4-6": case "claude-sonnet-4-5": case "claude-sonnet-4-20250514": case "claude-opus-4-6": @@ -336,7 +339,10 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa // If 1M context beta is enabled for supported models, update the model info if ( - (id === "claude-sonnet-4-20250514" || id === "claude-sonnet-4-5" || id === "claude-opus-4-6") && + (id === "claude-sonnet-4-20250514" || + id === "claude-sonnet-4-5" || + id === "claude-sonnet-4-6" || + id === "claude-opus-4-6") && this.options.anthropicBeta1MContext ) { // Use the tier pricing for 1M context diff --git a/src/api/providers/fetchers/__tests__/openrouter.spec.ts b/src/api/providers/fetchers/__tests__/openrouter.spec.ts index 3bcd27716f2..bca54b30781 100644 --- a/src/api/providers/fetchers/__tests__/openrouter.spec.ts +++ b/src/api/providers/fetchers/__tests__/openrouter.spec.ts @@ -266,6 +266,30 @@ describe("OpenRouter API", () => { }) describe("parseOpenRouterModel", () => { + it("sets claude-sonnet-4.6 model to Anthropic max tokens", () => { + const mockModel = { + name: "Claude Sonnet 4.6", + description: "Test model", + context_length: 200000, + max_completion_tokens: 8192, + pricing: { + prompt: "0.000003", + completion: "0.000015", + }, + } + + const result = parseOpenRouterModel({ + id: "anthropic/claude-sonnet-4.6", + model: mockModel, + inputModality: ["text"], + outputModality: ["text"], + maxTokens: 8192, + }) + + expect(result.maxTokens).toBe(64000) + expect(result.contextWindow).toBe(200000) + }) + it("sets horizon-alpha model to 32k max tokens", () => { const mockModel = { name: "Horizon Alpha", diff --git a/src/api/providers/fetchers/openrouter.ts b/src/api/providers/fetchers/openrouter.ts index 9fcf3d49cb5..0cf65fb09c3 100644 --- a/src/api/providers/fetchers/openrouter.ts +++ b/src/api/providers/fetchers/openrouter.ts @@ -243,6 +243,11 @@ export const parseOpenRouterModel = ({ modelInfo.maxTokens = anthropicModels["claude-3-7-sonnet-20250219:thinking"].maxTokens } + // Set claude-sonnet-4.6 model to use the correct configuration + if (id === "anthropic/claude-sonnet-4.6") { + modelInfo.maxTokens = anthropicModels["claude-sonnet-4-6"].maxTokens + } + // Set claude-opus-4.1 model to use the correct configuration if (id === "anthropic/claude-opus-4.1") { modelInfo.maxTokens = anthropicModels["claude-opus-4-1-20250805"].maxTokens diff --git a/webview-ui/src/components/settings/providers/Anthropic.tsx b/webview-ui/src/components/settings/providers/Anthropic.tsx index 58fa81d6bc2..6417e34b8be 100644 --- a/webview-ui/src/components/settings/providers/Anthropic.tsx +++ b/webview-ui/src/components/settings/providers/Anthropic.tsx @@ -26,6 +26,7 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro const supports1MContextBeta = selectedModel?.id === "claude-sonnet-4-20250514" || selectedModel?.id === "claude-sonnet-4-5" || + selectedModel?.id === "claude-sonnet-4-6" || selectedModel?.id === "claude-opus-4-6" const handleInputChange = useCallback( diff --git a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts index 8925adf5fda..a8dead311fa 100644 --- a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts +++ b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts @@ -402,6 +402,38 @@ describe("useSelectedModel", () => { }) }) + describe("anthropic provider with 1M context", () => { + beforeEach(() => { + mockUseRouterModels.mockReturnValue({ + data: undefined, + isLoading: false, + isError: false, + } as any) + + mockUseOpenRouterModelProviders.mockReturnValue({ + data: undefined, + isLoading: false, + isError: false, + } as any) + }) + + it("should apply 1M pricing tier for Claude Sonnet 4.6 when enabled", () => { + const apiConfiguration: ProviderSettings = { + apiProvider: "anthropic", + apiModelId: "claude-sonnet-4-6", + anthropicBeta1MContext: true, + } + + const wrapper = createWrapper() + const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper }) + + expect(result.current.id).toBe("claude-sonnet-4-6") + expect(result.current.info?.contextWindow).toBe(1_000_000) + expect(result.current.info?.inputPrice).toBe(6.0) + expect(result.current.info?.outputPrice).toBe(22.5) + }) + }) + describe("bedrock provider with 1M context", () => { beforeEach(() => { mockUseRouterModels.mockReturnValue({ diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 0ac82b50627..8a6e49e212f 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -341,11 +341,14 @@ function getSelectedModel({ // Apply 1M context beta tier pricing for supported Claude 4 models if ( provider === "anthropic" && - (id === "claude-sonnet-4-20250514" || id === "claude-sonnet-4-5" || id === "claude-opus-4-6") && + (id === "claude-sonnet-4-20250514" || + id === "claude-sonnet-4-5" || + id === "claude-sonnet-4-6" || + id === "claude-opus-4-6") && apiConfiguration.anthropicBeta1MContext && baseInfo ) { - // Type assertion since we know claude-sonnet-4-20250514 and claude-sonnet-4-5 have tiers + // Type assertion since supported Claude 4 models include 1M context pricing tiers. const modelWithTiers = baseInfo as typeof baseInfo & { tiers?: Array<{ contextWindow: number