mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
fix(realtime): emit response.created preamble before tool-call events
- Emit response.created, output_item.added, and conversation.item.created for function calls - Ensures OpenAI Realtime API spec compliance - Add test for preamble emission Made-with: Cursor
This commit is contained in:
parent
6274bc0d7b
commit
77571e811d
2 changed files with 101 additions and 1 deletions
|
|
@ -1077,12 +1077,65 @@ class GeminiRealtimeConfig(BaseRealtimeConfig):
|
|||
returned_message.append(transformed_message)
|
||||
elif openai_event == ResponsesAPIStreamEvents.FUNCTION_CALL_ARGUMENTS_DONE:
|
||||
# Handle toolCall from Gemini
|
||||
# Emit response.created preamble if this is the first event in the response
|
||||
if current_response_id is None:
|
||||
current_response_id = f"resp_{uuid.uuid4()}"
|
||||
current_output_item_id = f"item_{uuid.uuid4()}"
|
||||
current_conversation_id = current_conversation_id or f"conv_{uuid.uuid4()}"
|
||||
|
||||
# Emit response.created
|
||||
returned_message.append({
|
||||
"type": "response.created",
|
||||
"event_id": f"event_{uuid.uuid4()}",
|
||||
"response": {
|
||||
"object": "realtime.response",
|
||||
"id": current_response_id,
|
||||
"status": "in_progress",
|
||||
"output": [],
|
||||
"conversation_id": current_conversation_id,
|
||||
},
|
||||
})
|
||||
|
||||
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)
|
||||
# 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"]
|
||||
# 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",
|
||||
"status": "in_progress",
|
||||
"call_id": tool_call["call_id"],
|
||||
"name": tool_call["name"],
|
||||
"arguments": "",
|
||||
},
|
||||
})
|
||||
# response.function_call_arguments.done
|
||||
returned_message.append(tool_call)
|
||||
# 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"],
|
||||
},
|
||||
})
|
||||
elif openai_event == OpenAIRealtimeEventTypes.RESPONSE_DONE:
|
||||
transformed_response_done_event = self.transform_response_done_event(
|
||||
message=BidiGenerateContentServerMessage(**json_message), # type: ignore
|
||||
|
|
|
|||
|
|
@ -618,3 +618,50 @@ def test_gemini_session_update_includes_input_audio_transcription_default():
|
|||
assert "setup" in setup
|
||||
assert "inputAudioTranscription" in setup["setup"]
|
||||
assert setup["setup"]["inputAudioTranscription"] == {}
|
||||
|
||||
|
||||
def test_gemini_tool_call_emits_response_created_preamble():
|
||||
"""Verify response.created is emitted before tool call events when response_id is None."""
|
||||
config = GeminiRealtimeConfig()
|
||||
logging_obj = MagicMock()
|
||||
logging_obj.litellm_trace_id = "trace_123"
|
||||
|
||||
gemini_tool_call = {
|
||||
"toolCall": {
|
||||
"functionCalls": [
|
||||
{
|
||||
"id": "call_123",
|
||||
"name": "get_weather",
|
||||
"args": {"location": "San Francisco", "unit": "fahrenheit"}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# Transform with current_response_id=None to trigger preamble emission
|
||||
result = config.transform_realtime_response(
|
||||
json.dumps(gemini_tool_call),
|
||||
"gemini-2.5-flash",
|
||||
logging_obj,
|
||||
realtime_response_transform_input={
|
||||
"session_configuration_request": None,
|
||||
"current_output_item_id": None,
|
||||
"current_response_id": None,
|
||||
"current_conversation_id": None,
|
||||
"current_delta_chunks": [],
|
||||
"current_item_chunks": [],
|
||||
"current_delta_type": None,
|
||||
},
|
||||
)
|
||||
|
||||
responses = result["response"]
|
||||
# Should have: response.created, output_item.added, function_call_arguments.done, conversation.item.created
|
||||
assert len(responses) >= 4
|
||||
assert responses[0]["type"] == "response.created"
|
||||
assert "response" in responses[0]
|
||||
assert responses[0]["response"]["status"] == "in_progress"
|
||||
assert responses[1]["type"] == "response.output_item.added"
|
||||
assert responses[1]["item"]["type"] == "function_call"
|
||||
assert responses[2]["type"] == "response.function_call_arguments.done"
|
||||
assert responses[3]["type"] == "conversation.item.created"
|
||||
assert responses[3]["item"]["type"] == "function_call"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue