From 6e65f7570ae4b62a466eafe44f4c765569517292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=AF=E5=9F=BA=E9=AD=81?= <1412414664@qq.com> Date: Wed, 24 Jun 2026 05:54:05 +0800 Subject: [PATCH] fix: transform realtime queued events --- src/openai/resources/realtime/realtime.py | 26 ++++++++++------------- tests/api_resources/test_realtime.py | 23 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index e4c5bd8163..2958cf5a42 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -65,6 +65,14 @@ log: logging.Logger = logging.getLogger(__name__) +def _serialize_realtime_client_event(event: RealtimeClientEvent | RealtimeClientEventParam) -> str: + return ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, RealtimeClientEventParam)) + ) + + class Realtime(SyncAPIResource): @cached_property def client_secrets(self) -> ClientSecrets: @@ -601,11 +609,7 @@ def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: This can be called before entering the context manager. Queued messages are automatically sent once the WebSocket connection opens. """ - data = ( - event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) - if isinstance(event, BaseModel) - else json.dumps(event) - ) + data = _serialize_realtime_client_event(event) self.__send_queue.enqueue(data) def on( @@ -823,11 +827,7 @@ def recv_bytes(self) -> bytes: return message def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: - data = ( - event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) - if isinstance(event, BaseModel) - else json.dumps(maybe_transform(event, RealtimeClientEventParam)) - ) + data = _serialize_realtime_client_event(event) if self._is_reconnecting: self._send_queue.enqueue(data) return @@ -1069,11 +1069,7 @@ def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: This can be called before entering the context manager. Queued messages are automatically sent once the WebSocket connection opens. """ - data = ( - event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) - if isinstance(event, BaseModel) - else json.dumps(event) - ) + data = _serialize_realtime_client_event(event) self.__send_queue.enqueue(data) def on( diff --git a/tests/api_resources/test_realtime.py b/tests/api_resources/test_realtime.py index 2b0c7f7d8d..f7970fa8fa 100644 --- a/tests/api_resources/test_realtime.py +++ b/tests/api_resources/test_realtime.py @@ -3,17 +3,40 @@ from __future__ import annotations import os +import json import pytest +from openai import OpenAI, AsyncOpenAI, omit + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestRealtime: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @parametrize + def test_connection_manager_send_strips_omit_values(self, client: OpenAI) -> None: + manager = client.realtime.connect(model="gpt-4o-realtime-preview") + + manager.send({"type": "response.cancel", "event_id": omit}) + + queued = manager._RealtimeConnectionManager__send_queue.drain() + assert len(queued) == 1 + assert json.loads(queued[0]) == {"type": "response.cancel"} + class TestAsyncRealtime: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + + @parametrize + async def test_connection_manager_send_strips_omit_values(self, async_client: AsyncOpenAI) -> None: + manager = async_client.realtime.connect(model="gpt-4o-realtime-preview") + + manager.send({"type": "response.cancel", "event_id": omit}) + + queued = manager._AsyncRealtimeConnectionManager__send_queue.drain() + assert len(queued) == 1 + assert json.loads(queued[0]) == {"type": "response.cancel"}