diff --git a/packages/types/src/providers/anthropic.ts b/packages/types/src/providers/anthropic.ts index 759ebdea555..bd51d7ed719 100644 --- a/packages/types/src/providers/anthropic.ts +++ b/packages/types/src/providers/anthropic.ts @@ -28,6 +28,28 @@ export const anthropicModels = { }, ], }, + "claude-sonnet-4-5-20250929": { + 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' diff --git a/src/api/providers/__tests__/anthropic.spec.ts b/src/api/providers/__tests__/anthropic.spec.ts index b05e50125b8..f6f7a111dc6 100644 --- a/src/api/providers/__tests__/anthropic.spec.ts +++ b/src/api/providers/__tests__/anthropic.spec.ts @@ -288,5 +288,55 @@ describe("AnthropicHandler", () => { expect(model.info.inputPrice).toBe(6.0) expect(model.info.outputPrice).toBe(22.5) }) + + it("should map claude-sonnet-4-5 to claude-sonnet-4-5-20250929 for API calls", async () => { + const handler = new AnthropicHandler({ + apiKey: "test-api-key", + apiModelId: "claude-sonnet-4-5", + }) + + // Test createMessage + const stream = handler.createMessage("Test system", [{ role: "user", content: "Test message" }]) + + // Consume the stream to trigger the API call + for await (const chunk of stream) { + // Just consume the stream + } + + // Verify the API was called with the full snapshot name + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + model: "claude-sonnet-4-5-20250929", + stream: true, + }), + expect.any(Object), + ) + + // Clear mock for next test + mockCreate.mockClear() + + // Test completePrompt + await handler.completePrompt("Test prompt") + + // Verify the API was called with the full snapshot name + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + model: "claude-sonnet-4-5-20250929", + stream: false, + }), + ) + }) + + it("should handle claude-sonnet-4-5-20250929 model directly", () => { + const handler = new AnthropicHandler({ + apiKey: "test-api-key", + apiModelId: "claude-sonnet-4-5-20250929", + }) + const model = handler.getModel() + expect(model.id).toBe("claude-sonnet-4-5-20250929") + expect(model.info.maxTokens).toBe(64000) + expect(model.info.contextWindow).toBe(200000) + expect(model.info.supportsReasoningBudget).toBe(true) + }) }) }) diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 3fb60c0e4fd..56373e9924d 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -47,14 +47,20 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa // Add 1M context beta flag if enabled for Claude Sonnet 4 and 4.5 if ( - (modelId === "claude-sonnet-4-20250514" || modelId === "claude-sonnet-4-5") && + (modelId === "claude-sonnet-4-20250514" || + modelId === "claude-sonnet-4-5" || + modelId === "claude-sonnet-4-5-20250929") && this.options.anthropicBeta1MContext ) { betas.push("context-1m-2025-08-07") } + // Map the alias to the full snapshot name for API calls + const apiModelId = modelId === "claude-sonnet-4-5" ? "claude-sonnet-4-5-20250929" : modelId + switch (modelId) { case "claude-sonnet-4-5": + case "claude-sonnet-4-5-20250929": case "claude-sonnet-4-20250514": case "claude-opus-4-1-20250805": case "claude-opus-4-20250514": @@ -83,7 +89,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa stream = await this.client.messages.create( { - model: modelId, + model: apiModelId, max_tokens: maxTokens ?? ANTHROPIC_DEFAULT_MAX_TOKENS, temperature, thinking, @@ -115,6 +121,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa // Then check for models that support prompt caching switch (modelId) { case "claude-sonnet-4-5": + case "claude-sonnet-4-5-20250929": case "claude-sonnet-4-20250514": case "claude-opus-4-1-20250805": case "claude-opus-4-20250514": @@ -134,7 +141,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa } default: { stream = (await this.client.messages.create({ - model: modelId, + model: apiModelId, max_tokens: maxTokens ?? ANTHROPIC_DEFAULT_MAX_TOKENS, temperature, system: [{ text: systemPrompt, type: "text" }], @@ -249,7 +256,10 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa let info: ModelInfo = anthropicModels[id] // 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-sonnet-4-5") && this.options.anthropicBeta1MContext) { + if ( + (id === "claude-sonnet-4-20250514" || id === "claude-sonnet-4-5" || id === "claude-sonnet-4-5-20250929") && + this.options.anthropicBeta1MContext + ) { // Use the tier pricing for 1M context const tier = info.tiers?.[0] if (tier) { @@ -286,8 +296,11 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa async completePrompt(prompt: string) { let { id: model, temperature } = this.getModel() + // Map the alias to the full snapshot name for API calls + const apiModel = model === "claude-sonnet-4-5" ? "claude-sonnet-4-5-20250929" : model + const message = await this.client.messages.create({ - model, + model: apiModel, max_tokens: ANTHROPIC_DEFAULT_MAX_TOKENS, thinking: undefined, temperature, @@ -310,8 +323,11 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa // Use the current model const { id: model } = this.getModel() + // Map the alias to the full snapshot name for API calls + const apiModel = model === "claude-sonnet-4-5" ? "claude-sonnet-4-5-20250929" : model + const response = await this.client.messages.countTokens({ - model, + model: apiModel, messages: [{ role: "user", content: content }], })