mirror of
https://github.com/coder/coder.git
synced 2026-11-10 18:13:50 +08:00
*Disclaimer: implemented by a Coder Agent using Claude Opus 4.6/4.7* Fixes [coder/aibridge#280](https://github.com/coder/aibridge/issues/280). Claude Opus 4.7 (and future adaptive-only Bedrock models) reject the legacy `thinking.type: "enabled"` + `budget_tokens` shape with a 400. Claude Code falls back to that shape when it cannot read the upstream model's capability metadata, which is exactly the case when AI Bridge sits between the client and Bedrock. Pinning back to Opus 4.6 is the only operator workaround today. This is the counterpart to the `adaptive -> enabled` conversion added in [coder/aibridge#225](https://github.com/coder/aibridge/pull/225) for older Bedrock models. ## Behavior - New `bedrockModelRequiresAdaptiveThinking()` helper matches Opus 4.7 (covers `us.anthropic.claude-opus-4-7`, ARN-style application inference profile names that include the model ID, etc.). - New `RequestPayload.convertEnabledThinkingForBedrock()` rewrites `thinking: {type: enabled, budget_tokens: N}` to `thinking: {type: adaptive}`. The budget hint is dropped; an explicit `output_config.effort` from the caller is preserved naturally because we never touch that field. We deliberately do **not** derive an effort label from the budget (see decision log). - `removeUnsupportedBedrockFields` learns a variadic `exemptFields` parameter. Adaptive-only models support `output_config` natively (no beta flag required), so `augmentRequestForBedrock` exempts that field for those models. - Bedrock Opus 4.7 accepts `output_config.effort` but rejects `output_config.format` (structured outputs) with the same "Extra inputs are not permitted" 400. The generic strip pass operates at top-level granularity only, so a small targeted pass drops `output_config.format` after the top-level strip for adaptive-only models. The whole Bedrock thinking-type shim block carries a header comment flagging it as temporary; a planned native Bedrock provider removes the impedance mismatch and lets us delete it. ## Out of scope The issue calls out a possible follow-up around `Anthropic-Beta: interleaved-thinking-2025-05-14` for adaptive-only models; best evidence is that Opus 4.7 still accepts those flags, so this PR is a no-op there. <details> <summary>Decision log</summary> - `bedrockModelSupportsAdaptiveThinking` now also returns `true` for adaptive-only models. That keeps the existing `convertAdaptiveThinkingForBedrock` branch from running on Opus 4.7 (which would otherwise be incorrect; `adaptive` is the supported native type there), and the new `convertEnabledThinkingForBedrock` runs only for adaptive-only models via the explicit `bedrockModelRequiresAdaptiveThinking` switch case. The two model sets are disjoint by construction. - The reverse conversion does **not** derive `output_config.effort` from `budget_tokens / max_tokens`. The two thinking shapes encode different intents (`enabled+budget` is "give me exactly N tokens," `adaptive[+effort]` is "model, pick a budget, optionally biased") and there is no canonical mapping between them. An earlier draft of this PR derived effort via midpoints of an invented anchor table; it was symmetric-looking but lossy and required a lot of scaffolding (sorted anchors, init-time invariant guard, round-trip tests) to keep two halves consistent. The reverse direction now just rewrites the shape, which is honest about the information loss and matches platform-defined adaptive behavior when no effort hint is present. - `output_config.format` is stripped only for adaptive-only models. Other Bedrock models either don't get `output_config` through at all (top-level strip handles them) or accept it via a beta flag that may imply broader feature support. Easy to widen if the same 400 shows up elsewhere. - I chose `variadic exemptFields ...string` over passing the model down to `removeUnsupportedBedrockFields`, to keep that function focused on stripping and to localise the model-aware policy in `augmentRequestForBedrock`. </details>