diff --git a/src/openai/types/websocket_connection_options.py b/src/openai/types/websocket_connection_options.py index 0ddbf5af49..d8bfabad18 100644 --- a/src/openai/types/websocket_connection_options.py +++ b/src/openai/types/websocket_connection_options.py @@ -5,17 +5,17 @@ from typing import TYPE_CHECKING from typing_extensions import Sequence, TypeAlias, TypedDict -__all__ = ["WebSocketConnectionOptions", "WebsocketConnectionOptions"] +__all__ = ["SyncWebSocketConnectionOptions", "WebSocketConnectionOptions", "WebsocketConnectionOptions"] if TYPE_CHECKING: from websockets import Subprotocol from websockets.extensions import ClientExtensionFactory -class WebSocketConnectionOptions(TypedDict, total=False): - """WebSocket connection options copied from `websockets`. +class SyncWebSocketConnectionOptions(TypedDict, total=False): + """Native options for synchronous WebSocket connections. - For example: https://websockets.readthedocs.io/en/stable/reference/asyncio/client.html#websockets.asyncio.client.connect + See https://websockets.readthedocs.io/en/stable/reference/sync/client.html#websockets.sync.client.connect """ extensions: Sequence[ClientExtensionFactory] | None @@ -32,7 +32,14 @@ class WebSocketConnectionOptions(TypedDict, total=False): """Maximum size of incoming messages in bytes. None disables the limit.""" max_queue: int | None | tuple[int | None, int | None] - """High-water mark of the buffer where frames are received. It defaults to 16 frames. The low-water mark defaults to max_queue // 4. You may pass a (high, low) tuple to set the high-water and low-water marks. If you want to disable flow control entirely, you may set it to None, although that’s a bad idea.""" + """For synchronous connections, this requires websockets >=14. High-water mark of the buffer where frames are received. It defaults to 16 frames. The low-water mark defaults to max_queue // 4. You may pass a (high, low) tuple to set the high-water and low-water marks. If you want to disable flow control entirely, you may set it to None, although that’s a bad idea.""" + + +class WebSocketConnectionOptions(SyncWebSocketConnectionOptions, total=False): + """Native options for asynchronous WebSocket connections. + + See https://websockets.readthedocs.io/en/stable/reference/asyncio/client.html#websockets.asyncio.client.connect + """ write_limit: int | tuple[int, int | None] """High-water mark of write buffer in bytes. It is passed to set_write_buffer_limits(). It defaults to 32 KiB. You may pass a (high, low) tuple to set the high-water and low-water marks.""" diff --git a/tests/test_websocket_connection_options.py b/tests/test_websocket_connection_options.py index 122ee10078..468b797c8a 100644 --- a/tests/test_websocket_connection_options.py +++ b/tests/test_websocket_connection_options.py @@ -1,3 +1,7 @@ +# An invalid native-sync literal becoming accepted must fail the type check. +# pyright: reportUnnecessaryTypeIgnoreComment=true +from typing import TYPE_CHECKING + from openai import types from openai.types import websocket_connection_options @@ -18,3 +22,30 @@ def test_beta_realtime_import_still_works_with_old_alias() -> None: assert Realtime.__name__ == "Realtime" assert AsyncRealtime.__name__ == "AsyncRealtime" + + +if TYPE_CHECKING: + from openai import AsyncOpenAI + from openai.types.websocket_connection_options import SyncWebSocketConnectionOptions + + def native_options_contract( + client: AsyncOpenAI, + ) -> tuple[SyncWebSocketConnectionOptions, SyncWebSocketConnectionOptions]: + # All synchronous native options remain representable. max_queue requires + # websockets >=14 for a synchronous native connection. + valid_sync: SyncWebSocketConnectionOptions = { + "extensions": None, + "subprotocols": None, + "compression": None, + "max_size": None, + "max_queue": (8, 2), + } + invalid_sync: SyncWebSocketConnectionOptions = {"write_limit": 65536} # pyright: ignore[reportAssignmentType] + + # The existing names still support async write limits on all supported + # websocket versions, including through the generated public entrypoint. + original: types.WebSocketConnectionOptions = {"write_limit": 65536, "max_queue": 8} + legacy: types.WebsocketConnectionOptions = {"write_limit": (65536, 16384)} + client.realtime.connect(model="test", websocket_connection_options=original) + client.responses.connect(websocket_connection_options=legacy) + return valid_sync, invalid_sync