Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions litellm/llms/anthropic/chat/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,12 +676,22 @@ def _map_tool_helper( # noqa: PLR0915
**input_schema_filtered
)

_function_strict = tool["function"].get("strict")
_strict = (
_function_strict
if _function_strict is not None
else _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
Expand Down
2 changes: 1 addition & 1 deletion litellm/types/llms/anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class AnthropicMessagesToolChoice(TypedDict, total=False):
"additionalProperties": Optional[bool],
"required": Optional[List[str]],
"$defs": Optional[Dict],
"strict": Optional[bool],
},
total=False,
)
Expand All @@ -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):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4703,3 +4703,124 @@ 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


def test_map_tool_helper_strict_false_function_overrides_parameters_true():
"""strict=False on function level must not be overridden by a leftover
strict=True in parameters."""
config = AnthropicConfig()

tool = {
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather",
"strict": False,
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"strict": True,
},
},
}

result, _ = config._map_tool_helper(tool)
assert result is not None
assert "strict" not in result
assert "strict" not in result["input_schema"]
Loading