Merge pull request #27039 from BerriAI/litellm_fix_reasoning_effort_none_anthropic
Some checks are pending
Unit Tests: Caching (Redis) / caching-redis (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / schema-migration (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests: Security / security (push) Waiting to run

fix(anthropic,bedrock): omit thinking/output_config when reasoning_effort="none"
This commit is contained in:
Mateo Wang 2026-05-02 01:42:50 -07:00 • committed by GitHub
commit c94a8d6514
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 74 additions and 23 deletions

View file

@ -1088,24 +1088,29 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
elif param == "thinking":
optional_params["thinking"] = value
elif param == "reasoning_effort" and isinstance(value, str):
optional_params["thinking"] = AnthropicConfig._map_reasoning_effort(
mapped_thinking = AnthropicConfig._map_reasoning_effort(
reasoning_effort=value, model=model
)
# For Claude 4.6+ models, effort is controlled via output_config,
# not thinking budget_tokens. Map reasoning_effort to output_config.
if AnthropicConfig._is_claude_4_6_model(
model
) or AnthropicConfig._is_claude_4_7_model(model):
effort_map = {
"low": "low",
"minimal": "low",
"medium": "medium",
"high": "high",
"xhigh": "xhigh",
"max": "max",
}
mapped_effort = effort_map.get(value, value)
optional_params["output_config"] = {"effort": mapped_effort}
if mapped_thinking is None:
optional_params.pop("thinking", None)
optional_params.pop("output_config", None)
else:
optional_params["thinking"] = mapped_thinking
# For Claude 4.6+ models, effort is controlled via output_config,
# not thinking budget_tokens. Map reasoning_effort to output_config.
if AnthropicConfig._is_claude_4_6_model(
model
) or AnthropicConfig._is_claude_4_7_model(model):
effort_map = {
"low": "low",
"minimal": "low",
"medium": "medium",
"high": "high",
"xhigh": "xhigh",
"max": "max",
}
mapped_effort = effort_map.get(value, value)
optional_params["output_config"] = {"effort": mapped_effort}
elif param == "web_search_options" and isinstance(value, dict):
hosted_web_search_tool = self.map_web_search_tool(
cast(OpenAIWebSearchOptions, value)

View file

@ -449,9 +449,13 @@ class AmazonConverseConfig(BaseConfig):
optional_params.update(reasoning_config)
else:
# Anthropic and other models: convert to thinking parameter
optional_params["thinking"] = AnthropicConfig._map_reasoning_effort(
mapped_thinking = AnthropicConfig._map_reasoning_effort(
reasoning_effort=reasoning_effort, model=model
)
if mapped_thinking is None:
optional_params.pop("thinking", None)
else:
optional_params["thinking"] = mapped_thinking
@staticmethod
def _clamp_thinking_budget_tokens(optional_params: dict) -> None:

View file

@ -1687,9 +1687,7 @@ def test_max_effort_rejected_for_opus_45():
messages = [{"role": "user", "content": "Test"}]
with pytest.raises(
ValueError, match="effort='max' is not supported by this model"
):
with pytest.raises(ValueError, match="effort='max' is not supported by this model"):
optional_params = {"output_config": {"effort": "max"}}
config.transform_request(
model="claude-opus-4-5-20251101",
@ -2251,9 +2249,7 @@ def test_max_effort_rejected_for_sonnet_46():
config = AnthropicConfig()
messages = [{"role": "user", "content": "Test"}]
with pytest.raises(
ValueError, match="effort='max' is not supported by this model"
):
with pytest.raises(ValueError, match="effort='max' is not supported by this model"):
config.transform_request(
model="claude-sonnet-4-6-20260219",
messages=messages,
@ -2315,6 +2311,30 @@ def test_effort_beta_header_not_injected_for_46_models():
assert result is False, f"is_effort_used should return False for {model}"
@pytest.mark.parametrize(
"model",
[
"claude-opus-4-5-20251101",
"claude-opus-4-6-20250514",
"claude-sonnet-4-6-20260219",
"claude-opus-4-7",
],
)
def test_reasoning_effort_none_omits_thinking_and_output_config(model):
"""reasoning_effort="none" must omit thinking and output_config from the request."""
config = AnthropicConfig()
result = config.map_openai_params(
non_default_params={"reasoning_effort": "none"},
optional_params={},
model=model,
drop_params=False,
)
assert "thinking" not in result
assert "output_config" not in result
def test_effort_beta_header_still_injected_for_older_models():
"""
Test that is_effort_used still returns True for pre-4.6 models

View file

@ -288,6 +288,28 @@ def test_reasoning_with_forced_tool_choice_switches_to_auto():
assert optional_params["tool_choice"] == {"auto": {}}
@pytest.mark.parametrize(
"model",
[
"bedrock/converse/us.anthropic.claude-opus-4-5-20251101-v1:0",
"bedrock/converse/us.anthropic.claude-opus-4-6-v1",
"bedrock/converse/us.anthropic.claude-opus-4-7",
],
)
def test_reasoning_effort_none_omits_thinking_for_anthropic_converse(model):
"""reasoning_effort="none" must omit thinking from the Bedrock Converse request."""
config = AmazonConverseConfig()
optional_params = config.map_openai_params(
non_default_params={"reasoning_effort": "none"},
optional_params={},
model=model,
drop_params=False,
)
assert "thinking" not in optional_params
def test_get_supported_openai_params():
config = AmazonConverseConfig()
supported_params = config.get_supported_openai_params(