fix: support converse and 4.7

This commit is contained in:
mateo-berri 2026-04-22 20:14:36 -07:00
parent 0236b62988
commit 2d9d2f19c9
4 changed files with 121 additions and 29 deletions

View file

@ -59,6 +59,7 @@ from litellm.types.utils import (
Usage,
)
from litellm.utils import (
_supports_factory,
add_dummy_tool,
any_assistant_message_has_thinking_blocks,
has_tool_call_blocks,
@ -1192,9 +1193,6 @@ class AmazonConverseConfig(BaseConfig):
+ supported_config_params
)
inference_params.pop("json_mode", None) # used for handling json_schema
# Anthropic-only key. Bedrock expects `outputConfig` (camelCase) and
# will reject `output_config` if it leaks through pass-through routes.
inference_params.pop("output_config", None)
# Extract requestMetadata before processing other parameters
request_metadata = inference_params.pop("requestMetadata", None)
@ -1204,9 +1202,10 @@ class AmazonConverseConfig(BaseConfig):
output_config: Optional[OutputConfigBlock] = inference_params.pop(
"outputConfig", None
)
inference_params.pop(
"output_config", None
) # Bedrock Converse doesn't support it
if not _supports_factory(
model=model, custom_llm_provider=None, key="supports_output_config"
):
inference_params.pop("output_config", None)
# keep supported params in 'inference_params', and set all model-specific params in 'additional_request_params'
additional_request_params = {

View file

@ -1151,7 +1151,8 @@
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_output_config": true
},
"global.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 6.25e-06,
@ -1179,7 +1180,8 @@
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_output_config": true
},
"us.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 6.875e-06,
@ -1207,7 +1209,8 @@
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_output_config": true
},
"eu.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 6.875e-06,
@ -1235,7 +1238,8 @@
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_output_config": true
},
"au.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 6.875e-06,
@ -1263,7 +1267,8 @@
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_output_config": true
},
"anthropic.claude-sonnet-4-6": {
"cache_creation_input_token_cost": 3.75e-06,
@ -1950,7 +1955,8 @@
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 159
"tool_use_system_prompt_tokens": 159,
"supports_output_config": true
},
"azure_ai/claude-opus-4-1": {
"cache_creation_input_token_cost": 1.875e-05,
@ -9182,7 +9188,8 @@
"provider_specific_entry": {
"us": 1.1,
"fast": 6.0
}
},
"supports_output_config": true
},
"claude-opus-4-7-20260416": {
"cache_creation_input_token_cost": 6.25e-06,
@ -9214,7 +9221,8 @@
"provider_specific_entry": {
"us": 1.1,
"fast": 6.0
}
},
"supports_output_config": true
},
"claude-sonnet-4-20250514": {
"deprecation_date": "2026-05-14",
@ -27026,7 +27034,8 @@
"mode": "responses",
"supports_web_search": true,
"supports_reasoning": false,
"supports_function_calling": true
"supports_function_calling": true,
"supports_output_config": true
},
"perplexity/anthropic/claude-opus-4-5": {
"litellm_provider": "perplexity",
@ -31447,7 +31456,8 @@
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346
"tool_use_system_prompt_tokens": 346,
"supports_output_config": true
},
"vertex_ai/claude-opus-4-7@default": {
"cache_creation_input_token_cost": 6.25e-06,
@ -31474,7 +31484,8 @@
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346
"tool_use_system_prompt_tokens": 346,
"supports_output_config": true
},
"vertex_ai/claude-sonnet-4-5": {
"cache_creation_input_token_cost": 3.75e-06,

View file

@ -1151,7 +1151,8 @@
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_output_config": true
},
"global.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 6.25e-06,
@ -1179,7 +1180,8 @@
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_output_config": true
},
"us.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 6.875e-06,
@ -1207,7 +1209,8 @@
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_output_config": true
},
"eu.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 6.875e-06,
@ -1235,7 +1238,8 @@
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_output_config": true
},
"au.anthropic.claude-opus-4-7": {
"cache_creation_input_token_cost": 6.875e-06,
@ -1263,7 +1267,8 @@
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346,
"supports_native_structured_output": true
"supports_native_structured_output": true,
"supports_output_config": true
},
"anthropic.claude-sonnet-4-6": {
"cache_creation_input_token_cost": 3.75e-06,
@ -1950,7 +1955,8 @@
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 159
"tool_use_system_prompt_tokens": 159,
"supports_output_config": true
},
"azure_ai/claude-opus-4-1": {
"cache_creation_input_token_cost": 1.875e-05,
@ -9182,7 +9188,8 @@
"provider_specific_entry": {
"us": 1.1,
"fast": 6.0
}
},
"supports_output_config": true
},
"claude-opus-4-7-20260416": {
"cache_creation_input_token_cost": 6.25e-06,
@ -9214,7 +9221,8 @@
"provider_specific_entry": {
"us": 1.1,
"fast": 6.0
}
},
"supports_output_config": true
},
"claude-sonnet-4-20250514": {
"deprecation_date": "2026-05-14",
@ -27026,7 +27034,8 @@
"mode": "responses",
"supports_web_search": true,
"supports_reasoning": false,
"supports_function_calling": true
"supports_function_calling": true,
"supports_output_config": true
},
"perplexity/anthropic/claude-opus-4-5": {
"litellm_provider": "perplexity",
@ -31447,7 +31456,8 @@
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346
"tool_use_system_prompt_tokens": 346,
"supports_output_config": true
},
"vertex_ai/claude-opus-4-7@default": {
"cache_creation_input_token_cost": 6.25e-06,
@ -31474,7 +31484,8 @@
"supports_tool_choice": true,
"supports_vision": true,
"supports_xhigh_reasoning_effort": true,
"tool_use_system_prompt_tokens": 346
"tool_use_system_prompt_tokens": 346,
"supports_output_config": true
},
"vertex_ai/claude-sonnet-4-5": {
"cache_creation_input_token_cost": 3.75e-06,

View file

@ -3286,7 +3286,8 @@ def test_transform_request_with_output_config():
def test_transform_request_strips_anthropic_output_config():
"""
output_config is Anthropic-specific and must never be forwarded to Bedrock.
output_config is Anthropic-specific and must be stripped for models that
don't declare supports_output_config (e.g. Nova).
"""
config = AmazonConverseConfig()
messages = [{"role": "user", "content": "hello"}]
@ -3307,6 +3308,76 @@ def test_transform_request_strips_anthropic_output_config():
assert "output_config" not in additional_fields
def test_transform_request_forwards_output_config_for_claude_4_6():
"""
For Bedrock Converse models that declare supports_output_config=true
(Claude 4.6+), the Anthropic-style `output_config` must flow through
`additionalModelRequestFields` so Bedrock forwards the `effort` setting
to the Anthropic backend.
"""
from unittest.mock import patch
config = AmazonConverseConfig()
messages = [{"role": "user", "content": "hello"}]
with patch(
"litellm.llms.bedrock.chat.converse_transformation._supports_factory",
return_value=True,
):
result = config._transform_request(
model="us.anthropic.claude-opus-4-6-v1",
messages=messages,
optional_params={
"maxTokens": 64,
"output_config": {"effort": "high"},
},
litellm_params={},
headers={},
)
additional_fields = result.get("additionalModelRequestFields", {})
assert additional_fields.get("output_config") == {"effort": "high"}
# Native Bedrock `outputConfig` (structured outputs) should not be set.
assert "outputConfig" not in result
def test_transform_request_forwards_output_config_for_claude_4_7():
"""
Claude Opus 4.7 on Bedrock Converse must preserve output_config via
additionalModelRequestFields. Reads the real flag from the bundled cost
JSON so a regression in model_prices_and_context_window.json would trip
this test.
"""
old_env = os.environ.get("LITELLM_LOCAL_MODEL_COST_MAP")
old_cost = litellm.model_cost
os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True"
litellm.model_cost = litellm.get_model_cost_map(url="")
try:
config = AmazonConverseConfig()
messages = [{"role": "user", "content": "hello"}]
result = config._transform_request(
model="us.anthropic.claude-opus-4-7",
messages=messages,
optional_params={
"maxTokens": 64,
"output_config": {"effort": "max"},
},
litellm_params={},
headers={},
)
additional_fields = result.get("additionalModelRequestFields", {})
assert additional_fields.get("output_config") == {"effort": "max"}
assert "outputConfig" not in result
finally:
litellm.model_cost = old_cost
if old_env is None:
os.environ.pop("LITELLM_LOCAL_MODEL_COST_MAP", None)
else:
os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = old_env
def test_transform_response_native_structured_output():
"""Test response handling when model returns JSON as text content (native structured output)."""
response_json = {