diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index 2f11a3fccb5..8d6aa4b6b24 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -676,12 +676,17 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): **input_schema_filtered ) + _strict = tool["function"].get("strict") or _input_schema.get("strict") + _tool = AnthropicMessagesTool( name=tool["function"]["name"], input_schema=input_anthropic_schema, type="custom", ) + if _strict is True: + _tool["strict"] = True + _description = tool["function"].get("description") if _description is not None: _tool["description"] = _description diff --git a/litellm/types/llms/anthropic.py b/litellm/types/llms/anthropic.py index 1c4d31d21ad..361eeeecca8 100644 --- a/litellm/types/llms/anthropic.py +++ b/litellm/types/llms/anthropic.py @@ -25,7 +25,6 @@ AnthropicInputSchema = TypedDict( "additionalProperties": Optional[bool], "required": Optional[List[str]], "$defs": Optional[Dict], - "strict": Optional[bool], }, total=False, ) @@ -51,6 +50,7 @@ class AnthropicMessagesTool(TypedDict, total=False): defer_loading: bool allowed_callers: Optional[List[str]] input_examples: Optional[List[Dict[str, Any]]] + strict: bool class AnthropicComputerTool(TypedDict, total=False): diff --git a/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py b/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py index e38698c9100..65c0f953115 100644 --- a/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py +++ b/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py @@ -4703,3 +4703,99 @@ def test_sanitize_tool_names_in_request_no_tools_is_noop(): forward, reverse = AnthropicConfig._sanitize_tool_names_in_request({"tools": []}) assert forward == {} assert reverse == {} + + +def test_map_tool_helper_strict_from_function_level(): + """strict on tool.function (OpenAI canonical location) should be placed + at the tool top level, not inside input_schema.""" + config = AnthropicConfig() + + tool = { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather", + "strict": True, + "parameters": { + "type": "object", + "properties": {"city": {"type": "string"}}, + "required": ["city"], + "additionalProperties": False, + }, + }, + } + + result, _ = config._map_tool_helper(tool) + assert result is not None + assert result.get("strict") is True + assert "strict" not in result["input_schema"] + + +def test_map_tool_helper_strict_from_parameters(): + """strict inside tool.function.parameters (legacy placement) should be + moved to the tool top level.""" + config = AnthropicConfig() + + tool = { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather", + "parameters": { + "type": "object", + "properties": {"city": {"type": "string"}}, + "required": ["city"], + "additionalProperties": False, + "strict": True, + }, + }, + } + + result, _ = config._map_tool_helper(tool) + assert result is not None + assert result.get("strict") is True + assert "strict" not in result["input_schema"] + + +def test_map_tool_helper_no_strict_omits_field(): + """When strict is not set, it should not appear on the tool.""" + config = AnthropicConfig() + + tool = { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather", + "parameters": { + "type": "object", + "properties": {"city": {"type": "string"}}, + }, + }, + } + + result, _ = config._map_tool_helper(tool) + assert result is not None + assert "strict" not in result + assert "strict" not in result["input_schema"] + + +def test_map_tool_helper_strict_false_omits_field(): + """strict=False should not be forwarded to Anthropic.""" + config = AnthropicConfig() + + tool = { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather", + "strict": False, + "parameters": { + "type": "object", + "properties": {"city": {"type": "string"}}, + }, + }, + } + + result, _ = config._map_tool_helper(tool) + assert result is not None + assert "strict" not in result