From fc5e3151d9af922f13dd8149dfc1a50863b5f390 Mon Sep 17 00:00:00 2001 From: markstuart-oai Date: Sat, 26 Sep 2026 21:06:57 +0000 Subject: [PATCH 1/2] test(responses): verify pre-open WebSocket omission handling --- .../responses/test_websocket_preconnect.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 tests/lib/responses/test_websocket_preconnect.py diff --git a/tests/lib/responses/test_websocket_preconnect.py b/tests/lib/responses/test_websocket_preconnect.py new file mode 100644 index 0000000000..10097b3535 --- /dev/null +++ b/tests/lib/responses/test_websocket_preconnect.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import json +from typing import Any, cast + +import httpx2 +import pytest +from websockets.sync.server import ServerConnection + +from openai import OpenAI, AsyncOpenAI, omit, not_given +from openai.types.responses.responses_client_event import ResponseCreate + +from .test_websocket_session import script_server, response_event + + +@pytest.fixture(autouse=True) +def bypass_proxy(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("NO_PROXY", "127.0.0.1") + monkeypatch.setenv("no_proxy", "127.0.0.1") + + +@pytest.mark.parametrize("mode", ["sync", "async"]) +@pytest.mark.parametrize("beta", [False, True], ids=["public", "beta"]) +async def test_preopen_sends_normalize_omissions_without_losing_values_or_order(mode: str, beta: bool) -> None: + received: list[dict[str, Any]] = [] + handled: list[str] = [] + + def script(socket: ServerConnection) -> None: + for _ in range(2): + received.append(json.loads(socket.recv(timeout=5))) + socket.send(json.dumps(response_event("completed", id="resp_queued"))) + socket.close() + + first = { + "type": "response.create", + "model": "gpt-4o-mini", + "instructions": omit, + "previous_response_id": not_given, + "input": [], + "tools": [], + "store": False, + "metadata": {}, + "tool_choice": None, + "future_option": {"enabled": False, "value": None, "empty": []}, + } + second = ResponseCreate(type="response.create", model="gpt-4o-mini", input="second") + + with script_server(script) as url: + if mode == "sync": + with OpenAI( + api_key="fake-preconnect-key", base_url=url, http_client=httpx2.Client(trust_env=False) + ) as client: + resource = client.beta.responses if beta else client.responses + manager = resource.connect() + manager.on("response.completed", lambda event: handled.append(event.response.id)) + manager.send(cast(Any, first)) + manager.send(second) + with manager as connection: + connection.dispatch_events() + else: + async with AsyncOpenAI( + api_key="fake-preconnect-key", base_url=url, http_client=httpx2.AsyncClient(trust_env=False) + ) as client: + resource = client.beta.responses if beta else client.responses + manager = resource.connect() + manager.on("response.completed", lambda event: handled.append(event.response.id)) + # Queueing is synchronous even when opening/reading is async. + manager.send(cast(Any, first)) + manager.send(second) + async with manager as connection: + await connection.dispatch_events() + + assert received == [ + { + "type": "response.create", + "model": "gpt-4o-mini", + "input": [], + "tools": [], + "store": False, + "metadata": {}, + "tool_choice": None, + "future_option": {"enabled": False, "value": None, "empty": []}, + }, + {"type": "response.create", "model": "gpt-4o-mini", "input": "second"}, + ] + assert handled == ["resp_queued"] From e515048e767a2d89f98fa2c47972c607e9e0bf7a Mon Sep 17 00:00:00 2001 From: markstuart-oai Date: Sat, 26 Sep 2026 21:14:27 +0000 Subject: [PATCH 2/2] test(responses): use matching beta models and type preconnect handlers --- .../responses/test_websocket_preconnect.py | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/tests/lib/responses/test_websocket_preconnect.py b/tests/lib/responses/test_websocket_preconnect.py index 10097b3535..9205d8544c 100644 --- a/tests/lib/responses/test_websocket_preconnect.py +++ b/tests/lib/responses/test_websocket_preconnect.py @@ -8,7 +8,14 @@ from websockets.sync.server import ServerConnection from openai import OpenAI, AsyncOpenAI, omit, not_given +from openai.resources.beta.responses.responses import ( + ResponsesConnectionManager as BetaResponsesConnectionManager, + AsyncResponsesConnectionManager as BetaAsyncResponsesConnectionManager, +) +from openai.types.beta.beta_responses_client_event import ResponseCreate as BetaResponseCreate from openai.types.responses.responses_client_event import ResponseCreate +from openai.types.beta.beta_response_completed_event import BetaResponseCompletedEvent +from openai.types.responses.response_completed_event import ResponseCompletedEvent from .test_websocket_session import script_server, response_event @@ -31,7 +38,10 @@ def script(socket: ServerConnection) -> None: socket.send(json.dumps(response_event("completed", id="resp_queued"))) socket.close() - first = { + def handle_completed(event: ResponseCompletedEvent | BetaResponseCompletedEvent) -> None: + handled.append(event.response.id) + + first: dict[str, Any] = { "type": "response.create", "model": "gpt-4o-mini", "instructions": omit, @@ -43,32 +53,34 @@ def script(socket: ServerConnection) -> None: "tool_choice": None, "future_option": {"enabled": False, "value": None, "empty": []}, } - second = ResponseCreate(type="response.create", model="gpt-4o-mini", input="second") - with script_server(script) as url: if mode == "sync": with OpenAI( api_key="fake-preconnect-key", base_url=url, http_client=httpx2.Client(trust_env=False) ) as client: - resource = client.beta.responses if beta else client.responses - manager = resource.connect() - manager.on("response.completed", lambda event: handled.append(event.response.id)) + manager = client.beta.responses.connect() if beta else client.responses.connect() manager.send(cast(Any, first)) - manager.send(second) + if isinstance(manager, BetaResponsesConnectionManager): + manager.send(BetaResponseCreate(type="response.create", model="gpt-4o-mini", input="second")) + else: + manager.send(ResponseCreate(type="response.create", model="gpt-4o-mini", input="second")) + manager.on("response.completed", handle_completed) with manager as connection: connection.dispatch_events() else: async with AsyncOpenAI( api_key="fake-preconnect-key", base_url=url, http_client=httpx2.AsyncClient(trust_env=False) - ) as client: - resource = client.beta.responses if beta else client.responses - manager = resource.connect() - manager.on("response.completed", lambda event: handled.append(event.response.id)) + ) as async_client: # Queueing is synchronous even when opening/reading is async. - manager.send(cast(Any, first)) - manager.send(second) - async with manager as connection: - await connection.dispatch_events() + async_manager = async_client.beta.responses.connect() if beta else async_client.responses.connect() + async_manager.send(cast(Any, first)) + if isinstance(async_manager, BetaAsyncResponsesConnectionManager): + async_manager.send(BetaResponseCreate(type="response.create", model="gpt-4o-mini", input="second")) + else: + async_manager.send(ResponseCreate(type="response.create", model="gpt-4o-mini", input="second")) + async_manager.on("response.completed", handle_completed) + async with async_manager as async_connection: + await async_connection.dispatch_events() assert received == [ {