From 8df4d5386bd1f8b6ff3ab524c73ddb5b3874ceb1 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Fri, 22 May 2026 18:55:31 +0530 Subject: [PATCH] fix(gemini/realtime): resolve mypy TypedDict errors in transformation Align realtime event payloads and setup types with OpenAI/Gemini TypedDicts so mypy passes and tool-call events type-check correctly. Co-authored-by: Cursor --- .../llms/base_llm/realtime/transformation.py | 3 +- .../llms/gemini/realtime/transformation.py | 115 ++++++++---------- litellm/types/llms/gemini.py | 2 +- litellm/types/llms/openai.py | 12 ++ 4 files changed, 67 insertions(+), 65 deletions(-) diff --git a/litellm/llms/base_llm/realtime/transformation.py b/litellm/llms/base_llm/realtime/transformation.py index 22768110be9..0f239b4ad45 100644 --- a/litellm/llms/base_llm/realtime/transformation.py +++ b/litellm/llms/base_llm/realtime/transformation.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any, List, Optional, Union import httpx +from litellm.types.llms.openai import OpenAIRealtimeStreamSessionEvents from litellm.types.realtime import ( RealtimeResponseTransformInput, RealtimeResponseTypedDict, @@ -74,7 +75,7 @@ class BaseRealtimeConfig(ABC): model: str, logging_session_id: str, session_configuration_request: Optional[str] = None, - ) -> Optional[dict]: + ) -> Optional[Union[dict, OpenAIRealtimeStreamSessionEvents]]: """ Optional hook for providers that defer session setup until client `session.update`. diff --git a/litellm/llms/gemini/realtime/transformation.py b/litellm/llms/gemini/realtime/transformation.py index ed353660068..5e92be9cb8e 100644 --- a/litellm/llms/gemini/realtime/transformation.py +++ b/litellm/llms/gemini/realtime/transformation.py @@ -30,6 +30,7 @@ from litellm.types.llms.openai import ( OpenAIRealtimeDoneEvent, OpenAIRealtimeEvents, OpenAIRealtimeEventTypes, + OpenAIRealtimeFunctionCallArgumentsDone, OpenAIRealtimeOutputItemDone, OpenAIRealtimeResponseAudioDone, OpenAIRealtimeResponseContentPartAdded, @@ -37,6 +38,7 @@ from litellm.types.llms.openai import ( OpenAIRealtimeResponseDoneObject, OpenAIRealtimeResponseTextDone, OpenAIRealtimeStreamResponseBaseObject, + OpenAIRealtimeStreamResponseOutputItem, OpenAIRealtimeStreamResponseOutputItemAdded, OpenAIRealtimeStreamSession, OpenAIRealtimeStreamSessionEvents, @@ -741,7 +743,7 @@ class GeminiRealtimeConfig(BaseRealtimeConfig): tool_call_message: dict, response_id: Optional[str] = None, output_item_id: Optional[str] = None, - ) -> List[Dict[str, Any]]: + ) -> List[OpenAIRealtimeFunctionCallArgumentsDone]: """ Transform Gemini toolCall message to OpenAI function call events. @@ -756,7 +758,7 @@ class GeminiRealtimeConfig(BaseRealtimeConfig): f"Gemini Realtime: Transforming {len(function_calls)} tool call(s) to OpenAI format" ) - events = [] + events: List[OpenAIRealtimeFunctionCallArgumentsDone] = [] for idx, fc in enumerate(function_calls): call_id = fc.get("id", "") name = fc.get("name", "") @@ -766,16 +768,16 @@ class GeminiRealtimeConfig(BaseRealtimeConfig): self._tool_call_id_to_name[call_id] = name events.append( - { - "type": "response.function_call_arguments.done", - "event_id": f"event_{uuid.uuid4()}", - "response_id": resolved_response_id, - "item_id": f"{resolved_output_item_id}_tool_{idx}", - "output_index": idx, - "call_id": call_id, - "name": name, - "arguments": json.dumps(fc.get("args", {})), - } + OpenAIRealtimeFunctionCallArgumentsDone( + type="response.function_call_arguments.done", + event_id=f"event_{uuid.uuid4()}", + response_id=resolved_response_id, + item_id=f"{resolved_output_item_id}_tool_{idx}", + output_index=idx, + call_id=call_id, + name=name, + arguments=json.dumps(fc.get("args", {})), + ) ) return events @@ -1193,71 +1195,59 @@ class GeminiRealtimeConfig(BaseRealtimeConfig): # Emit output_item.added and conversation.item.created for each function call for idx, tool_call in enumerate(tool_call_events): item_id = tool_call["item_id"] + function_call_item: OpenAIRealtimeStreamResponseOutputItem = { + "id": item_id, + "object": "realtime.item", + "type": "function_call", + "status": "completed", + "call_id": tool_call["call_id"], + "name": tool_call["name"], + "arguments": tool_call["arguments"], + } # response.output_item.added returned_message.append( - { - "type": "response.output_item.added", - "event_id": f"event_{uuid.uuid4()}", - "response_id": current_response_id, - "output_index": idx, - "item": { - "id": item_id, - "object": "realtime.item", - "type": "function_call", + OpenAIRealtimeStreamResponseOutputItemAdded( + type="response.output_item.added", + response_id=current_response_id, + output_index=idx, + item={ + **function_call_item, "status": "in_progress", - "call_id": tool_call["call_id"], - "name": tool_call["name"], "arguments": "", }, - } + ) ) # response.function_call_arguments.done returned_message.append(tool_call) # response.output_item.done returned_message.append( - { - "type": "response.output_item.done", - "event_id": f"event_{uuid.uuid4()}", - "response_id": current_response_id, - "output_index": idx, - "item": { - "id": item_id, - "object": "realtime.item", - "type": "function_call", - "status": "completed", - "call_id": tool_call["call_id"], - "name": tool_call["name"], - "arguments": tool_call["arguments"], - }, - } + OpenAIRealtimeOutputItemDone( + type="response.output_item.done", + event_id=f"event_{uuid.uuid4()}", + response_id=current_response_id, + output_index=idx, + item=function_call_item, + ) ) # conversation.item.created returned_message.append( - { - "type": "conversation.item.created", - "event_id": f"event_{uuid.uuid4()}", - "item": { - "id": item_id, - "object": "realtime.item", - "type": "function_call", - "status": "completed", - "call_id": tool_call["call_id"], - "name": tool_call["name"], - "arguments": tool_call["arguments"], - }, - } + OpenAIRealtimeConversationItemCreated( + type="conversation.item.created", + event_id=f"event_{uuid.uuid4()}", + item=function_call_item, + ) ) # response.done - close the response so clients can submit tool results returned_message.append( - { - "type": "response.done", - "event_id": f"event_{uuid.uuid4()}", - "response": { - "id": current_response_id, - "object": "realtime.response", - "status": "completed", - "output": [ + OpenAIRealtimeDoneEvent( + type="response.done", + event_id=f"event_{uuid.uuid4()}", + response=OpenAIRealtimeResponseDoneObject( + id=current_response_id, + object="realtime.response", + status="completed", + output=[ { "id": te["item_id"], "object": "realtime.item", @@ -1269,9 +1259,8 @@ class GeminiRealtimeConfig(BaseRealtimeConfig): } for te in tool_call_events ], - "usage": None, - }, - } + ), + ) ) # Reset IDs so the next model turn (after tool results) starts a # fresh response with its own response.created preamble. diff --git a/litellm/types/llms/gemini.py b/litellm/types/llms/gemini.py index 9e3fea1bbbb..8763544facc 100644 --- a/litellm/types/llms/gemini.py +++ b/litellm/types/llms/gemini.py @@ -133,7 +133,7 @@ class BidiGenerateContentSetup(TypedDict, total=False): tools: List[Tools] """The tools to be used for the realtime session.""" - realtimeInputConfig: dict + realtimeInputConfig: BidiGenerateContentRealtimeInputConfig """The realtime config to be used for the realtime session.""" sessionResumption: dict diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py index abe58199dfd..1ca55534441 100644 --- a/litellm/types/llms/openai.py +++ b/litellm/types/llms/openai.py @@ -2061,6 +2061,17 @@ class OpenAIRealtimeContentPartDone(TypedDict): type: Literal["response.content_part.done"] +class OpenAIRealtimeFunctionCallArgumentsDone(TypedDict): + type: Literal["response.function_call_arguments.done"] + event_id: str + response_id: str + item_id: str + output_index: int + call_id: str + name: str + arguments: str + + class OpenAIRealtimeOutputItemDone(TypedDict): event_id: str item: OpenAIRealtimeStreamResponseOutputItem @@ -2126,6 +2137,7 @@ OpenAIRealtimeEvents = Union[ OpenAIRealtimeResponseAudioDone, OpenAIRealtimeContentPartDone, OpenAIRealtimeOutputItemDone, + OpenAIRealtimeFunctionCallArgumentsDone, OpenAIRealtimeDoneEvent, ]