fix(aibridge/intercept/messages): treat Bedrock Opus 5, Haiku 5.5, Fable 5, and Mythos 5 as adaptive-only (#30692)

Bedrock rejects `thinking.type: "enabled"` with `budget_tokens` for
Claude Opus 5, Haiku 5.5, Fable 5, and Mythos 5 ([AWS adaptive thinking
docs](https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-adaptive-thinking.html)).
AI Gateway didn't recognize these models as adaptive-only, so it rewrote
adaptive thinking to the `enabled` shape and stripped
`output_config.effort`. That broke reasoning effort for all of them.

`bedrockModelRequiresAdaptiveThinking` now matches
`anthropic.claude-opus-5` (which also covers Opus 5.5, the same way
`anthropic.claude-sonnet-5` covers Sonnet 5.5),
`anthropic.claude-haiku-5-5`, `anthropic.claude-fable-5`, and
`anthropic.claude-mythos-5` (the last two also cover their 5.1
releases). Mythos Preview accepts both thinking modes, so it isn't
matched.

Each model group has two commits: the first adds test cases that pin the
current broken behavior, and the second applies the fix and flips the
test expectations, so the behavior change shows up in the test diff.

Fixes AIGOV-704

<details>
<summary>Decision log</summary>

- This is the same class of bug as #25335 (Opus 4.7), #26691 (Opus 4.8),
#27339 (Sonnet 5) and #29762 (Opus 5.5). Each time, a new model was
missing from the substring allowlist. This PR follows the same minimal
pattern. Moving capability detection into the model catalog is left as a
follow-up.
- Chatd's `anthropicReasoningEffort` maps effort `none` to `low` only
for Opus 5.5 and Sonnet 5.5, and is intentionally unchanged. Per the
Anthropic and AWS docs, Opus 5 and Haiku 5.5 still accept `thinking:
{type: "disabled"}` at effort `high` or below, so sending `none` (which
becomes `disabled`) is valid for them.
- Adaptive-only models also get `output_config.format` stripped. That
matches the docs: structured outputs aren't available for Haiku 5.5 on
Bedrock.

</details>

> [!NOTE]
> Generated by Coder Agents on behalf of @dannykopping.
This commit is contained in:
Danny Kopping
2026-10-14 12:25:30 +02:00
committed by GitHub
parent 40c0adc7ac
commit e6d24ff30a
2 changed files with 64 additions and 3 deletions
+8 -3
View File
@@ -724,14 +724,19 @@ func bedrockModelSupportsAdaptiveThinking(model string) bool {
// bedrockModelRequiresAdaptiveThinking returns true if the given Bedrock model
// ID only supports the "adaptive" thinking type and rejects the legacy
// "enabled" + budget_tokens shape with a 400.
// "enabled" + budget_tokens shape with a 400. The Opus 5, Sonnet 5, Fable 5,
// and Mythos 5 prefixes also match their point releases.
//
// See https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-opus-4-7.html
// and https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-adaptive-thinking.html
func bedrockModelRequiresAdaptiveThinking(model string) bool {
return strings.Contains(model, "anthropic.claude-opus-4-7") ||
strings.Contains(model, "anthropic.claude-opus-4-8") ||
strings.Contains(model, "anthropic.claude-opus-5-5") ||
strings.Contains(model, "anthropic.claude-sonnet-5")
strings.Contains(model, "anthropic.claude-opus-5") ||
strings.Contains(model, "anthropic.claude-sonnet-5") ||
strings.Contains(model, "anthropic.claude-haiku-5-5") ||
strings.Contains(model, "anthropic.claude-fable-5") ||
strings.Contains(model, "anthropic.claude-mythos-5")
}
// filterBedrockBetaFlags removes unsupported beta flags from the Anthropic-Beta
@@ -970,6 +970,62 @@ func TestAugmentRequestForBedrock_AdaptiveThinking(t *testing.T) {
expectEffort: "low",
expectKeptFields: []string{"output_config"},
},
{
name: "opus_5_model_with_enabled_thinking_is_converted_to_adaptive_and_drops_budget",
bedrockModel: "anthropic.claude-opus-5",
requestBody: `{"max_tokens":10000,"thinking":{"type":"enabled","budget_tokens":5000}}`,
expectThinkingType: "adaptive",
},
{
name: "global_opus_5_model_keeps_adaptive_thinking_and_output_config_effort",
bedrockModel: "global.anthropic.claude-opus-5",
requestBody: `{"max_tokens":10000,"thinking":{"type":"adaptive"},"output_config":{"effort":"medium"}}`,
expectThinkingType: "adaptive",
expectEffort: "medium",
expectKeptFields: []string{"output_config"},
},
{
name: "haiku_5_5_model_with_enabled_thinking_is_converted_to_adaptive_and_drops_budget",
bedrockModel: "anthropic.claude-haiku-5-5",
requestBody: `{"max_tokens":10000,"thinking":{"type":"enabled","budget_tokens":5000}}`,
expectThinkingType: "adaptive",
},
{
name: "regional_haiku_5_5_model_keeps_adaptive_thinking_and_output_config_effort",
bedrockModel: "us.anthropic.claude-haiku-5-5",
requestBody: `{"max_tokens":10000,"thinking":{"type":"adaptive"},"output_config":{"effort":"low"}}`,
expectThinkingType: "adaptive",
expectEffort: "low",
expectKeptFields: []string{"output_config"},
},
{
name: "fable_5_model_with_enabled_thinking_is_converted_to_adaptive_and_drops_budget",
bedrockModel: "anthropic.claude-fable-5",
requestBody: `{"max_tokens":10000,"thinking":{"type":"enabled","budget_tokens":5000}}`,
expectThinkingType: "adaptive",
},
{
name: "global_fable_5_1_model_keeps_adaptive_thinking_and_output_config_effort",
bedrockModel: "global.anthropic.claude-fable-5-1",
requestBody: `{"max_tokens":10000,"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`,
expectThinkingType: "adaptive",
expectEffort: "high",
expectKeptFields: []string{"output_config"},
},
{
name: "mythos_5_model_with_enabled_thinking_is_converted_to_adaptive_and_drops_budget",
bedrockModel: "anthropic.claude-mythos-5",
requestBody: `{"max_tokens":10000,"thinking":{"type":"enabled","budget_tokens":5000}}`,
expectThinkingType: "adaptive",
},
{
name: "global_mythos_5_1_model_keeps_adaptive_thinking_and_output_config_effort",
bedrockModel: "global.anthropic.claude-mythos-5-1",
requestBody: `{"max_tokens":10000,"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`,
expectThinkingType: "adaptive",
expectEffort: "high",
expectKeptFields: []string{"output_config"},
},
{
// Opus 4.7 on Bedrock rejects output_config.format (structured
// outputs) with a 400 even though it accepts output_config.effort.