feat: map reasoning_effort=xhigh/max to budget_tokens on pre-4.6 Anthropic models - #27051
feat: map reasoning_effort=xhigh/max to budget_tokens on pre-4.6 Anthropic models#27051mateo-berri wants to merge 1 commit into
Conversation
…ropic models
Adds DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET=8192 and
DEFAULT_REASONING_EFFORT_MAX_THINKING_BUDGET=16384 (env-overridable) and
extends AnthropicConfig._map_reasoning_effort so reasoning_effort='xhigh'
and 'max' produce thinking={type:'enabled', budget_tokens:N} on pre-4.6
models. 4.6/4.7 behavior is unchanged: those still short-circuit to
adaptive thinking and have output_config.effort applied separately by
_apply_output_config (with per-model gating).
Manual QATested live on a proxy built from this branch ( 4.5 path: budget_tokens for
|
| model / route | effort | HTTP | wire body |
|---|---|---|---|
Anthropic claude-haiku-4-5 |
xhigh |
200 | thinking={type:"enabled", budget_tokens: 8192} |
Anthropic claude-haiku-4-5 |
max |
200 | thinking={type:"enabled", budget_tokens: 16384} |
Bedrock Converse sonnet-4-5 |
xhigh |
200 | additionalModelRequestFields.thinking={type:"enabled", budget_tokens: 8192} |
Bedrock Converse sonnet-4-5 |
max |
200 | additionalModelRequestFields.thinking={type:"enabled", budget_tokens: 16384} |
Bedrock Invoke chat opus-4-5 |
xhigh |
200 | thinking={type:"enabled", budget_tokens: 8192}, anthropic_version: "bedrock-2023-05-31" |
Bedrock Invoke chat opus-4-5 |
max |
200 | thinking={type:"enabled", budget_tokens: 16384}, anthropic_version: "bedrock-2023-05-31" |
Vertex haiku-4-5 |
xhigh |
200 | thinking={type:"enabled", budget_tokens: 8192}, anthropic_version: "vertex-2023-10-16" |
Vertex haiku-4-5 |
max |
200 | thinking={type:"enabled", budget_tokens: 16384}, anthropic_version: "vertex-2023-10-16" |
Before this PR these all 500'd with litellm.APIConnectionError: Unmapped reasoning effort: xhigh / ... max.
4.6/4.7 path: unchanged (regression check)
| model | effort | HTTP | wire body |
|---|---|---|---|
Anthropic opus-4-7 |
xhigh |
200 | thinking={type:"adaptive"}, output_config={effort:"xhigh"} |
Anthropic opus-4-7 |
max |
200 | thinking={type:"adaptive"}, output_config={effort:"max"} |
Anthropic opus-4-6 |
xhigh |
500 ValueError (gated, opus-4-6 doesn't support xhigh) |
— |
Anthropic opus-4-6 |
max |
200 | thinking={type:"adaptive"}, output_config={effort:"max"} |
Anthropic sonnet-4-6 |
xhigh |
500 ValueError (gated) | — |
Anthropic sonnet-4-6 |
max |
500 ValueError (gated) | — |
Per-model gating in _apply_output_config (anthropic/chat/transformation.py:1538+) is unchanged: xhigh is data-driven via supports_xhigh_reasoning_effort in model_prices_and_context_window.json; max accepts opus-4-6/4-7 patterns + supports_max_reasoning_effort.
Other regression checks
| effort | model | HTTP | notes |
|---|---|---|---|
low |
claude-haiku-4-5 |
200 | budget_tokens: 1024 (unchanged) |
medium |
claude-haiku-4-5 |
200 | budget_tokens: 2048 (unchanged) |
high |
claude-haiku-4-5 |
200 | budget_tokens: 4096 (unchanged) |
none |
claude-haiku-4-5 |
200 | no thinking (PR #27039 fix path unchanged) |
none |
claude-opus-4-7 |
200 | no thinking / no output_config |
Unit tests
tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py::test_reasoning_effort_maps_to_budget_thinking_for_non_opus_4_6 PASSED
142 passed in 0.87s # full anthropic chat transformation suite
229 passed in 6.91s # full bedrock chat suite (Converse calls AnthropicConfig._map_reasoning_effort directly)
Note on minimal (out of scope for this PR)
minimal still maps to budget_tokens=128, which is below Anthropic's published minimum of 1024 — so reasoning_effort=minimal continues to 400 on Anthropic / Azure / Vertex / Bedrock Invoke (with exact error "thinking.enabled.budget_tokens: Input should be greater than or equal to 1024"); only Bedrock Converse silently clamps to 1024 in converse_transformation.py:457. This PR intentionally preserves the existing minimal=128 value to keep the diff minimal; bumping to ≥1024 is a separate decision tracked in PR #27039's QA comment.
Greptile SummaryThis PR extends
Confidence Score: 3/5Safe to merge only if callers ensure max_tokens exceeds the thinking budget; without that guard the new effort levels will fail at the provider layer. One P1 issue: the new effort mappings produce a thinking budget larger than the output limit of all pre-4.6 models in the default configuration, causing systematic provider rejections. litellm/llms/anthropic/chat/transformation.py — the new effort branches need a guard or documentation around the budget vs output token relationship
|
| Filename | Overview |
|---|---|
| litellm/constants.py | Adds two env-overridable constants for xhigh and max thinking budgets; clean and consistent with existing pattern. |
| litellm/llms/anthropic/chat/transformation.py | Adds xhigh and max branches to _map_reasoning_effort for pre-4.6 models; logic is correct and the adaptive path for 4.6+ models is unaffected. |
| tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py | Extends existing test parametrization with xhigh/max cases; mock-only, consistent with existing test style. |
Reviews (1): Last reviewed commit: "feat: map reasoning_effort=xhigh/max to ..." | Re-trigger Greptile
| elif reasoning_effort == "xhigh": | ||
| return AnthropicThinkingParam( | ||
| type="enabled", | ||
| budget_tokens=DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET, | ||
| ) | ||
| elif reasoning_effort == "max": | ||
| return AnthropicThinkingParam( | ||
| type="enabled", | ||
| budget_tokens=DEFAULT_REASONING_EFFORT_MAX_THINKING_BUDGET, | ||
| ) |
There was a problem hiding this comment.
Anthropic requires
budget_tokens to be strictly less than max_tokens. Most pre-4.6 models have a max output of 4096–8192 tokens. With xhigh=8192, the budget equals the output limit for claude-3-5-sonnet models, and with max=16384 it exceeds the limit for virtually every pre-4.6 model. Without a compensating increase to max_tokens, these requests will fail at the API layer. Consider adding a guard that bumps max_tokens to at least budget_tokens + 1 when the user has not explicitly set it.
| elif reasoning_effort == "xhigh": | |
| return AnthropicThinkingParam( | |
| type="enabled", | |
| budget_tokens=DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET, | |
| ) | |
| elif reasoning_effort == "max": | |
| return AnthropicThinkingParam( | |
| type="enabled", | |
| budget_tokens=DEFAULT_REASONING_EFFORT_MAX_THINKING_BUDGET, | |
| ) | |
| elif reasoning_effort == "xhigh": | |
| return AnthropicThinkingParam( | |
| type="enabled", | |
| budget_tokens=DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET, | |
| ) | |
| elif reasoning_effort == "max": | |
| return AnthropicThinkingParam( | |
| type="enabled", | |
| budget_tokens=DEFAULT_REASONING_EFFORT_MAX_THINKING_BUDGET, | |
| ) | |
| # NOTE: callers should ensure max_tokens > budget_tokens; Anthropic rejects | |
| # requests where budget_tokens >= max_tokens. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
Superceded by #27074 |
Linear: LIT-2758 (follow-up)
What this PR does
Extends
AnthropicConfig._map_reasoning_effortsoreasoning_effort='xhigh'andreasoning_effort='max'producethinking={type:'enabled', budget_tokens:N}on pre-4.6 Anthropic models, instead of raisingValueError("Unmapped reasoning effort: ...")and surfacing as a 500APIConnectionError.Adds two env-overridable defaults to
litellm/constants.py:minimallowmediumhighxhighmax4.6/4.7 behavior is unchanged: those models still short-circuit to
thinking={type:'adaptive'}at the top of_map_reasoning_effort, and_apply_output_configseparately appliesoutput_config.effort=<value>with the existing per-model gating (opus-4-7 supportsxhigh+max; opus-4-6 supportsmaxonly; sonnet-4-6 supports neither).Why
Surfaced during the QA sweep on #27039 — every pre-4.6 model 500'd on
reasoning_effort=xhigh|maxbecause_map_reasoning_efforthad no branch for them. After this PR, the 4.5 path emits a validthinkingshape and providers accept it (manually verified — see Manual QA section below).Checklist
tests/litellm/make test-unitpasses (anthropic + bedrock chat suites)uv run black .formatteduv run ruff check .cleannone(PR fix(anthropic,bedrock): omit thinking/output_config when reasoning_effort="none" #27039 fix path unchanged)