Browse Source

feat: add Claude 4.5 Sonnet model across all providers (#8368)

Daniel 5 months ago
parent
commit
6a91385f05

+ 22 - 0
packages/types/src/providers/anthropic.ts

@@ -6,6 +6,28 @@ export type AnthropicModelId = keyof typeof anthropicModels
 export const anthropicDefaultModelId: AnthropicModelId = "claude-sonnet-4-20250514"
 
 export const anthropicModels = {
+	"claude-4.5-sonnet": {
+		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,
+		supportsComputerUse: 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-20250514": {
 		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'

+ 15 - 0
packages/types/src/providers/bedrock.ts

@@ -13,6 +13,21 @@ export const bedrockDefaultPromptRouterModelId: BedrockModelId = "anthropic.clau
 // of the default prompt routers AWS enabled for GA of the promot router
 // feature.
 export const bedrockModels = {
+	"anthropic.claude-4.5-sonnet-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		supportsReasoningBudget: true,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		cacheWritesPrice: 3.75,
+		cacheReadsPrice: 0.3,
+		minTokensPerCachePoint: 1024,
+		maxCachePoints: 4,
+		cachableFields: ["system", "messages", "tools"],
+	},
 	"amazon.nova-pro-v1:0": {
 		maxTokens: 5000,
 		contextWindow: 300_000,

+ 8 - 0
packages/types/src/providers/claude-code.ts

@@ -40,6 +40,14 @@ export function getClaudeCodeModelId(baseModelId: ClaudeCodeModelId, useVertex =
 }
 
 export const claudeCodeModels = {
+	"claude-4.5-sonnet": {
+		...anthropicModels["claude-4.5-sonnet"],
+		supportsImages: false,
+		supportsPromptCache: true, // Claude Code does report cache tokens
+		supportsReasoningEffort: false,
+		supportsReasoningBudget: false,
+		requiredReasoningBudget: false,
+	},
 	"claude-sonnet-4-20250514": {
 		...anthropicModels["claude-sonnet-4-20250514"],
 		supportsImages: false,

+ 3 - 0
packages/types/src/providers/openrouter.ts

@@ -38,6 +38,7 @@ export const OPEN_ROUTER_PROMPT_CACHING_MODELS = new Set([
 	"anthropic/claude-3.7-sonnet:beta",
 	"anthropic/claude-3.7-sonnet:thinking",
 	"anthropic/claude-sonnet-4",
+	"anthropic/claude-4.5-sonnet",
 	"anthropic/claude-opus-4",
 	"anthropic/claude-opus-4.1",
 	"google/gemini-2.5-flash-preview",
@@ -59,6 +60,7 @@ export const OPEN_ROUTER_COMPUTER_USE_MODELS = new Set([
 	"anthropic/claude-3.7-sonnet:beta",
 	"anthropic/claude-3.7-sonnet:thinking",
 	"anthropic/claude-sonnet-4",
+	"anthropic/claude-4.5-sonnet",
 	"anthropic/claude-opus-4",
 	"anthropic/claude-opus-4.1",
 ])
@@ -81,6 +83,7 @@ export const OPEN_ROUTER_REASONING_BUDGET_MODELS = new Set([
 	"anthropic/claude-opus-4",
 	"anthropic/claude-opus-4.1",
 	"anthropic/claude-sonnet-4",
+	"anthropic/claude-4.5-sonnet",
 	"google/gemini-2.5-pro-preview",
 	"google/gemini-2.5-pro",
 	"google/gemini-2.5-flash-preview-05-20",

+ 12 - 0
packages/types/src/providers/vertex.ts

@@ -6,6 +6,18 @@ export type VertexModelId = keyof typeof vertexModels
 export const vertexDefaultModelId: VertexModelId = "claude-sonnet-4@20250514"
 
 export const vertexModels = {
+	"claude-4.5-sonnet": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		cacheWritesPrice: 3.75,
+		cacheReadsPrice: 0.3,
+		supportsReasoningBudget: true,
+	},
 	"gemini-2.5-flash-preview-05-20:thinking": {
 		maxTokens: 65_535,
 		contextWindow: 1_048_576,

+ 24 - 0
src/api/providers/__tests__/anthropic.spec.ts

@@ -264,5 +264,29 @@ describe("AnthropicHandler", () => {
 			expect(result.reasoningBudget).toBeUndefined()
 			expect(result.temperature).toBe(0)
 		})
+
+		it("should handle Claude 4.5 Sonnet model correctly", () => {
+			const handler = new AnthropicHandler({
+				apiKey: "test-api-key",
+				apiModelId: "claude-4.5-sonnet",
+			})
+			const model = handler.getModel()
+			expect(model.id).toBe("claude-4.5-sonnet")
+			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",
+				apiModelId: "claude-4.5-sonnet",
+				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)
+		})
 	})
 })

+ 9 - 4
src/api/providers/anthropic.ts

@@ -45,12 +45,16 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
 		const cacheControl: CacheControlEphemeral = { type: "ephemeral" }
 		let { id: modelId, betas = [], maxTokens, temperature, reasoning: thinking } = this.getModel()
 
-		// Add 1M context beta flag if enabled for Claude Sonnet 4
-		if (modelId === "claude-sonnet-4-20250514" && this.options.anthropicBeta1MContext) {
+		// Add 1M context beta flag if enabled for Claude Sonnet 4 and 4.5
+		if (
+			(modelId === "claude-sonnet-4-20250514" || modelId === "claude-4.5-sonnet") &&
+			this.options.anthropicBeta1MContext
+		) {
 			betas.push("context-1m-2025-08-07")
 		}
 
 		switch (modelId) {
+			case "claude-4.5-sonnet":
 			case "claude-sonnet-4-20250514":
 			case "claude-opus-4-1-20250805":
 			case "claude-opus-4-20250514":
@@ -110,6 +114,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
 
 						// Then check for models that support prompt caching
 						switch (modelId) {
+							case "claude-4.5-sonnet":
 							case "claude-sonnet-4-20250514":
 							case "claude-opus-4-1-20250805":
 							case "claude-opus-4-20250514":
@@ -243,8 +248,8 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
 		let id = modelId && modelId in anthropicModels ? (modelId as AnthropicModelId) : anthropicDefaultModelId
 		let info: ModelInfo = anthropicModels[id]
 
-		// If 1M context beta is enabled for Claude Sonnet 4, update the model info
-		if (id === "claude-sonnet-4-20250514" && this.options.anthropicBeta1MContext) {
+		// If 1M context beta is enabled for Claude Sonnet 4 or 4.5, update the model info
+		if ((id === "claude-sonnet-4-20250514" || id === "claude-4.5-sonnet") && this.options.anthropicBeta1MContext) {
 			// Use the tier pricing for 1M context
 			const tier = info.tiers?.[0]
 			if (tier) {

+ 3 - 0
src/api/providers/fetchers/__tests__/openrouter.spec.ts

@@ -34,6 +34,7 @@ describe("OpenRouter API", () => {
 				"google/gemini-2.5-flash", // OpenRouter doesn't report this as supporting prompt caching
 				"google/gemini-2.5-flash-lite-preview-06-17", // OpenRouter doesn't report this as supporting prompt caching
 				"anthropic/claude-opus-4.1", // Not yet available in OpenRouter API
+				"anthropic/claude-4.5-sonnet", // Not yet available in OpenRouter API
 			])
 
 			const ourCachingModels = Array.from(OPEN_ROUTER_PROMPT_CACHING_MODELS).filter(
@@ -52,6 +53,7 @@ describe("OpenRouter API", () => {
 
 			const excludedComputerUseModels = new Set([
 				"anthropic/claude-opus-4.1", // Not yet available in OpenRouter API
+				"anthropic/claude-4.5-sonnet", // Not yet available in OpenRouter API
 			])
 
 			const expectedComputerUseModels = Array.from(OPEN_ROUTER_COMPUTER_USE_MODELS)
@@ -134,6 +136,7 @@ describe("OpenRouter API", () => {
 				"google/gemini-2.5-flash-lite-preview-06-17",
 				"google/gemini-2.5-pro",
 				"anthropic/claude-opus-4.1", // Not yet available in OpenRouter API
+				"anthropic/claude-4.5-sonnet", // Not yet available in OpenRouter API
 			])
 
 			const expectedReasoningBudgetModels = Array.from(OPEN_ROUTER_REASONING_BUDGET_MODELS)

+ 2 - 1
webview-ui/src/components/settings/providers/Anthropic.tsx

@@ -22,7 +22,8 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro
 	const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl)
 
 	// Check if the current model supports 1M context beta
-	const supports1MContextBeta = selectedModel?.id === "claude-sonnet-4-20250514"
+	const supports1MContextBeta =
+		selectedModel?.id === "claude-sonnet-4-20250514" || selectedModel?.id === "claude-4.5-sonnet"
 
 	const handleInputChange = useCallback(
 		<K extends keyof ProviderSettings, E>(