fix(bedrock): use adaptive thinking + output_config.effort for Opus 4.8

The handoff spec wired Bedrock Converse thinking as
{type: enabled, budget_tokens: N}, but Opus 4.7+ rejects that with a
ValidationException: thinking.type "enabled" is not supported; it requires
thinking.type "adaptive" plus output_config.effort. Verified by live invoke
against us.anthropic.claude-opus-4-8 (account 328440206208, us-east-1):
the enabled+budget shape 400s, adaptive+effort returns normally.

Map profile effort to additional_model_request_fields:
  {thinking: {type: adaptive, display: summarized},
   output_config: {effort: <low|medium|high|xhigh|max>}}
reusing anthropic_thinking_for/anthropic_effort_for. Update the two
subagent-model tests asserting the old shape.
This commit is contained in:
Adam Moussa 2026-06-29 15:02:17 -04:00
parent 1c93e5c62b
commit 3a10830e90
No known key found for this signature in database
2 changed files with 16 additions and 9 deletions

View file

@ -184,13 +184,18 @@ def provider_model_kwargs(
if effort is not None:
kwargs["effort"] = effort
elif model_id.startswith("bedrock_converse:"):
budget = {"low": 4000, "medium": 8000, "high": 16000, "xhigh": 32000, "max": 60000}.get(
profile_effort or ""
)
if budget is not None:
kwargs["additional_model_request_fields"] = {
"thinking": {"type": "enabled", "budget_tokens": budget}
}
# Opus 4.7+ on Bedrock rejects thinking.type "enabled"/budget_tokens with a
# ValidationException; it requires adaptive thinking plus output_config.effort,
# passed through Converse's additional_model_request_fields.
fields: dict[str, object] = {}
thinking = anthropic_thinking_for(profile_effort)
if thinking is not None:
fields["thinking"] = thinking
effort = anthropic_effort_for(profile_effort)
if effort is not None:
fields["output_config"] = {"effort": effort}
if fields:
kwargs["additional_model_request_fields"] = fields
elif model_id.startswith("google_genai:") and is_gemini_3_family(model_id):
thinking_level = google_thinking_level_for(profile_effort)
if thinking_level is not None:

View file

@ -81,7 +81,8 @@ async def test_agent_uses_profile_subagent_model_override() -> None:
main_call = make_model.call_args_list[0]
assert main_call.args == ("bedrock_converse:us.anthropic.claude-opus-4-8",)
assert main_call.kwargs["additional_model_request_fields"] == {
"thinking": {"type": "enabled", "budget_tokens": 16000}
"thinking": {"type": "adaptive", "display": "summarized"},
"output_config": {"effort": "high"},
}
subagent_call = make_model.call_args_list[1]
@ -153,5 +154,6 @@ async def test_agent_subagent_inherits_profile_model_override_without_explicit_p
assert make_model.call_args_list[0].args == ("bedrock_converse:us.anthropic.claude-opus-4-8",)
assert make_model.call_args_list[1].args == ("bedrock_converse:us.anthropic.claude-opus-4-8",)
assert make_model.call_args_list[1].kwargs["additional_model_request_fields"] == {
"thinking": {"type": "enabled", "budget_tokens": 16000}
"thinking": {"type": "adaptive", "display": "summarized"},
"output_config": {"effort": "high"},
}