diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 7b485501f6..ba415af9a5 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -1848,9 +1848,10 @@ def convert_to_anthropic_tool_invoke( break else: # Regular tool_use + sanitized_tool_id = _sanitize_anthropic_tool_use_id(tool_id) _anthropic_tool_use_param = AnthropicMessagesToolUseParam( type="tool_use", - id=tool_id, + id=sanitized_tool_id, name=tool_name, input=tool_input, ) diff --git a/tests/llm_translation/test_prompt_factory.py b/tests/llm_translation/test_prompt_factory.py index 8974632631..88bca00774 100644 --- a/tests/llm_translation/test_prompt_factory.py +++ b/tests/llm_translation/test_prompt_factory.py @@ -973,6 +973,54 @@ def test_convert_to_anthropic_tool_invoke_regular_tool(): assert result[0]["input"] == {"location": "San Francisco"} +def test_convert_to_anthropic_tool_invoke_sanitizes_invalid_ids(): + """Test that tool_use IDs with invalid characters are sanitized. + + Anthropic requires tool_use_id to match ^[a-zA-Z0-9_-]+$. + IDs from external frameworks (e.g. MiniMax) may contain characters + like colons that violate this pattern. + """ + tool_calls = [ + { + "id": "sessions_history:183", + "type": "function", + "function": { + "name": "get_weather", + "arguments": '{"location": "Boston"}', + }, + }, + { + "id": "composio.NOTION_SEARCH", + "type": "function", + "function": { + "name": "search_notes", + "arguments": '{"query": "test"}', + }, + }, + ] + + result = convert_to_anthropic_tool_invoke(tool_calls) + + assert len(result) == 2 + # Colons replaced with underscores + assert result[0]["id"] == "sessions_history_183" + # Dots replaced with underscores + assert result[1]["id"] == "composio_NOTION_SEARCH" + # Valid IDs should pass through unchanged + valid_tool_calls = [ + { + "id": "toolu_01ABC-xyz_123", + "type": "function", + "function": { + "name": "get_weather", + "arguments": '{"location": "NYC"}', + }, + } + ] + valid_result = convert_to_anthropic_tool_invoke(valid_tool_calls) + assert valid_result[0]["id"] == "toolu_01ABC-xyz_123" + + def test_convert_to_anthropic_tool_invoke_server_tool(): """ Test that server_tool_use (srvtoolu_) is reconstructed as server_tool_use.