Normalize Anthropic messages xhigh output_config

This commit is contained in:
Karolis Sukys 2026-05-15 11:31:44 +02:00 • committed by ksukys
parent bcbae93ecc
commit aa0088743f
2 changed files with 91 additions and 16 deletions

View file

@ -212,6 +212,11 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig):
),
status_code=400,
)
mapped_effort = (
AnthropicMessagesConfig._normalize_xhigh_effort_for_max_model(
model, mapped_effort
)
)
gate_error = AnthropicConfig._validate_effort_for_model(
model, mapped_effort
)
@ -223,6 +228,35 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig):
existing_output_config.setdefault("effort", mapped_effort)
optional_params["output_config"] = existing_output_config
@staticmethod
def _normalize_xhigh_effort_for_max_model(model: str, effort: str) -> str:
"""Return ``max`` when a model supports max effort but not xhigh."""
from litellm.llms.anthropic.chat.transformation import AnthropicConfig
if effort != "xhigh":
return effort
if AnthropicConfig._supports_effort_level(model, "xhigh"):
return effort
if AnthropicConfig._supports_effort_level(model, "max"):
return "max"
return effort
@staticmethod
def _normalize_explicit_output_config_xhigh_effort(
model: str, optional_params: Dict
) -> None:
"""Normalize explicit ``output_config.effort=xhigh`` when ``max`` is supported."""
output_config = optional_params.get("output_config")
if not isinstance(output_config, dict):
return
effort = output_config.get("effort")
if not isinstance(effort, str):
return
output_config["effort"] = (
AnthropicMessagesConfig._normalize_xhigh_effort_for_max_model(model, effort)
)
@staticmethod
def _translate_legacy_thinking_for_adaptive_model(
model: str, optional_params: Dict
@ -274,6 +308,11 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig):
status_code=400,
)
self._normalize_explicit_output_config_xhigh_effort(
model=model,
optional_params=anthropic_messages_optional_request_params,
)
self._translate_reasoning_effort_to_anthropic(
model=model,
optional_params=anthropic_messages_optional_request_params,

View file

@ -99,28 +99,27 @@ def test_invalid_reasoning_effort_raises_400(bad_effort):
@pytest.mark.parametrize(
"model,bad_effort",
"model",
[
("claude-opus-4-6", "xhigh"),
("bedrock/invoke/us.anthropic.claude-opus-4-6-v1", "xhigh"),
("claude-sonnet-4-6", "xhigh"),
"claude-opus-4-6",
"bedrock/invoke/us.anthropic.claude-opus-4-6-v1",
"claude-sonnet-4-6",
],
)
def test_reasoning_effort_unsupported_tier_raises_400_messages(model, bad_effort):
def test_reasoning_effort_xhigh_normalizes_to_max_messages(model):
config = AnthropicMessagesConfig()
optional_params = {"max_tokens": 1024, "reasoning_effort": bad_effort}
optional_params = {"max_tokens": 1024, "reasoning_effort": "xhigh"}
with pytest.raises(AnthropicError) as exc_info:
config.transform_anthropic_messages_request(
model=model,
messages=[{"role": "user", "content": "Hello"}],
anthropic_messages_optional_request_params=optional_params,
litellm_params={},
headers={},
)
result = config.transform_anthropic_messages_request(
model=model,
messages=[{"role": "user", "content": "Hello"}],
anthropic_messages_optional_request_params=optional_params,
litellm_params={},
headers={},
)
assert exc_info.value.status_code == 400
assert "not supported by this model" in str(exc_info.value)
output_config = result.get("output_config")
assert isinstance(output_config, dict) and output_config.get("effort") == "max"
@pytest.mark.parametrize(
@ -146,6 +145,43 @@ def test_reasoning_effort_max_accepted_on_sonnet_46_messages(model):
assert isinstance(output_config, dict) and output_config.get("effort") == "max"
@pytest.mark.parametrize(
"model",
[
"claude-sonnet-4-6",
"bedrock/invoke/us.anthropic.claude-sonnet-4-6",
],
)
def test_explicit_output_config_xhigh_normalizes_to_max_messages(model):
config = AnthropicMessagesConfig()
optional_params = {"max_tokens": 1024, "output_config": {"effort": "xhigh"}}
result = config.transform_anthropic_messages_request(
model=model,
messages=[{"role": "user", "content": "Hello"}],
anthropic_messages_optional_request_params=optional_params,
litellm_params={},
headers={},
)
assert result.get("output_config") == {"effort": "max"}
def test_explicit_output_config_xhigh_preserved_when_supported_messages():
config = AnthropicMessagesConfig()
optional_params = {"max_tokens": 1024, "output_config": {"effort": "xhigh"}}
result = config.transform_anthropic_messages_request(
model="claude-opus-4-7",
messages=[{"role": "user", "content": "Hello"}],
anthropic_messages_optional_request_params=optional_params,
litellm_params={},
headers={},
)
assert result.get("output_config") == {"effort": "xhigh"}
def test_explicit_output_config_wins_over_reasoning_effort():
config = AnthropicMessagesConfig()
optional_params = {