mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
fix(anthropic): prevent tool type leaking into input_schema during guardrail translation
When translating Anthropic custom tools to OpenAI format for guardrails, the extra-params loop in translate_anthropic_tools_to_openai() copied every unmapped key into function.parameters. Since "type" was not in mapped_tool_params, the tool-level type:"custom" was written into parameters, overwriting input_schema.type from "object" to "custom". This caused Anthropic/Bedrock to reject the request with: tools.0.custom.input_schema.type: Input should be 'object' Two fixes: 1. Add "type" to mapped_tool_params so it's excluded from the loop 2. Use copy.copy() on input_schema to prevent shallow-copy mutation of the original request data Fixes: tools rejected after guardrail pre_call on /v1/messages endpoint Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9343aeefca
commit
b0a37e59e7
2 changed files with 62 additions and 2 deletions
|
|
@ -794,7 +794,7 @@ class LiteLLMAnthropicMessagesAdapter:
|
|||
"""
|
||||
new_tools: List[ChatCompletionToolParam] = []
|
||||
tool_name_mapping: Dict[str, str] = {}
|
||||
mapped_tool_params = ["name", "input_schema", "description", "cache_control"]
|
||||
mapped_tool_params = ["name", "input_schema", "description", "cache_control", "type"]
|
||||
|
||||
for tool in tools:
|
||||
# Check if this is an Anthropic-native tool that should be kept as-is
|
||||
|
|
@ -815,7 +815,7 @@ class LiteLLMAnthropicMessagesAdapter:
|
|||
name=truncated_name,
|
||||
)
|
||||
if "input_schema" in tool:
|
||||
function_chunk["parameters"] = tool["input_schema"] # type: ignore
|
||||
function_chunk["parameters"] = copy.copy(tool["input_schema"]) # type: ignore
|
||||
if "description" in tool:
|
||||
function_chunk["description"] = tool["description"] # type: ignore
|
||||
|
||||
|
|
|
|||
|
|
@ -2111,3 +2111,63 @@ class TestTranslateAnthropicOutputFormatToOpenAI:
|
|||
assert self.adapter.translate_anthropic_output_format_to_openai("invalid") is None
|
||||
assert self.adapter.translate_anthropic_output_format_to_openai({"type": "text"}) is None
|
||||
assert self.adapter.translate_anthropic_output_format_to_openai({"type": "json_schema"}) is None
|
||||
|
||||
|
||||
class TestTranslateAnthropicToolsToOpenAI:
|
||||
"""Tests for translate_anthropic_tools_to_openai tool format preservation."""
|
||||
|
||||
def setup_method(self):
|
||||
self.adapter = LiteLLMAnthropicMessagesAdapter()
|
||||
|
||||
def test_custom_tool_type_not_leaked_into_parameters(self):
|
||||
"""
|
||||
Regression: the 'type' field from a custom Anthropic tool must NOT
|
||||
leak into function.parameters. Previously, the extra-params loop
|
||||
copied every unmapped key into parameters, overwriting
|
||||
input_schema.type from 'object' to 'custom'.
|
||||
"""
|
||||
tools = [
|
||||
{
|
||||
"type": "custom",
|
||||
"name": "Write",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {"type": "string"},
|
||||
"content": {"type": "string"},
|
||||
},
|
||||
"required": ["file_path", "content"],
|
||||
},
|
||||
}
|
||||
]
|
||||
result, _ = self.adapter.translate_anthropic_tools_to_openai(tools=tools)
|
||||
assert len(result) == 1
|
||||
params = result[0]["function"]["parameters"]
|
||||
assert params["type"] == "object", (
|
||||
f"Expected parameters.type='object', got '{params.get('type')}'. "
|
||||
"The tool-level 'type' field leaked into parameters."
|
||||
)
|
||||
|
||||
def test_input_schema_not_mutated_by_translation(self):
|
||||
"""
|
||||
The original input_schema dict must not be mutated when
|
||||
translating to OpenAI format (prevents shallow-copy corruption).
|
||||
"""
|
||||
original_schema = {
|
||||
"type": "object",
|
||||
"properties": {"q": {"type": "string"}},
|
||||
}
|
||||
tools = [
|
||||
{
|
||||
"type": "custom",
|
||||
"name": "Search",
|
||||
"input_schema": original_schema,
|
||||
}
|
||||
]
|
||||
self.adapter.translate_anthropic_tools_to_openai(tools=tools)
|
||||
assert original_schema["type"] == "object", (
|
||||
"Original input_schema was mutated by translation"
|
||||
)
|
||||
assert "name" not in original_schema, (
|
||||
"Extra keys leaked into the original input_schema"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue