fix: add claude-haiku-4-5 to native structured output allowlist

claude-haiku-4-5-20251001 supports native structured output via the
output_format parameter but was missing from the model substring allowlist
in map_openai_params, causing it to fall back to the json_tool_call
synthesis workaround. This broke downstream tool dispatch when the model
also returned legitimate tool calls.

Adds test in tests/test_litellm/llms/anthropic/test_anthropic_structured_output.py.

Fixes https://github.com/BerriAI/litellm/issues/25308

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Smeet Agrawal 2026-04-09 10:28:27 +05:30
parent 1ef452e969
commit 0d29da0af5
2 changed files with 39 additions and 38 deletions

View file

@ -447,44 +447,6 @@ def test_anthropic_tool_helper(cache_control_location):
assert tool["cache_control"] == {"type": "ephemeral"}
def test_haiku_4_5_uses_native_structured_output():
"""
Haiku 4.5 supports native structured output via output_format.
It should NOT fall back to json_tool_call synthesis.
Regression test for https://github.com/BerriAI/litellm/issues/25308
"""
from litellm.llms.anthropic.chat.transformation import AnthropicConfig
config = AnthropicConfig()
response_format = {
"type": "json_schema",
"json_schema": {
"name": "MovieReview",
"schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"rating": {"type": "number"},
},
"required": ["title", "rating"],
},
},
}
optional_params = config.map_openai_params(
non_default_params={"response_format": response_format},
optional_params={},
model="claude-haiku-4-5-20251001",
drop_params=False,
)
# Should use native output_format, not json_tool_call
assert "output_format" in optional_params, (
"haiku-4-5 should use native output_format, not json_tool_call"
)
assert "tools" not in optional_params or not any(
t.get("name") == "json_tool_call" for t in optional_params.get("tools", [])
), "haiku-4-5 should not synthesize a json_tool_call"
def test_create_json_tool_call_for_response_format():
"""
tests using response_format=json with anthropic

View file

@ -128,6 +128,45 @@ class TestAnthropicStructuredOutput:
if "properties" in nested_item_schema and "tags" in nested_item_schema["properties"]:
assert "maxItems" not in nested_item_schema["properties"]["tags"]
def test_haiku_4_5_uses_native_structured_output(self):
"""
claude-haiku-4-5 supports native structured output via output_format.
It should NOT fall back to the json_tool_call synthesis workaround.
Regression test for https://github.com/BerriAI/litellm/issues/25308
"""
from litellm.llms.anthropic.chat.transformation import AnthropicConfig
config = AnthropicConfig()
response_format = {
"type": "json_schema",
"json_schema": {
"name": "MovieReview",
"schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"rating": {"type": "number"},
},
"required": ["title", "rating"],
},
},
}
optional_params = config.map_openai_params(
non_default_params={"response_format": response_format},
optional_params={},
model="claude-haiku-4-5-20251001",
drop_params=False,
)
# Should use native output_format, not json_tool_call
assert "output_format" in optional_params, (
"claude-haiku-4-5 should use native output_format, not json_tool_call"
)
assert not any(
t.get("name") == "json_tool_call"
for t in optional_params.get("tools", [])
), "claude-haiku-4-5 should not synthesize a json_tool_call"
def test_other_constraints_preserved(self):
"""
Test that constraints are properly handled (removed from schema, added to description).