mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
fix(anthropic): enforce type:'object' on tool input schemas
Anthropic's API requires all tool input_schema to have type:'object' at the root level. When OpenAI-format tools have parameters with a missing or non-'object' type field (common with MCP tool servers), the schema was passed through unchanged, causing Anthropic to reject with: 'tools.N.custom.input_schema.type: Input should be object'. The existing default handles the case where parameters is entirely missing, but does not normalize schemas that ARE provided with a wrong or absent type field. Fix: After extracting _input_schema in _map_tool_helper(), ensure type is set to 'object' and properties exists. This matches the normalization already done implicitly by the Bedrock handler. Added 4 unit tests covering: missing type, wrong type, valid schema (no-op), and entirely missing parameters. Related issues: #12020, #64, #1671
This commit is contained in:
parent
160e2d9642
commit
78159212d9
2 changed files with 120 additions and 0 deletions
|
|
@ -395,6 +395,14 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
},
|
||||
)
|
||||
|
||||
# Anthropic requires input_schema.type to be "object". Normalize
|
||||
# schemas from external sources (MCP servers, OpenAI callers) that
|
||||
# may omit the type field or use a non-object type.
|
||||
if _input_schema.get("type") != "object":
|
||||
_input_schema["type"] = "object"
|
||||
if "properties" not in _input_schema:
|
||||
_input_schema["properties"] = {}
|
||||
|
||||
_allowed_properties = set(AnthropicInputSchema.__annotations__.keys())
|
||||
input_schema_filtered = {
|
||||
k: v for k, v in _input_schema.items() if k in _allowed_properties
|
||||
|
|
|
|||
|
|
@ -3175,3 +3175,115 @@ def test_map_openai_params_max_tokens_normalized_to_int():
|
|||
|
||||
assert "max_tokens" in result
|
||||
assert result["max_tokens"] == 1
|
||||
|
||||
|
||||
# ========================================================================
|
||||
# Tool schema normalization tests
|
||||
# ========================================================================
|
||||
|
||||
|
||||
def test_map_tool_helper_enforces_object_type_when_missing():
|
||||
"""
|
||||
Anthropic requires input_schema.type to be "object". When an OpenAI tool
|
||||
has parameters without a 'type' field (common with MCP servers), LiteLLM
|
||||
should inject type:"object" before forwarding to Anthropic.
|
||||
|
||||
Without this fix, Anthropic rejects with:
|
||||
tools.N.custom.input_schema.type: Input should be 'object'
|
||||
"""
|
||||
config = AnthropicConfig()
|
||||
|
||||
# Tool with parameters that has properties but no 'type' field
|
||||
tool = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "search_code",
|
||||
"description": "Search for code patterns",
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"query": {"type": "string", "description": "Search query"}
|
||||
},
|
||||
"required": ["query"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result, _ = config._map_tool_helper(tool)
|
||||
assert result is not None
|
||||
assert result["input_schema"]["type"] == "object"
|
||||
assert "properties" in result["input_schema"]
|
||||
assert "query" in result["input_schema"]["properties"]
|
||||
|
||||
|
||||
def test_map_tool_helper_enforces_object_type_when_wrong_type():
|
||||
"""
|
||||
If a tool schema has type:"string" or type:"array" at the root level,
|
||||
LiteLLM should normalize it to type:"object" for Anthropic compatibility.
|
||||
"""
|
||||
config = AnthropicConfig()
|
||||
|
||||
tool = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "echo",
|
||||
"description": "Echo input",
|
||||
"parameters": {
|
||||
"type": "string",
|
||||
"description": "The input to echo",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result, _ = config._map_tool_helper(tool)
|
||||
assert result is not None
|
||||
assert result["input_schema"]["type"] == "object"
|
||||
|
||||
|
||||
def test_map_tool_helper_preserves_valid_object_schema():
|
||||
"""
|
||||
When a tool schema already has type:"object", it should be preserved
|
||||
without modification.
|
||||
"""
|
||||
config = AnthropicConfig()
|
||||
|
||||
tool = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get weather",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {"type": "string"},
|
||||
},
|
||||
"required": ["city"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result, _ = config._map_tool_helper(tool)
|
||||
assert result is not None
|
||||
assert result["input_schema"]["type"] == "object"
|
||||
assert "city" in result["input_schema"]["properties"]
|
||||
assert result["input_schema"]["required"] == ["city"]
|
||||
|
||||
|
||||
def test_map_tool_helper_empty_parameters_get_default():
|
||||
"""
|
||||
When parameters is entirely missing, the existing default should still
|
||||
produce a valid {type:"object", properties:{}} schema.
|
||||
"""
|
||||
config = AnthropicConfig()
|
||||
|
||||
tool = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "no_params_tool",
|
||||
"description": "Tool with no parameters",
|
||||
},
|
||||
}
|
||||
|
||||
result, _ = config._map_tool_helper(tool)
|
||||
assert result is not None
|
||||
assert result["input_schema"]["type"] == "object"
|
||||
assert result["input_schema"].get("properties") == {}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue