Skip to content

Commit 5d890d8

Browse files
committed
fix: map claude-sonnet-4-5 alias to full snapshot name claude-sonnet-4-5-20250929
- Add claude-sonnet-4-5-20250929 model definition to anthropic.ts - Update AnthropicHandler to map the alias to snapshot name for API calls - Add tests to verify the mapping works correctly - Fixes issue where custom base URLs that require exact snapshot IDs were failing Fixes #8545
1 parent 5a3f911 commit 5d890d8

File tree

3 files changed

+94
-6
lines changed

3 files changed

+94
-6
lines changed

packages/types/src/providers/anthropic.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,28 @@ export const anthropicModels = {
2828
},
2929
],
3030
},
31+
"claude-sonnet-4-5-20250929": {
32+
maxTokens: 64_000, // Overridden to 8k if `enableReasoningEffort` is false.
33+
contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07'
34+
supportsImages: true,
35+
supportsComputerUse: true,
36+
supportsPromptCache: true,
37+
inputPrice: 3.0, // $3 per million input tokens (≤200K context)
38+
outputPrice: 15.0, // $15 per million output tokens (≤200K context)
39+
cacheWritesPrice: 3.75, // $3.75 per million tokens
40+
cacheReadsPrice: 0.3, // $0.30 per million tokens
41+
supportsReasoningBudget: true,
42+
// Tiered pricing for extended context (requires beta flag 'context-1m-2025-08-07')
43+
tiers: [
44+
{
45+
contextWindow: 1_000_000, // 1M tokens with beta flag
46+
inputPrice: 6.0, // $6 per million input tokens (>200K context)
47+
outputPrice: 22.5, // $22.50 per million output tokens (>200K context)
48+
cacheWritesPrice: 7.5, // $7.50 per million tokens (>200K context)
49+
cacheReadsPrice: 0.6, // $0.60 per million tokens (>200K context)
50+
},
51+
],
52+
},
3153
"claude-sonnet-4-20250514": {
3254
maxTokens: 64_000, // Overridden to 8k if `enableReasoningEffort` is false.
3355
contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07'

src/api/providers/__tests__/anthropic.spec.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,5 +288,55 @@ describe("AnthropicHandler", () => {
288288
expect(model.info.inputPrice).toBe(6.0)
289289
expect(model.info.outputPrice).toBe(22.5)
290290
})
291+
292+
it("should map claude-sonnet-4-5 to claude-sonnet-4-5-20250929 for API calls", async () => {
293+
const handler = new AnthropicHandler({
294+
apiKey: "test-api-key",
295+
apiModelId: "claude-sonnet-4-5",
296+
})
297+
298+
// Test createMessage
299+
const stream = handler.createMessage("Test system", [{ role: "user", content: "Test message" }])
300+
301+
// Consume the stream to trigger the API call
302+
for await (const chunk of stream) {
303+
// Just consume the stream
304+
}
305+
306+
// Verify the API was called with the full snapshot name
307+
expect(mockCreate).toHaveBeenCalledWith(
308+
expect.objectContaining({
309+
model: "claude-sonnet-4-5-20250929",
310+
stream: true,
311+
}),
312+
expect.any(Object),
313+
)
314+
315+
// Clear mock for next test
316+
mockCreate.mockClear()
317+
318+
// Test completePrompt
319+
await handler.completePrompt("Test prompt")
320+
321+
// Verify the API was called with the full snapshot name
322+
expect(mockCreate).toHaveBeenCalledWith(
323+
expect.objectContaining({
324+
model: "claude-sonnet-4-5-20250929",
325+
stream: false,
326+
}),
327+
)
328+
})
329+
330+
it("should handle claude-sonnet-4-5-20250929 model directly", () => {
331+
const handler = new AnthropicHandler({
332+
apiKey: "test-api-key",
333+
apiModelId: "claude-sonnet-4-5-20250929",
334+
})
335+
const model = handler.getModel()
336+
expect(model.id).toBe("claude-sonnet-4-5-20250929")
337+
expect(model.info.maxTokens).toBe(64000)
338+
expect(model.info.contextWindow).toBe(200000)
339+
expect(model.info.supportsReasoningBudget).toBe(true)
340+
})
291341
})
292342
})

src/api/providers/anthropic.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,20 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
4747

4848
// Add 1M context beta flag if enabled for Claude Sonnet 4 and 4.5
4949
if (
50-
(modelId === "claude-sonnet-4-20250514" || modelId === "claude-sonnet-4-5") &&
50+
(modelId === "claude-sonnet-4-20250514" ||
51+
modelId === "claude-sonnet-4-5" ||
52+
modelId === "claude-sonnet-4-5-20250929") &&
5153
this.options.anthropicBeta1MContext
5254
) {
5355
betas.push("context-1m-2025-08-07")
5456
}
5557

58+
// Map the alias to the full snapshot name for API calls
59+
const apiModelId = modelId === "claude-sonnet-4-5" ? "claude-sonnet-4-5-20250929" : modelId
60+
5661
switch (modelId) {
5762
case "claude-sonnet-4-5":
63+
case "claude-sonnet-4-5-20250929":
5864
case "claude-sonnet-4-20250514":
5965
case "claude-opus-4-1-20250805":
6066
case "claude-opus-4-20250514":
@@ -83,7 +89,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
8389

8490
stream = await this.client.messages.create(
8591
{
86-
model: modelId,
92+
model: apiModelId,
8793
max_tokens: maxTokens ?? ANTHROPIC_DEFAULT_MAX_TOKENS,
8894
temperature,
8995
thinking,
@@ -115,6 +121,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
115121
// Then check for models that support prompt caching
116122
switch (modelId) {
117123
case "claude-sonnet-4-5":
124+
case "claude-sonnet-4-5-20250929":
118125
case "claude-sonnet-4-20250514":
119126
case "claude-opus-4-1-20250805":
120127
case "claude-opus-4-20250514":
@@ -134,7 +141,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
134141
}
135142
default: {
136143
stream = (await this.client.messages.create({
137-
model: modelId,
144+
model: apiModelId,
138145
max_tokens: maxTokens ?? ANTHROPIC_DEFAULT_MAX_TOKENS,
139146
temperature,
140147
system: [{ text: systemPrompt, type: "text" }],
@@ -249,7 +256,10 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
249256
let info: ModelInfo = anthropicModels[id]
250257

251258
// If 1M context beta is enabled for Claude Sonnet 4 or 4.5, update the model info
252-
if ((id === "claude-sonnet-4-20250514" || id === "claude-sonnet-4-5") && this.options.anthropicBeta1MContext) {
259+
if (
260+
(id === "claude-sonnet-4-20250514" || id === "claude-sonnet-4-5" || id === "claude-sonnet-4-5-20250929") &&
261+
this.options.anthropicBeta1MContext
262+
) {
253263
// Use the tier pricing for 1M context
254264
const tier = info.tiers?.[0]
255265
if (tier) {
@@ -286,8 +296,11 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
286296
async completePrompt(prompt: string) {
287297
let { id: model, temperature } = this.getModel()
288298

299+
// Map the alias to the full snapshot name for API calls
300+
const apiModel = model === "claude-sonnet-4-5" ? "claude-sonnet-4-5-20250929" : model
301+
289302
const message = await this.client.messages.create({
290-
model,
303+
model: apiModel,
291304
max_tokens: ANTHROPIC_DEFAULT_MAX_TOKENS,
292305
thinking: undefined,
293306
temperature,
@@ -310,8 +323,11 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
310323
// Use the current model
311324
const { id: model } = this.getModel()
312325

326+
// Map the alias to the full snapshot name for API calls
327+
const apiModel = model === "claude-sonnet-4-5" ? "claude-sonnet-4-5-20250929" : model
328+
313329
const response = await this.client.messages.countTokens({
314-
model,
330+
model: apiModel,
315331
messages: [{ role: "user", content: content }],
316332
})
317333

0 commit comments

Comments
 (0)