Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 11 additions & 15 deletions src/openai/resources/realtime/realtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down
23 changes: 23 additions & 0 deletions tests/api_resources/test_realtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"}