Skip to content

Commit 5d0d6e7

Browse files
feat: add Claude Opus 4.8 support across Anthropic, Bedrock, and Vertex providers (#386)
* feat(bedrock): add anthropic.claude-opus-4-7 to native model registry Adds Claude Opus 4.7 to the Bedrock native model registry with: - Full ModelInfo (maxTokens, contextWindow, pricing, cache config) - supportsReasoningBudget: true (enables thinking budget in UI) - cachableFields for multi-point prompt caching - 1M context tier pricing - Global Inference support Without this entry, custom model usage falls back to guessModelInfoFromId() which lacks supportsReasoningBudget and cachableFields, causing "too many tokens" errors during parallel file injection (no cache = tokens accumulate). Note: Pricing estimated based on claude-opus-4-6-v1. To be verified against Bedrock console pricing page before merge. * feat(bedrock): support Claude 4.7+ adaptive thinking and remove temperature Claude Opus/Sonnet 4.7 introduced breaking API changes: - temperature/top_p/top_k removed (causes 400 error) - thinking.type 'enabled' + budget_tokens removed (causes 400 error) - New thinking.type 'adaptive' with output_config.effort levels - New display: 'summarized' option to surface thinking content Changes: - Detect Gen 4.7+ models via baseModelId.includes('opus-4-7' | 'sonnet-4-7') - Omit temperature from inferenceConfig for 4.7+ models - Use thinking: { type: 'adaptive', display: 'summarized' } for 4.7+ - Set output_config.effort: 'xhigh' (highest level for coding/agentic tasks) - Maintain full backward compatibility with 4.6 and earlier models - Expanded BedrockAdditionalModelFields interface to support both formats References: - Claude 4.7 release notes (Apr 16, 2026) - effort levels: low | medium | high | xhigh | max * feat(types): add claude-opus-4-8 to Anthropic, Bedrock, and Vertex registries - Register claude-opus-4-8 in anthropicModels with 1M context, 128k output, supportsReasoningBudget, supportsReasoningBinary, supportsTemperature: false (mirrors 4.7 - no breaking API changes per the official migration guide). - Register anthropic.claude-opus-4-8 in bedrockModels with cache points, cachableFields, and 1M context tier pricing. - Register claude-opus-4-8 in vertexModels with the same shape. - Add anthropic.claude-opus-4-8 to BEDROCK_1M_CONTEXT_MODEL_IDS and BEDROCK_GLOBAL_INFERENCE_MODEL_IDS. - Add claude-opus-4-8 to VERTEX_1M_CONTEXT_MODEL_IDS. * feat(bedrock): support Claude Opus 4.8 (extends 4.7 adaptive thinking detection) - Anthropic provider: add claude-opus-4-8 to both prompt-caching switch statements so it gets the same handling as 4.7 (native 1M context, no beta header required). - Bedrock provider: rename isGen47Model -> isAdaptiveThinkingModel and expand the pattern to match opus-4-7, opus-4-8, sonnet-4-7, sonnet-4-8. 4.8 inherits the same adaptive-thinking + temperature-rejection contract from 4.7 with no breaking API changes. - OpenAI-compatible provider: update comment to mention 4.8 alongside 4.7; no logic change (already honors the supportsTemperature: false flag). The rename describes the capability (adaptive thinking) rather than a specific generation, making future Claude versions easier to support. * test: add unit coverage for Claude Opus 4.8 across providers - anthropic.spec.ts: 5 cases mirroring 4.7 (1M-beta-header guard, adaptive thinking ON/OFF, custom maxTokens, getModel info). - anthropic-vertex.spec.ts: 1M context tier pricing for Vertex Opus 4.8. - shared/api.spec.ts: getModelMaxOutputTokens hybrid-token handling on 4.8. - bedrock.spec.ts: new 'Claude 4.7+ adaptive thinking' block with 5 cases covering 4.7 + 4.8 adaptive thinking, reasoning-off behaviour, a 4.6 regression guard (budget_tokens + temperature), and cross-region prefix detection (us.anthropic.claude-opus-4-8). 235 unit tests pass, 0 type errors. Validated live end-to-end via Bedrock Global Inference (global.anthropic.claude-opus-4-8). * fix(bedrock): omit temperature in completePrompt for adaptive-thinking models Addresses CodeRabbit review on #386. completePrompt was unconditionally sending temperature in its inferenceConfig, which causes a 400 error for Claude Opus/Sonnet 4.7 and 4.8 (sampling parameters were removed by Anthropic for these models). createMessage already guarded this, but the non-stream path did not. - Extract the adaptive-thinking detection into a private isAdaptiveThinkingModel(modelId) method (parseBaseModelId-aware, so cross-region/global prefixes are handled). - Reuse it in both createMessage and completePrompt so the two request paths stay consistent. - Add two regression tests: completePrompt omits temperature for opus-4-8 and still sends it for opus-4-6. 64 bedrock tests pass, check-types clean. * test(e2e): add Bedrock smoke test for Claude Opus 4.8 Addresses @edelauna's review request on #386 to cover 4.8 in the new Bedrock e2e harness. Mirrors the existing user-agent smoke test but re-points the provider at us.anthropic.claude-opus-4-8. Since 4.8 is an adaptive-thinking model, this exercises the request path that omits temperature (and sends thinking.type "adaptive" when reasoning is enabled), proving a Bedrock round-trip completes without a 400. Runs against the binary-event-stream mock server in CI and against real AWS when BEDROCK_LIVE_E2E=true. The original 4.7-era test is left untouched; model id is overridable via BEDROCK_OPUS_48_MODEL_ID. check-types clean (tsconfig.esm.json). * test(bedrock): cover sonnet-4-7/4-8 branches in isAdaptiveThinkingModel Codecov flagged the sonnet-4-7 and sonnet-4-8 branches of isAdaptiveThinkingModel as uncovered — they have no Bedrock registry entry yet (future-proof guards), so no existing test reached them. Add a focused unit test that calls the private method directly (same pattern the suite already uses for parseBaseModelId / getPrefixForRegion), covering: - all four positive patterns: opus-4-7, opus-4-8, sonnet-4-7, sonnet-4-8 - cross-region / global prefixes (us./eu./global.) via parseBaseModelId - negative cases: opus-4-6, sonnet-4-6, claude-3-5, nova Brings patch coverage to 100%. 68 bedrock tests pass, check-types clean. * fix(vertex): use adaptive thinking for Opus 4.8 --------- Co-authored-by: Vandre Sales <vandre.sales@gmail.com> Co-authored-by: Elliott de Launay <edelauna@gmail.com>
1 parent db07a23 commit 5d0d6e7

12 files changed

Lines changed: 640 additions & 20 deletions

File tree

apps/vscode-e2e/src/suite/providers/bedrock.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ const AWS_BEARER_TOKEN_BEDROCK = process.env.AWS_BEARER_TOKEN_BEDROCK
88
const BEDROCK_REGION = process.env.BEDROCK_REGION ?? "us-east-1"
99
// Use a cross-region inference profile so the token works without per-region model access.
1010
const BEDROCK_MODEL_ID = process.env.BEDROCK_MODEL_ID ?? "us.anthropic.claude-haiku-4-5-20251001-v1:0"
11+
// Claude Opus 4.8 routed through a cross-region inference profile. 4.8 is an
12+
// adaptive-thinking model, so this exercises the request path that omits
13+
// temperature and (when reasoning is enabled) sends thinking.type "adaptive".
14+
const BEDROCK_OPUS_48_MODEL_ID = process.env.BEDROCK_OPUS_48_MODEL_ID ?? "us.anthropic.claude-opus-4-8"
1115
const BEDROCK_LIVE_E2E = process.env.BEDROCK_LIVE_E2E === "true"
1216

1317
suite("Bedrock provider", function () {
@@ -91,4 +95,53 @@ suite("Bedrock provider", function () {
9195
assert.ok(true, "Task completed successfully via Bedrock with ZooCode# userAgentAppId")
9296
}
9397
})
98+
99+
test("Should complete a task end-to-end via AWS Bedrock using Claude Opus 4.8", async () => {
100+
const api = globalThis.api
101+
102+
// Re-point the provider at Claude Opus 4.8 while keeping the same transport
103+
// (mock server in CI, real AWS in live mode). Parity smoke test: it proves the
104+
// 4.8 request path — model resolution, adaptive-thinking payload, and the
105+
// temperature omission required by 4.7+ — completes a Bedrock round-trip
106+
// without a 400. The mock server replies with the same attempt_completion("4")
107+
// tool call regardless of model, so a successful completion exercises request
108+
// formation end-to-end.
109+
if (!process.env.AIMOCK_URL && BEDROCK_LIVE_E2E && AWS_BEARER_TOKEN_BEDROCK) {
110+
await api.setConfiguration({
111+
apiProvider: "bedrock" as const,
112+
awsUseApiKey: true,
113+
awsApiKey: AWS_BEARER_TOKEN_BEDROCK,
114+
awsRegion: BEDROCK_REGION,
115+
apiModelId: BEDROCK_OPUS_48_MODEL_ID,
116+
})
117+
} else {
118+
await api.setConfiguration({
119+
apiProvider: "bedrock" as const,
120+
awsUseApiKey: true,
121+
awsApiKey: "mock-key",
122+
awsRegion: BEDROCK_REGION,
123+
apiModelId: BEDROCK_OPUS_48_MODEL_ID,
124+
awsBedrockEndpoint: mockServer!.url,
125+
awsBedrockEndpointEnabled: true,
126+
})
127+
}
128+
129+
const taskId = await api.startNewTask({
130+
configuration: { mode: "ask", autoApprovalEnabled: true },
131+
text: "bedrock-opus-48-smoke: what is 2+2? Reply with only the number.",
132+
})
133+
134+
await waitUntilCompleted({ api, taskId })
135+
136+
if (mockServer) {
137+
// The request reached the Bedrock endpoint (no 400 from temperature/thinking).
138+
const userAgent = mockServer.lastRequestHeaders?.["user-agent"] as string | undefined
139+
assert.ok(userAgent, "Bedrock request should include user-agent header")
140+
assert.ok(userAgent.includes("ZooCode#"), `user-agent should contain "ZooCode#" — got: ${userAgent}`)
141+
} else {
142+
// Live mode: a successful round-trip proves 4.8 request formation works
143+
// against real AWS Bedrock (adaptive thinking, no rejected sampling params).
144+
assert.ok(true, "Task completed successfully via Bedrock with Claude Opus 4.8")
145+
}
146+
})
94147
})

packages/types/src/providers/anthropic.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,24 @@ export const anthropicModels = {
108108
supportsReasoningBinary: true,
109109
supportsTemperature: false,
110110
},
111+
"claude-opus-4-8": {
112+
maxTokens: 128_000, // Overridden to 8k if `enableReasoningEffort` is false.
113+
contextWindow: 1_000_000, // 1M context window native (no beta header required, same as 4.7)
114+
supportsImages: true,
115+
supportsPromptCache: true,
116+
inputPrice: 5.0, // $5 per million input tokens (regular tier)
117+
outputPrice: 25.0, // $25 per million output tokens (regular tier)
118+
cacheWritesPrice: 6.25, // $6.25 per million tokens
119+
cacheReadsPrice: 0.5, // $0.50 per million tokens
120+
// 4.8 inherits the adaptive-thinking model introduced in 4.7 — no breaking
121+
// API changes. supportsReasoningBudget is kept true so the existing token-cap
122+
// handling and max-token overrides behave identically.
123+
supportsReasoningBudget: true,
124+
// 4.8 still rejects budget_tokens-style thinking payloads, so the UI must
125+
// expose reasoning as a binary on/off toggle on this provider path.
126+
supportsReasoningBinary: true,
127+
supportsTemperature: false,
128+
},
111129
"claude-opus-4-5-20251101": {
112130
maxTokens: 32_000, // Overridden to 8k if `enableReasoningEffort` is false.
113131
contextWindow: 200_000,

packages/types/src/providers/bedrock.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,56 @@ export const bedrockModels = {
167167
},
168168
],
169169
},
170+
"anthropic.claude-opus-4-7": {
171+
maxTokens: 8192,
172+
contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07'
173+
supportsImages: true,
174+
supportsPromptCache: true,
175+
supportsReasoningBudget: true,
176+
inputPrice: 5.0, // $5 per million input tokens (≤200K context) — verify against Bedrock console
177+
outputPrice: 25.0, // $25 per million output tokens (≤200K context) — verify against Bedrock console
178+
cacheWritesPrice: 6.25, // $6.25 per million tokens
179+
cacheReadsPrice: 0.5, // $0.50 per million tokens
180+
minTokensPerCachePoint: 1024,
181+
maxCachePoints: 4,
182+
cachableFields: ["system", "messages", "tools"],
183+
// Tiered pricing for extended context (requires beta flag 'context-1m-2025-08-07')
184+
tiers: [
185+
{
186+
contextWindow: 1_000_000, // 1M tokens with beta flag
187+
inputPrice: 10.0, // $10 per million input tokens (>200K context)
188+
outputPrice: 37.5, // $37.50 per million output tokens (>200K context)
189+
cacheWritesPrice: 12.5, // $12.50 per million tokens (>200K context)
190+
cacheReadsPrice: 1.0, // $1.00 per million tokens (>200K context)
191+
},
192+
],
193+
},
194+
"anthropic.claude-opus-4-8": {
195+
maxTokens: 8192,
196+
contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07'
197+
supportsImages: true,
198+
supportsPromptCache: true,
199+
supportsReasoningBudget: true,
200+
inputPrice: 5.0, // $5 per million input tokens (≤200K context) — verify against Bedrock console
201+
outputPrice: 25.0, // $25 per million output tokens (≤200K context) — verify against Bedrock console
202+
cacheWritesPrice: 6.25, // $6.25 per million tokens
203+
cacheReadsPrice: 0.5, // $0.50 per million tokens
204+
minTokensPerCachePoint: 1024,
205+
maxCachePoints: 4,
206+
cachableFields: ["system", "messages", "tools"],
207+
// Tiered pricing for extended context (requires beta flag 'context-1m-2025-08-07')
208+
// 4.8 inherits the same Bedrock pricing structure as 4.7 — no API breaking changes.
209+
// Adaptive thinking is the only supported reasoning mode (same as 4.7).
210+
tiers: [
211+
{
212+
contextWindow: 1_000_000, // 1M tokens with beta flag
213+
inputPrice: 10.0, // $10 per million input tokens (>200K context)
214+
outputPrice: 37.5, // $37.50 per million output tokens (>200K context)
215+
cacheWritesPrice: 12.5, // $12.50 per million tokens (>200K context)
216+
cacheReadsPrice: 1.0, // $1.00 per million tokens (>200K context)
217+
},
218+
],
219+
},
170220
"anthropic.claude-opus-4-5-20251101-v1:0": {
171221
maxTokens: 8192,
172222
contextWindow: 200_000,
@@ -525,6 +575,8 @@ export const BEDROCK_1M_CONTEXT_MODEL_IDS = [
525575
"anthropic.claude-sonnet-4-5-20250929-v1:0",
526576
"anthropic.claude-sonnet-4-6",
527577
"anthropic.claude-opus-4-6-v1",
578+
"anthropic.claude-opus-4-7",
579+
"anthropic.claude-opus-4-8",
528580
] as const
529581

530582
// Amazon Bedrock models that support Global Inference profiles
@@ -535,13 +587,16 @@ export const BEDROCK_1M_CONTEXT_MODEL_IDS = [
535587
// - Claude Haiku 4.5
536588
// - Claude Opus 4.5
537589
// - Claude Opus 4.6
590+
// - Claude Opus 4.7
538591
export const BEDROCK_GLOBAL_INFERENCE_MODEL_IDS = [
539592
"anthropic.claude-sonnet-4-20250514-v1:0",
540593
"anthropic.claude-sonnet-4-5-20250929-v1:0",
541594
"anthropic.claude-sonnet-4-6",
542595
"anthropic.claude-haiku-4-5-20251001-v1:0",
543596
"anthropic.claude-opus-4-5-20251101-v1:0",
544597
"anthropic.claude-opus-4-6-v1",
598+
"anthropic.claude-opus-4-7",
599+
"anthropic.claude-opus-4-8",
545600
] as const
546601

547602
// Amazon Bedrock Service Tier types

packages/types/src/providers/vertex.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ export const vertexModels = {
384384
cacheWritesPrice: 6.25, // $6.25 per million tokens
385385
cacheReadsPrice: 0.5, // $0.50 per million tokens
386386
supportsReasoningBudget: true,
387+
supportsReasoningBinary: true,
387388
supportsTemperature: false,
388389
// Tiered pricing for extended context (requires beta flag 'context-1m-2025-08-07')
389390
tiers: [
@@ -396,6 +397,30 @@ export const vertexModels = {
396397
},
397398
],
398399
},
400+
"claude-opus-4-8": {
401+
maxTokens: 8192,
402+
contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07'
403+
supportsImages: true,
404+
supportsPromptCache: true,
405+
inputPrice: 5.0, // $5 per million input tokens (≤200K context)
406+
outputPrice: 25.0, // $25 per million output tokens (≤200K context)
407+
cacheWritesPrice: 6.25, // $6.25 per million tokens
408+
cacheReadsPrice: 0.5, // $0.50 per million tokens
409+
supportsReasoningBudget: true,
410+
supportsReasoningBinary: true,
411+
supportsTemperature: false,
412+
// 4.8 inherits the same Vertex pricing structure as 4.7 — no breaking changes.
413+
// Tiered pricing for extended context (requires beta flag 'context-1m-2025-08-07')
414+
tiers: [
415+
{
416+
contextWindow: 1_000_000, // 1M tokens with beta flag
417+
inputPrice: 10.0, // $10 per million input tokens (>200K context)
418+
outputPrice: 37.5, // $37.50 per million output tokens (>200K context)
419+
cacheWritesPrice: 12.5, // $12.50 per million tokens (>200K context)
420+
cacheReadsPrice: 1.0, // $1.00 per million tokens (>200K context)
421+
},
422+
],
423+
},
399424
"claude-opus-4-5@20251101": {
400425
maxTokens: 8192,
401426
contextWindow: 200_000,
@@ -595,6 +620,7 @@ export const VERTEX_1M_CONTEXT_MODEL_IDS = [
595620
"claude-sonnet-4-6",
596621
"claude-opus-4-6",
597622
"claude-opus-4-7",
623+
"claude-opus-4-8",
598624
] as const
599625

600626
export const VERTEX_REGIONS = [

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,22 @@ describe("VertexHandler", () => {
929929
expect(model.betas).toContain("context-1m-2025-08-07")
930930
})
931931

932+
it("should enable 1M context for Claude Opus 4.8 when beta flag is set", () => {
933+
const handler = new AnthropicVertexHandler({
934+
apiModelId: "claude-opus-4-8",
935+
vertexProjectId: "test-project",
936+
vertexRegion: "us-central1",
937+
vertex1MContext: true,
938+
})
939+
940+
const model = handler.getModel()
941+
expect(model.info.contextWindow).toBe(1_000_000)
942+
expect(model.info.inputPrice).toBe(10.0)
943+
expect(model.info.outputPrice).toBe(37.5)
944+
expect(model.info.supportsTemperature).toBe(false)
945+
expect(model.betas).toContain("context-1m-2025-08-07")
946+
})
947+
932948
it("should not enable 1M context when flag is disabled", () => {
933949
const handler = new AnthropicVertexHandler({
934950
apiModelId: VERTEX_1M_CONTEXT_MODEL_IDS[0],
@@ -1145,6 +1161,37 @@ describe("VertexHandler", () => {
11451161
undefined,
11461162
)
11471163
})
1164+
1165+
it("should use adaptive thinking for Claude Opus 4.8", async () => {
1166+
const opus48Handler = new AnthropicVertexHandler({
1167+
apiModelId: "claude-opus-4-8",
1168+
vertexProjectId: "test-project",
1169+
vertexRegion: "us-central1",
1170+
enableReasoningEffort: true,
1171+
})
1172+
1173+
const mockCreate = vitest.fn().mockImplementation(async () => ({
1174+
async *[Symbol.asyncIterator]() {
1175+
yield { type: "message_start", message: { usage: { input_tokens: 10, output_tokens: 5 } } }
1176+
},
1177+
}))
1178+
;(opus48Handler["client"].messages as any).create = mockCreate
1179+
1180+
await opus48Handler
1181+
.createMessage("You are a helpful assistant", [{ role: "user", content: "Hello" }])
1182+
.next()
1183+
1184+
expect(mockCreate).toHaveBeenCalledWith(
1185+
expect.objectContaining({
1186+
thinking: { type: "adaptive" },
1187+
}),
1188+
undefined,
1189+
)
1190+
1191+
const request = mockCreate.mock.calls[0][0]
1192+
expect(request.thinking).not.toHaveProperty("budget_tokens")
1193+
expect(request.temperature).toBeUndefined()
1194+
})
11481195
})
11491196

11501197
describe("native tool calling", () => {

0 commit comments

Comments
 (0)