mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
Fix greptile review
This commit is contained in:
parent
7882f8072f
commit
c6a51d031f
4 changed files with 98 additions and 2 deletions
|
|
@ -667,6 +667,8 @@ class GeminiRealtimeConfig(BaseRealtimeConfig):
|
|||
def transform_tool_call_events(
|
||||
self,
|
||||
tool_call_message: dict,
|
||||
response_id: Optional[str] = None,
|
||||
output_item_id: Optional[str] = None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Transform Gemini toolCall message to OpenAI function call events.
|
||||
|
|
@ -674,6 +676,8 @@ class GeminiRealtimeConfig(BaseRealtimeConfig):
|
|||
Converts Gemini's functionCalls format to OpenAI's response.function_call_arguments.done events.
|
||||
"""
|
||||
function_calls = tool_call_message.get("functionCalls", [])
|
||||
resolved_response_id = response_id or f"resp_{uuid.uuid4()}"
|
||||
resolved_output_item_id = output_item_id or f"item_{uuid.uuid4()}"
|
||||
|
||||
verbose_logger.debug(
|
||||
f"Gemini Realtime: Transforming {len(function_calls)} tool call(s) to OpenAI format"
|
||||
|
|
@ -683,11 +687,14 @@ class GeminiRealtimeConfig(BaseRealtimeConfig):
|
|||
{
|
||||
"type": "response.function_call_arguments.done",
|
||||
"event_id": f"event_{uuid.uuid4()}",
|
||||
"response_id": resolved_response_id,
|
||||
"item_id": resolved_output_item_id,
|
||||
"output_index": idx,
|
||||
"call_id": fc.get("id", ""),
|
||||
"name": fc.get("name", ""),
|
||||
"arguments": json.dumps(fc.get("args", {})),
|
||||
}
|
||||
for fc in function_calls
|
||||
for idx, fc in enumerate(function_calls)
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -1067,7 +1074,11 @@ class GeminiRealtimeConfig(BaseRealtimeConfig):
|
|||
returned_message.append(transformed_message)
|
||||
elif openai_event == ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DONE:
|
||||
# Handle toolCall from Gemini
|
||||
tool_call_events = self.transform_tool_call_events(value)
|
||||
tool_call_events = self.transform_tool_call_events(
|
||||
value,
|
||||
response_id=current_response_id,
|
||||
output_item_id=current_output_item_id,
|
||||
)
|
||||
returned_message.extend(tool_call_events)
|
||||
elif openai_event == OpenAIRealtimeEventTypes.RESPONSE_DONE:
|
||||
transformed_response_done_event = self.transform_response_done_event(
|
||||
|
|
|
|||
|
|
@ -254,6 +254,50 @@ async def test_transcription_captured_in_backend_to_client():
|
|||
assert logging_obj.model_call_details["messages"] == streaming.input_messages
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_client_ack_caches_setup_to_prevent_duplicate_session_update_setup():
|
||||
websocket = MagicMock()
|
||||
backend_ws = MagicMock()
|
||||
logging_obj = MagicMock()
|
||||
logging_obj.pre_call = MagicMock()
|
||||
|
||||
# Two session.update messages arrive before setupComplete round-trip.
|
||||
websocket.receive_text = AsyncMock(
|
||||
side_effect=[
|
||||
json.dumps({"type": "session.update", "session": {"tools": []}}),
|
||||
json.dumps({"type": "session.update", "session": {"tools": []}}),
|
||||
Exception("client done"),
|
||||
]
|
||||
)
|
||||
|
||||
provider_config = MagicMock()
|
||||
|
||||
def _transform(message: str, model: str, session_configuration_request=None):
|
||||
if session_configuration_request is None:
|
||||
return [json.dumps({"setup": {"model": "models/gemini-2.5-flash"}})]
|
||||
return []
|
||||
|
||||
provider_config.transform_realtime_request = MagicMock(side_effect=_transform)
|
||||
|
||||
backend_ws.send = AsyncMock()
|
||||
|
||||
streaming = RealTimeStreaming(
|
||||
websocket=websocket,
|
||||
backend_ws=backend_ws,
|
||||
logging_obj=logging_obj,
|
||||
provider_config=provider_config,
|
||||
model="gemini-2.5-flash",
|
||||
)
|
||||
|
||||
await streaming.client_ack_messages()
|
||||
|
||||
# Setup should be forwarded exactly once even with repeated session.update.
|
||||
assert backend_ws.send.await_count == 1
|
||||
assert streaming.session_configuration_request is not None
|
||||
sent_payload = json.loads(backend_ws.send.await_args_list[0].args[0])
|
||||
assert "setup" in sent_payload
|
||||
|
||||
|
||||
def test_collect_session_tools_from_session_update():
|
||||
"""
|
||||
Test that tools from session.update events are collected.
|
||||
|
|
|
|||
|
|
@ -297,6 +297,9 @@ def test_gemini_realtime_tool_call_transformation():
|
|||
assert function_call_event is not None, "Expected function_call_arguments.done event"
|
||||
assert function_call_event["call_id"] == "call_123"
|
||||
assert function_call_event["name"] == "get_weather"
|
||||
assert function_call_event["response_id"] == "resp_123"
|
||||
assert function_call_event["item_id"] == "item_123"
|
||||
assert function_call_event["output_index"] == 0
|
||||
|
||||
# Verify arguments are properly serialized as JSON string
|
||||
args = json.loads(function_call_event["arguments"])
|
||||
|
|
@ -454,3 +457,18 @@ def test_gemini_realtime_user_text_transformation():
|
|||
assert len(turn["parts"]) == 1
|
||||
assert turn["parts"][0]["text"] == "What's the weather in London?"
|
||||
assert client_content["turnComplete"] is True
|
||||
|
||||
|
||||
def test_return_new_content_delta_events_without_session_config_does_not_error():
|
||||
config = GeminiRealtimeConfig()
|
||||
|
||||
events = config.return_new_content_delta_events(
|
||||
response_id="resp_1",
|
||||
output_item_id="item_1",
|
||||
conversation_id="conv_1",
|
||||
delta_type="text",
|
||||
session_configuration_request=None,
|
||||
)
|
||||
|
||||
assert len(events) >= 1
|
||||
assert events[0]["type"] == "response.created"
|
||||
|
|
|
|||
|
|
@ -97,6 +97,29 @@ def test_vertex_requires_session_configuration_feature_flag(monkeypatch):
|
|||
assert cfg.requires_session_configuration() is False
|
||||
|
||||
|
||||
def test_vertex_session_update_defaults_to_audio_modality():
|
||||
cfg = VertexAIRealtimeConfig(
|
||||
access_token="tok", project="my-proj", location="us-central1"
|
||||
)
|
||||
|
||||
session_update = {
|
||||
"type": "session.update",
|
||||
"session": {
|
||||
"instructions": "You are a helpful assistant.",
|
||||
# No modalities provided on purpose
|
||||
},
|
||||
}
|
||||
|
||||
messages = cfg.transform_realtime_request(
|
||||
json.dumps(session_update),
|
||||
"gemini-live-2.5-flash-native-audio",
|
||||
session_configuration_request=None,
|
||||
)
|
||||
assert len(messages) == 1
|
||||
setup_payload = json.loads(messages[0])["setup"]
|
||||
assert setup_payload["generationConfig"]["responseModalities"] == ["AUDIO"]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Round-trip test: text-in / text-out via RealTimeStreaming
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue