From b60cc950f3be4edc61e3a1f01f288192c4db811c Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 22 May 2026 21:44:43 +0000 Subject: [PATCH] fix(gemini realtime): mirror modalities/temperature/max_output_tokens on tool-call response.created The audio/text response.created preamble includes modalities, temperature, and max_output_tokens on the response object so spec-compliant clients can initialise per-response state. The tool-call response.created was missing these fields, leaving clients without consistent response metadata when a response starts with a tool call instead of content. Read them from the cached session_configuration_request the same way the audio/text path does. --- .../llms/gemini/realtime/transformation.py | 33 ++++++++++++++++++- .../test_gemini_realtime_transformation.py | 6 ++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/litellm/llms/gemini/realtime/transformation.py b/litellm/llms/gemini/realtime/transformation.py index 84bdd6051a4..327eee5fd94 100644 --- a/litellm/llms/gemini/realtime/transformation.py +++ b/litellm/llms/gemini/realtime/transformation.py @@ -1251,7 +1251,31 @@ class GeminiRealtimeConfig(BaseRealtimeConfig): current_response_id = f"resp_{uuid.uuid4()}" current_output_item_id = f"item_{uuid.uuid4()}" - # Emit response.created + # Mirror the audio/text path: include modalities, + # temperature, and max_output_tokens on response.created so + # spec-compliant clients see consistent response metadata + # regardless of whether the response starts with content or + # a tool call. + session_setup: BidiGenerateContentSetup = {} + if session_configuration_request is not None: + try: + session_setup = json.loads( + session_configuration_request + ).get("setup", {}) + except (json.JSONDecodeError, TypeError): + session_setup = {} + tool_call_generation_config = ( + session_setup.get("generationConfig", {}) or {} + ) + tool_call_modalities = [ + modality.lower() + for modality in cast( + List[str], + tool_call_generation_config.get( + "responseModalities", ["AUDIO"] + ), + ) + ] returned_message.append( { "type": "response.created", @@ -1262,6 +1286,13 @@ class GeminiRealtimeConfig(BaseRealtimeConfig): "status": "in_progress", "output": [], "conversation_id": current_conversation_id, + "modalities": tool_call_modalities, + "temperature": tool_call_generation_config.get( + "temperature" + ), + "max_output_tokens": tool_call_generation_config.get( + "maxOutputTokens" + ), }, } ) 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 aac14588c95..3d4b95d7740 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 @@ -715,6 +715,12 @@ def test_gemini_tool_call_emits_response_created_preamble(): assert responses[0]["type"] == "response.created" assert "response" in responses[0] assert responses[0]["response"]["status"] == "in_progress" + # response.created on the tool-call path mirrors the audio/text preamble: + # modalities/temperature/max_output_tokens are present so spec-compliant + # clients see consistent response metadata regardless of payload type. + assert "modalities" in responses[0]["response"] + assert "temperature" in responses[0]["response"] + assert "max_output_tokens" in responses[0]["response"] assert responses[1]["type"] == "response.output_item.added" assert responses[1]["item"]["type"] == "function_call" assert responses[1]["item"]["status"] == "in_progress"