Skip to content

fix(bedrock): forward output_config.effort for adaptive-thinking Claude - #27040

Closed
mateo-berri wants to merge 1 commit into
litellm_internal_stagingfrom
litellm_bedrock-effort-passthrough-e8c2
Closed

fix(bedrock): forward output_config.effort for adaptive-thinking Claude#27040
mateo-berri wants to merge 1 commit into
litellm_internal_stagingfrom
litellm_bedrock-effort-passthrough-e8c2

Conversation

@mateo-berri

Copy link
Copy Markdown
Contributor

Relevant issues

Customer report on the LiteLLM Anthropic-via-Bedrock route: output_config.effort for Claude Sonnet 4.6 was being silently dropped on every Bedrock surface (Converse and Invoke, /v1/messages and /completion). Tested across v1.83.7, v1.83.14, and v1.84.0 and the parameter never appeared in the wire body.

Linear ticket

Resolves LIT-2758

Pre-Submission checklist

  • I have Added testing in the tests/test_litellm/ directory
  • My PR passes all unit tests on make test-unit (136 bedrock unit tests pass locally)
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Type

🐛 Bug Fix

Changes

Bedrock exposes Anthropic's effort parameter on three routes:

Route Wire shape
Bedrock Invoke /v1/messages (Anthropic Messages API) top-level output_config: {effort: ...}
Bedrock Invoke /completion (legacy chat invoke) top-level output_config: {effort: ...}
Bedrock Converse nested additionalModelRequestFields.thinking.effort

Adaptive-thinking models (Claude 4.6 / 4.7) accept effort natively with no beta header. Claude Opus 4.5 accepts it on Bedrock Invoke when the effort-2025-11-24 beta is attached.

LiteLLM was unconditionally stripping output_config on every Bedrock route — a defensive measure originally added because pre-4.6 Claude on Bedrock 400s on the field. That defense was correct for older models but caused effort to vanish for Sonnet 4.6 and friends.

This change splits the strip behavior by model:

Bedrock Invoke /v1/messages (AmazonAnthropicClaudeMessagesConfig)

  • Add a model-aware _supports_effort_on_bedrock check (Claude 4.6/4.7 or Opus 4.5).
  • For supported models, leave output_config intact; for everyone else, strip it as before.
  • Add output_config to the BedrockInvokeAnthropicMessagesRequest TypedDict so the body allowlist preserves it on the supported path. Existing allowlist tests use Haiku and stay green.

Bedrock Invoke /completion (AmazonAnthropicClaudeConfig)

  • Mirror the same model-aware strip in _build_bedrock_anthropic_request_base.

Bedrock Converse (AmazonConverseConfig)

  • Add _fold_output_config_effort_into_thinking: if the caller supplied output_config.effort on an adaptive-thinking model, fold it into thinking.effort (creating thinking: {type: adaptive, effort: ...} if absent, never overriding an explicit caller value). output_config is then stripped before signing.
  • Update _handle_reasoning_effort_parameter so that on adaptive-thinking models, OpenAI reasoning_effort populates thinking.effort (with type=adaptive) instead of the legacy thinking.type=enabled, budget_tokens=... shape.

Beta-headers config

  • Map effort-2025-11-24 to itself for bedrock (was null), so the auto-attach in is_effort_used (already triggered by output_config.effort on Opus 4.5) is preserved instead of dropped.

Tests

  • test_bedrock_messages_preserves_output_config_for_adaptive_thinking_models — Sonnet 4.6, Opus 4.6, Opus 4.7 (regional + global) all forward output_config, no beta header.
  • test_bedrock_messages_preserves_output_config_for_opus_4_5_with_beta — Opus 4.5 forwards output_config and auto-attaches the effort-2025-11-24 beta.
  • test_output_config_preserved_for_adaptive_thinking_on_bedrock_invoke and test_output_config_preserved_for_opus_4_5_on_bedrock_invoke — same coverage on /completion.
  • test_converse_folds_output_config_effort_into_thinking_for_46, test_converse_folds_output_config_effort_preserves_existing_thinking, test_converse_reasoning_effort_sets_thinking_effort_for_46 — Converse fold path with explicit-thinking precedence.

Existing strip-output_config tests (Haiku, Sonnet 4, Nova) all stay green and still assert removal — the model-aware split keeps behavior unchanged for unsupported models.

Note on the documentation question

The reporter also flagged that on the OpenAI-compatible route, reasoning_effort maps to thinking: {type: "adaptive"} for Claude 4.6+, which was surprising. That mapping is correct (and documented in litellm/llms/anthropic/chat/transformation.py::_map_reasoning_effort and common_utils.py::_is_adaptive_thinking_model) — Anthropic's adaptive thinking is the recommended replacement for budget_tokens on those models. Documentation lives in the separate BerriAI/litellm-docs repo and is not modified here, but the docstring on _map_reasoning_effort already explains the behavior.

Slack Thread

Open in Web Open in Cursor 

Claude Sonnet 4.6 / Opus 4.6 / Opus 4.7 expose Anthropic's effort parameter
through 'output_config.effort' (Messages API and Bedrock Invoke) and through
'additionalModelRequestFields.thinking.effort' (Bedrock Converse). LiteLLM
was stripping output_config on every Bedrock route, so Sonnet 4.6 callers
saw the effort never reach the wire body.

This fix:
- Bedrock Invoke /v1/messages: preserve output_config for adaptive-thinking
  models (Claude 4.6/4.7) and for Opus 4.5 with the effort-2025-11-24 beta
  header (auto-attached via the existing is_effort_used path).
- Bedrock Invoke /completion: same — stop stripping output_config for the
  supported model set.
- Bedrock Converse: fold output_config.effort into thinking.effort and
  switch reasoning_effort to populate thinking.effort on adaptive-thinking
  models. output_config itself is still removed before the request signs,
  matching Bedrock's wire shape.
- Map effort-2025-11-24 to itself for 'bedrock' in the beta-headers JSON
  so the auto-attach for Opus 4.5 isn't dropped on the way out.
- Add adaptive-thinking and Opus-4.5-with-beta tests on all three Bedrock
  routes; haiku/sonnet-4 stays in the strip path.
@codecov

codecov Bot commented May 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.93939% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...tellm/llms/bedrock/chat/converse_transformation.py 90.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@mateo-berri

Copy link
Copy Markdown
Contributor Author

Superceded by #27074

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant