Skip to content

Commit c7f9651

Browse files
authored
feat: [ENG-3652] 1M Context window for Sonnet 4, 4.5 (#5308)
* use 1m header on all anthropic providers * tests and changelog * changelog
1 parent 473ee48 commit c7f9651

File tree

12 files changed

+320
-22
lines changed

12 files changed

+320
-22
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"title": "Claude Sonnet 4 and Sonnet 4.5 now support 1M context window",
3+
"description": "Updated AI Gateway to enable 1M context window by default for Claude Sonnet 4 and Claude Sonnet 4.5 models across all providers."
4+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Claude Sonnet 4 and Claude Sonnet 4.5 models on the AI Gateway now support 1M context window by default.
2+
3+
## What's Changed
4+
5+
- **1M Context Window**: Sonnet 4 and Sonnet 4.5 models now use the extended 1M token context window by default
6+
- **All Providers**: This update applies to Anthropic API, AWS Bedrock, and Google Vertex AI
7+
8+
No configuration changes are needed - requests to Sonnet 4 models will automatically use the extended context window.

packages/__tests__/cost/__snapshots__/registrySnapshots.test.ts.snap

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,7 +1347,7 @@ exports[`Registry Snapshots endpoint configurations snapshot 1`] = `
13471347
},
13481348
"anthropic/claude-sonnet-4": {
13491349
"claude-sonnet-4:anthropic": {
1350-
"context": 200000,
1350+
"context": 1000000,
13511351
"crossRegion": false,
13521352
"maxTokens": 64000,
13531353
"modelId": "claude-sonnet-4-20250514",
@@ -1367,7 +1367,7 @@ exports[`Registry Snapshots endpoint configurations snapshot 1`] = `
13671367
],
13681368
},
13691369
"claude-sonnet-4:bedrock": {
1370-
"context": 200000,
1370+
"context": 1000000,
13711371
"crossRegion": true,
13721372
"maxTokens": 64000,
13731373
"modelId": "anthropic.claude-sonnet-4-20250514-v1:0",
@@ -1389,7 +1389,7 @@ exports[`Registry Snapshots endpoint configurations snapshot 1`] = `
13891389
],
13901390
},
13911391
"claude-sonnet-4:helicone": {
1392-
"context": 200000,
1392+
"context": 1000000,
13931393
"crossRegion": false,
13941394
"maxTokens": 64000,
13951395
"modelId": "pa/cd-st-4-20250514",
@@ -1429,7 +1429,7 @@ exports[`Registry Snapshots endpoint configurations snapshot 1`] = `
14291429
],
14301430
},
14311431
"claude-sonnet-4:vertex": {
1432-
"context": 200000,
1432+
"context": 1000000,
14331433
"crossRegion": true,
14341434
"maxTokens": 64000,
14351435
"modelId": "claude-sonnet-4@20250514",
@@ -1451,7 +1451,7 @@ exports[`Registry Snapshots endpoint configurations snapshot 1`] = `
14511451
},
14521452
"anthropic/claude-sonnet-4-5-20250929": {
14531453
"claude-sonnet-4-5-20250929:anthropic": {
1454-
"context": 200000,
1454+
"context": 1000000,
14551455
"crossRegion": false,
14561456
"maxTokens": 64000,
14571457
"modelId": "claude-sonnet-4-5-20250929",
@@ -1471,7 +1471,7 @@ exports[`Registry Snapshots endpoint configurations snapshot 1`] = `
14711471
],
14721472
},
14731473
"claude-sonnet-4-5-20250929:bedrock": {
1474-
"context": 200000,
1474+
"context": 1000000,
14751475
"crossRegion": true,
14761476
"maxTokens": 64000,
14771477
"modelId": "anthropic.claude-sonnet-4-5-20250929-v1:0",
@@ -1493,7 +1493,7 @@ exports[`Registry Snapshots endpoint configurations snapshot 1`] = `
14931493
],
14941494
},
14951495
"claude-sonnet-4-5-20250929:helicone": {
1496-
"context": 200000,
1496+
"context": 1000000,
14971497
"crossRegion": false,
14981498
"maxTokens": 64000,
14991499
"modelId": "pa/claude-sonnet-4-5-20250929",
@@ -1513,7 +1513,7 @@ exports[`Registry Snapshots endpoint configurations snapshot 1`] = `
15131513
],
15141514
},
15151515
"claude-sonnet-4-5-20250929:openrouter": {
1516-
"context": 200000,
1516+
"context": 1000000,
15171517
"crossRegion": false,
15181518
"maxTokens": 64000,
15191519
"modelId": "anthropic/claude-sonnet-4.5",
@@ -1533,7 +1533,7 @@ exports[`Registry Snapshots endpoint configurations snapshot 1`] = `
15331533
],
15341534
},
15351535
"claude-sonnet-4-5-20250929:vertex": {
1536-
"context": 200000,
1536+
"context": 1000000,
15371537
"crossRegion": true,
15381538
"maxTokens": 64000,
15391539
"modelId": "claude-sonnet-4-5@20250929",
@@ -6378,6 +6378,11 @@ exports[`Registry Snapshots pricing snapshot 1`] = `
63786378
"threshold": 0,
63796379
"web_search": 0.01,
63806380
},
6381+
{
6382+
"input": 0.00000633,
6383+
"output": 0.0000237375,
6384+
"threshold": 200000,
6385+
},
63816386
],
63826387
"vertex": [
63836388
{
@@ -6454,6 +6459,11 @@ exports[`Registry Snapshots pricing snapshot 1`] = `
64546459
"output": 0.00002374,
64556460
"threshold": 0,
64566461
},
6462+
{
6463+
"input": 0.00000633,
6464+
"output": 0.0000237375,
6465+
"threshold": 200000,
6466+
},
64576467
],
64586468
"vertex": [
64596469
{
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { AnthropicProvider } from "../../../cost/models/providers/anthropic";
2+
3+
describe("AnthropicProvider", () => {
4+
const provider = new AnthropicProvider();
5+
6+
describe("buildUrl", () => {
7+
it("should return the Anthropic messages endpoint", () => {
8+
const url = provider.buildUrl(
9+
{ providerModelId: "claude-3-haiku" } as any,
10+
{ isStreaming: false }
11+
);
12+
13+
expect(url).toBe("https://api.anthropic.com/v1/messages");
14+
});
15+
});
16+
17+
describe("authenticate", () => {
18+
it("should return x-api-key header with provided key", () => {
19+
const result = provider.authenticate(
20+
{ apiKey: "test-api-key" },
21+
{ providerModelId: "claude-3-haiku" } as any
22+
);
23+
24+
expect(result.headers["x-api-key"]).toBe("test-api-key");
25+
});
26+
27+
it("should return anthropic-version header", () => {
28+
const result = provider.authenticate(
29+
{ apiKey: "test-api-key", bodyMapping: "OPENAI" },
30+
{ providerModelId: "claude-3-haiku" } as any
31+
);
32+
33+
expect(result.headers["anthropic-version"]).toBe("2023-06-01");
34+
});
35+
36+
it("should include anthropic-beta header for sonnet-4 models", () => {
37+
const result = provider.authenticate(
38+
{ apiKey: "test-api-key" },
39+
{ providerModelId: "claude-sonnet-4-20250514" } as any
40+
);
41+
42+
expect(result.headers["x-api-key"]).toBe("test-api-key");
43+
expect(result.headers["anthropic-beta"]).toBe("context-1m-2025-08-07");
44+
});
45+
46+
it("should not include anthropic-beta header for non-sonnet-4 models", () => {
47+
const result = provider.authenticate(
48+
{ apiKey: "test-api-key" },
49+
{ providerModelId: "claude-3-5-sonnet-20241022" } as any
50+
);
51+
52+
expect(result.headers["x-api-key"]).toBe("test-api-key");
53+
expect(result.headers["anthropic-beta"]).toBeUndefined();
54+
});
55+
});
56+
57+
describe("provider metadata", () => {
58+
it("should have correct display name", () => {
59+
expect(provider.displayName).toBe("Anthropic");
60+
});
61+
62+
it("should have correct auth type", () => {
63+
expect(provider.auth).toBe("api-key");
64+
});
65+
66+
it("should have correct base URL", () => {
67+
expect(provider.baseUrl).toBe("https://api.anthropic.com");
68+
});
69+
});
70+
});
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { BedrockProvider } from "../../../cost/models/providers/bedrock";
2+
3+
describe("BedrockProvider", () => {
4+
const provider = new BedrockProvider();
5+
6+
describe("buildUrl", () => {
7+
it("should build invoke URL for non-streaming requests", () => {
8+
const url = provider.buildUrl(
9+
{
10+
providerModelId: "anthropic.claude-3-haiku-20240307-v1:0",
11+
modelConfig: { providerModelId: "anthropic.claude-3-haiku-20240307-v1:0" },
12+
userConfig: { region: "us-east-1" },
13+
} as any,
14+
{ isStreaming: false }
15+
);
16+
17+
expect(url).toBe(
18+
"https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-haiku-20240307-v1:0/invoke"
19+
);
20+
});
21+
22+
it("should build invoke-with-response-stream URL for streaming requests", () => {
23+
const url = provider.buildUrl(
24+
{
25+
providerModelId: "anthropic.claude-3-haiku-20240307-v1:0",
26+
modelConfig: { providerModelId: "anthropic.claude-3-haiku-20240307-v1:0" },
27+
userConfig: { region: "us-west-2" },
28+
} as any,
29+
{ isStreaming: true }
30+
);
31+
32+
expect(url).toBe(
33+
"https://bedrock-runtime.us-west-2.amazonaws.com/model/anthropic.claude-3-haiku-20240307-v1:0/invoke-with-response-stream"
34+
);
35+
});
36+
37+
it("should add region prefix for cross-region requests", () => {
38+
const url = provider.buildUrl(
39+
{
40+
providerModelId: "anthropic.claude-3-haiku-20240307-v1:0",
41+
modelConfig: { providerModelId: "anthropic.claude-3-haiku-20240307-v1:0" },
42+
userConfig: { region: "us-east-1", crossRegion: true },
43+
} as any,
44+
{ isStreaming: false }
45+
);
46+
47+
expect(url).toBe(
48+
"https://bedrock-runtime.us-east-1.amazonaws.com/model/us.anthropic.claude-3-haiku-20240307-v1:0/invoke"
49+
);
50+
});
51+
52+
it("should default to us-east-1 region when not specified", () => {
53+
const url = provider.buildUrl(
54+
{
55+
providerModelId: "anthropic.claude-3-haiku-20240307-v1:0",
56+
modelConfig: { providerModelId: "anthropic.claude-3-haiku-20240307-v1:0" },
57+
userConfig: {},
58+
} as any,
59+
{ isStreaming: false }
60+
);
61+
62+
expect(url).toBe(
63+
"https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-haiku-20240307-v1:0/invoke"
64+
);
65+
});
66+
});
67+
68+
describe("authenticate", () => {
69+
it("should throw error when apiKey is missing", async () => {
70+
await expect(
71+
provider.authenticate(
72+
{ secretKey: "test-secret-key" },
73+
{ providerModelId: "claude-3-haiku", userConfig: { region: "us-east-1" } } as any
74+
)
75+
).rejects.toThrow("Bedrock requires both apiKey and secretKey");
76+
});
77+
78+
it("should throw error when secretKey is missing", async () => {
79+
await expect(
80+
provider.authenticate(
81+
{ apiKey: "test-access-key" },
82+
{ providerModelId: "claude-3-haiku", userConfig: { region: "us-east-1" } } as any
83+
)
84+
).rejects.toThrow("Bedrock requires both apiKey and secretKey");
85+
});
86+
87+
it("should throw error when requestMethod is missing", async () => {
88+
await expect(
89+
provider.authenticate(
90+
{
91+
apiKey: "test-access-key",
92+
secretKey: "test-secret-key",
93+
requestUrl: "https://test.com",
94+
requestBody: "{}"
95+
},
96+
{ providerModelId: "claude-3-haiku", userConfig: { region: "us-east-1" } } as any
97+
)
98+
).rejects.toThrow("Bedrock authentication requires requestMethod, requestUrl, and requestBody");
99+
});
100+
});
101+
102+
describe("buildRequestBody", () => {
103+
it("should add anthropic_version for Claude models with OPENAI mapping", () => {
104+
const body = provider.buildRequestBody(
105+
{ providerModelId: "anthropic.claude-3-haiku-20240307-v1:0" } as any,
106+
{
107+
bodyMapping: "OPENAI",
108+
toAnthropic: (body: any) => ({ ...body, converted: true }),
109+
parsedBody: {
110+
model: "claude-3-haiku",
111+
messages: [{ role: "user", content: "Test" }]
112+
}
113+
} as any
114+
);
115+
116+
const parsed = JSON.parse(body);
117+
expect(parsed.anthropic_version).toBe("bedrock-2023-05-31");
118+
expect(parsed.converted).toBe(true);
119+
expect(parsed.model).toBeUndefined();
120+
expect(parsed.stream).toBeUndefined();
121+
});
122+
123+
it("should handle Claude models without OPENAI mapping", () => {
124+
const body = provider.buildRequestBody(
125+
{ providerModelId: "anthropic.claude-3-5-sonnet-20241022-v1:0" } as any,
126+
{
127+
bodyMapping: "NO_MAPPING",
128+
parsedBody: {
129+
messages: [{ role: "user", content: "Hello" }],
130+
max_tokens: 1024
131+
}
132+
} as any
133+
);
134+
135+
const parsed = JSON.parse(body);
136+
expect(parsed.anthropic_version).toBe("bedrock-2023-05-31");
137+
expect(parsed.messages).toEqual([{ role: "user", content: "Hello" }]);
138+
expect(parsed.max_tokens).toBe(1024);
139+
expect(parsed.model).toBeUndefined();
140+
expect(parsed.stream).toBeUndefined();
141+
});
142+
});
143+
144+
describe("provider metadata", () => {
145+
it("should have correct display name", () => {
146+
expect(provider.displayName).toBe("AWS Bedrock");
147+
});
148+
149+
it("should have correct auth type", () => {
150+
expect(provider.auth).toBe("aws-signature");
151+
});
152+
153+
it("should have correct required config", () => {
154+
expect(provider.requiredConfig).toEqual(["region"]);
155+
});
156+
157+
it("should have correct base URL template", () => {
158+
expect(provider.baseUrl).toBe("https://bedrock-runtime.{region}.amazonaws.com");
159+
});
160+
});
161+
});

packages/__tests__/cost/providers/vertex.test.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ describe("VertexProvider", () => {
298298
it("should return Bearer token with Google access token", async () => {
299299
const result = await provider.authenticate(
300300
{ apiKey: '{"type":"service_account"}', orgId: "test-org" },
301-
{} as any,
301+
{ providerModelId: "claude-3-haiku" } as any,
302302
undefined
303303
);
304304

@@ -326,7 +326,7 @@ describe("VertexProvider", () => {
326326

327327
await provider.authenticate(
328328
{ apiKey: '{"type":"service_account"}', orgId: "test-org" },
329-
{} as any,
329+
{ providerModelId: "claude-3-haiku" } as any,
330330
mockCacheProvider as any
331331
);
332332

@@ -337,6 +337,28 @@ describe("VertexProvider", () => {
337337
mockCacheProvider
338338
);
339339
});
340+
341+
it("should include anthropic-beta header for sonnet-4 models", async () => {
342+
const result = await provider.authenticate(
343+
{ apiKey: '{"type":"service_account"}', orgId: "test-org" },
344+
{ providerModelId: "claude-sonnet-4-20250514" } as any,
345+
undefined
346+
);
347+
348+
expect(result.headers.Authorization).toBe("Bearer test-access-token");
349+
expect(result.headers["anthropic-beta"]).toBe("context-1m-2025-08-07");
350+
});
351+
352+
it("should not include anthropic-beta header for non-sonnet-4 models", async () => {
353+
const result = await provider.authenticate(
354+
{ apiKey: '{"type":"service_account"}', orgId: "test-org" },
355+
{ providerModelId: "claude-3-5-sonnet-20241022" } as any,
356+
undefined
357+
);
358+
359+
expect(result.headers.Authorization).toBe("Bearer test-access-token");
360+
expect(result.headers["anthropic-beta"]).toBeUndefined();
361+
});
340362
});
341363

342364
describe("Edge cases", () => {

0 commit comments

Comments
 (0)