diff --git a/litellm/llms/prompt_templates/factory.py b/litellm/llms/prompt_templates/factory.py index f8affa88760..30acaa03401 100644 --- a/litellm/llms/prompt_templates/factory.py +++ b/litellm/llms/prompt_templates/factory.py @@ -1052,6 +1052,9 @@ def convert_to_gemini_tool_call_result( if msg_tool_call_id and prev_tool_call_id and msg_tool_call_id == prev_tool_call_id: name = tool.get("function", {}).get("name", "") + if not name: + raise Exception("Missing corresponding tool call for tool response message") + # We can't determine from openai message format whether it's a successful or # error call result so default to the successful result template inferred_content_value = infer_protocol_value(value=content) diff --git a/litellm/tests/test_amazing_vertex_completion.py b/litellm/tests/test_amazing_vertex_completion.py index c4705325b99..5d494ac1358 100644 --- a/litellm/tests/test_amazing_vertex_completion.py +++ b/litellm/tests/test_amazing_vertex_completion.py @@ -1154,8 +1154,7 @@ async def test_gemini_pro_function_calling(provider, sync_mode): # The result of the tool call is added to the history { "role": "tool", - "tool_call_id": "call_123", - "name": "get_weather", + "tool_call_id": "call_123", "content": "27 degrees celsius and clear in San Francisco, CA", }, # Now the assistant can reply with the result of the tool call. @@ -1377,6 +1376,52 @@ async def test_vertexai_aembedding(): except Exception as e: pytest.fail(f"Error occurred: {e}") +@pytest.mark.asyncio +def test_tool_name_conversion(): + messages = [ + { + "role": "system", + "content": "Your name is Litellm Bot, you are a helpful assistant", + }, + # User asks for their name and weather in San Francisco + { + "role": "user", + "content": "Hello, what is your name and can you tell me the weather?", + }, + # Assistant replies with a tool call + { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "id": "call_123", + "type": "function", + "index": 0, + "function": { + "name": "get_weather", + "arguments": '{"location":"San Francisco, CA"}', + }, + } + ], + }, + # The result of the tool call is added to the history + { + "role": "tool", + "tool_call_id": "call_123", + "content": "27 degrees celsius and clear in San Francisco, CA", + }, + # Now the assistant can reply with the result of the tool call. + ] + + translated_messages = _gemini_convert_messages_with_history(messages=messages) + + print(f"\n\ntranslated_messages: {translated_messages}\ntranslated_messages") + + # assert that the last tool response has the corresponding tool name + assert ( + translated_messages[-1]["parts"][0]["function_response"]["name"] == "get_weather" + ) + # Extra gemini Vision tests for completion + stream, async, async + stream # if we run into issues with gemini, we will also add these to our ci/cd pipeline @@ -1526,7 +1571,6 @@ def test_prompt_factory(): { "role": "tool", "tool_call_id": "call_123", - "name": "get_weather", "content": "27 degrees celsius and clear in San Francisco, CA", }, # Now the assistant can reply with the result of the tool call. @@ -1536,7 +1580,6 @@ def test_prompt_factory(): print(f"\n\ntranslated_messages: {translated_messages}\ntranslated_messages") - def test_prompt_factory_nested(): messages = [ {"role": "user", "content": [{"type": "text", "text": "hi"}]}, @@ -1558,4 +1601,4 @@ def test_prompt_factory_nested(): assert "text" in message["parts"][0], "Missing 'text' from 'parts'" assert isinstance( message["parts"][0]["text"], str - ), "'text' value not a string." + ), "'text' value not a string." \ No newline at end of file