Skip to content
Merged
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
17 changes: 13 additions & 4 deletions litellm/llms/anthropic/chat/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,19 @@ def _map_tool_choice(
elif tool_choice == "none":
_tool_choice = AnthropicMessagesToolChoice(type="none")
elif isinstance(tool_choice, dict):
_tool_name = tool_choice.get("function", {}).get("name")
_tool_choice = AnthropicMessagesToolChoice(type="tool")
if _tool_name is not None:
_tool_choice["name"] = _tool_name
if "type" in tool_choice and "function" not in tool_choice:
tool_type = tool_choice.get("type")
if tool_type == "auto":
_tool_choice = AnthropicMessagesToolChoice(type="auto")
elif tool_type == "required" or tool_type == "any":
_tool_choice = AnthropicMessagesToolChoice(type="any")
elif tool_type == "none":
_tool_choice = AnthropicMessagesToolChoice(type="none")
else:
_tool_name = tool_choice.get("function", {}).get("name")
if _tool_name is not None:
_tool_choice = AnthropicMessagesToolChoice(type="tool")
_tool_choice["name"] = _tool_name

if parallel_tool_use is not None:
# Anthropic uses 'disable_parallel_tool_use' flag to determine if parallel tool use is allowed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,59 @@ def test_map_tool_choice_dict_type_function_with_name():
assert result["name"] == "my_tool"


def test_map_tool_choice_dict_type_auto():
"""
Test that dict {"type": "auto"} maps to Anthropic type='auto'.
This handles Cursor's format for tool_choice.
"""
config = AnthropicConfig()
result = config._map_tool_choice(
tool_choice={"type": "auto"},
parallel_tool_use=None,
)
assert result is not None
assert result["type"] == "auto"


def test_map_tool_choice_dict_type_required():
"""
Test that dict {"type": "required"} maps to Anthropic type='any'.
"""
config = AnthropicConfig()
result = config._map_tool_choice(
tool_choice={"type": "required"},
parallel_tool_use=None,
)
assert result is not None
assert result["type"] == "any"


def test_map_tool_choice_dict_type_none():
"""
Test that dict {"type": "none"} maps to Anthropic type='none'.
"""
config = AnthropicConfig()
result = config._map_tool_choice(
tool_choice={"type": "none"},
parallel_tool_use=None,
)
assert result is not None
assert result["type"] == "none"


def test_map_tool_choice_dict_type_function_without_name():
"""
Test that dict {"type": "function"} without name is handled gracefully.
Should return None since there's no valid tool name.
"""
config = AnthropicConfig()
result = config._map_tool_choice(
tool_choice={"type": "function"},
parallel_tool_use=None,
)
assert result is None


def test_transform_response_with_prefix_prompt():
import httpx

Expand Down
Loading