Skip to content

Commit 1151c2a

Browse files
committed
Realtime: forward all raw model events
1 parent 4967881 commit 1151c2a

File tree

4 files changed

+30
-16
lines changed

4 files changed

+30
-16
lines changed

src/agents/realtime/model_events.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,15 @@ class RealtimeModelExceptionEvent:
156156
type: Literal["exception"] = "exception"
157157

158158

159+
@dataclass
160+
class RealtimeModelRawServerEvent:
161+
"""Raw events forwarded from the server."""
162+
163+
data: Any
164+
165+
type: Literal["raw_server_event"] = "raw_server_event"
166+
167+
159168
# TODO (rm) Add usage events
160169

161170

@@ -174,4 +183,5 @@ class RealtimeModelExceptionEvent:
174183
RealtimeModelTurnEndedEvent,
175184
RealtimeModelOtherEvent,
176185
RealtimeModelExceptionEvent,
186+
RealtimeModelRawServerEvent,
177187
]

src/agents/realtime/openai_realtime.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
RealtimeModelInputAudioTranscriptionCompletedEvent,
8787
RealtimeModelItemDeletedEvent,
8888
RealtimeModelItemUpdatedEvent,
89+
RealtimeModelRawServerEvent,
8990
RealtimeModelToolCallEvent,
9091
RealtimeModelTranscriptDeltaEvent,
9192
RealtimeModelTurnEndedEvent,
@@ -447,6 +448,7 @@ async def _cancel_response(self) -> None:
447448
self._ongoing_response = False
448449

449450
async def _handle_ws_event(self, event: dict[str, Any]):
451+
await self._emit_event(RealtimeModelRawServerEvent(data=event))
450452
try:
451453
if "previous_item_id" in event and event["previous_item_id"] is None:
452454
event["previous_item_id"] = "" # TODO (rm) remove

src/agents/realtime/session.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ async def on_event(self, event: RealtimeModelEvent) -> None:
274274
self._stored_exception = event.exception
275275
elif event.type == "other":
276276
pass
277+
elif event.type == "raw_server_event":
278+
pass
277279
else:
278280
assert_never(event)
279281

tests/realtime/test_openai_realtime.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,9 @@ async def test_handle_malformed_json_logs_error_continues(self, model):
180180
# Malformed JSON should not crash the handler
181181
await model._handle_ws_event("invalid json {")
182182

183-
# Should emit error event to listeners
184-
mock_listener.on_event.assert_called_once()
185-
error_event = mock_listener.on_event.call_args[0][0]
183+
# Should emit raw server event and error event to listeners
184+
assert mock_listener.on_event.call_count == 2
185+
error_event = mock_listener.on_event.call_args_list[1][0][0]
186186
assert error_event.type == "error"
187187

188188
@pytest.mark.asyncio
@@ -195,9 +195,9 @@ async def test_handle_invalid_event_schema_logs_error(self, model):
195195

196196
await model._handle_ws_event(invalid_event)
197197

198-
# Should emit error event to listeners
199-
mock_listener.on_event.assert_called_once()
200-
error_event = mock_listener.on_event.call_args[0][0]
198+
# Should emit raw server event and error event to listeners
199+
assert mock_listener.on_event.call_count == 2
200+
error_event = mock_listener.on_event.call_args_list[1][0][0]
201201
assert error_event.type == "error"
202202

203203
@pytest.mark.asyncio
@@ -241,9 +241,9 @@ async def test_handle_audio_delta_event_success(self, model):
241241

242242
await model._handle_ws_event(audio_event)
243243

244-
# Should emit audio event to listeners
245-
mock_listener.on_event.assert_called_once()
246-
emitted_event = mock_listener.on_event.call_args[0][0]
244+
# Should emit raw server event and audio event to listeners
245+
assert mock_listener.on_event.call_count == 2
246+
emitted_event = mock_listener.on_event.call_args_list[1][0][0]
247247
assert isinstance(emitted_event, RealtimeModelAudioEvent)
248248
assert emitted_event.response_id == "resp_123"
249249
assert emitted_event.data == b"test audio" # decoded from base64
@@ -274,9 +274,9 @@ async def test_handle_error_event_success(self, model):
274274

275275
await model._handle_ws_event(error_event)
276276

277-
# Should emit error event to listeners
278-
mock_listener.on_event.assert_called_once()
279-
emitted_event = mock_listener.on_event.call_args[0][0]
277+
# Should emit raw server event and error event to listeners
278+
assert mock_listener.on_event.call_count == 2
279+
emitted_event = mock_listener.on_event.call_args_list[1][0][0]
280280
assert isinstance(emitted_event, RealtimeModelErrorEvent)
281281

282282
@pytest.mark.asyncio
@@ -303,12 +303,12 @@ async def test_handle_tool_call_event_success(self, model):
303303

304304
await model._handle_ws_event(tool_call_event)
305305

306-
# Should emit both item updated and tool call events
307-
assert mock_listener.on_event.call_count == 2
306+
# Should emit raw server event, item updated, and tool call events
307+
assert mock_listener.on_event.call_count == 3
308308

309-
# First should be item updated, second should be tool call
309+
# First should be raw server event, second should be item updated, third should be tool call
310310
calls = mock_listener.on_event.call_args_list
311-
tool_call_emitted = calls[1][0][0]
311+
tool_call_emitted = calls[2][0][0]
312312
assert isinstance(tool_call_emitted, RealtimeModelToolCallEvent)
313313
assert tool_call_emitted.name == "get_weather"
314314
assert tool_call_emitted.arguments == '{"location": "San Francisco"}'

0 commit comments

Comments
 (0)