From c59260fc70699ea4647be3ec3a6c0f28485fc79e Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 22 May 2026 21:50:22 +0000 Subject: [PATCH] =?UTF-8?q?fix(gemini=20realtime):=20keep=20call=5Fid?= =?UTF-8?q?=E2=86=92name=20mapping=20across=20function=5Fcall=5Foutput=20r?= =?UTF-8?q?etries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A client SDK that retries function_call_output (or sends the same result twice) would previously hit a missing-name lookup on the second send because _handle_function_call_output popped the call_id → name entry. Without name, Gemini may silently reject the response. Use dict.get so the mapping persists for the lifetime of the session. --- litellm/llms/gemini/realtime/transformation.py | 8 +++++--- .../test_gemini_realtime_transformation.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/litellm/llms/gemini/realtime/transformation.py b/litellm/llms/gemini/realtime/transformation.py index 327eee5fd94..2a8f4457e9c 100644 --- a/litellm/llms/gemini/realtime/transformation.py +++ b/litellm/llms/gemini/realtime/transformation.py @@ -417,9 +417,11 @@ class GeminiRealtimeConfig(BaseRealtimeConfig): else {"result": parsed_output} ) - # Look up the function name from stored mapping and remove the - # entry to prevent unbounded growth in long-running sessions. - function_name = self._tool_call_id_to_name.pop(call_id, None) + # Look up the function name from stored mapping. Keep the entry so a + # client SDK that retries function_call_output (or sends it twice for + # the same tool call) still produces a Gemini toolResponse with the + # required ``name`` field. + function_name = self._tool_call_id_to_name.get(call_id) if not function_name: verbose_logger.warning( f"Gemini Realtime: Function name not found for call_id={call_id}. " diff --git a/tests/test_litellm/llms/gemini/realtime/test_gemini_realtime_transformation.py b/tests/test_litellm/llms/gemini/realtime/test_gemini_realtime_transformation.py index 3d4b95d7740..f1dae60379a 100644 --- a/tests/test_litellm/llms/gemini/realtime/test_gemini_realtime_transformation.py +++ b/tests/test_litellm/llms/gemini/realtime/test_gemini_realtime_transformation.py @@ -534,6 +534,20 @@ def test_gemini_realtime_function_call_output_transformation(): assert func_response["response"]["temperature"] == 72 assert func_response["response"]["conditions"] == "sunny" + # A retry of the same function_call_output (e.g. a client SDK that + # re-sends the result) must still produce a functionResponses payload + # carrying ``name`` — the call_id → name mapping must not be evicted + # after the first lookup. + retry_messages = config.transform_realtime_request( + json.dumps(function_output), + "gemini-2.5-flash", + session_configuration_request="existing", + ) + retry_response = json.loads(retry_messages[0])["toolResponse"]["functionResponses"][ + 0 + ] + assert retry_response["name"] == "get_weather" + def test_gemini_realtime_user_text_transformation(): """Test transformation of OpenAI user message to Gemini clientContent format."""