From 335c6f1d65acbf250f179c038bd611ea02b37fbd Mon Sep 17 00:00:00 2001 From: nightcityblade Date: Thu, 21 May 2026 23:15:41 +0800 Subject: [PATCH] fix: preserve query params in realtime websocket URLs --- src/openai/_base_client.py | 13 ++++++++++-- .../resources/beta/realtime/realtime.py | 8 +++---- src/openai/resources/realtime/realtime.py | 8 +++---- tests/test_client.py | 21 +++++++++++++++++++ 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 17863bc067..da524ccfa4 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -399,9 +399,18 @@ def __init__( ) def _enforce_trailing_slash(self, url: URL) -> URL: - if url.raw_path.endswith(b"/"): + raw_path = url.raw_path + if url.query: + raw_path = raw_path[: -(len(url.query) + 1)] + + if raw_path.endswith(b"/"): return url - return url.copy_with(raw_path=url.raw_path + b"/") + + raw_path += b"/" + if url.query: + raw_path += b"?" + url.query + + return url.copy_with(raw_path=raw_path) def _make_status_error_from_response( self, diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index 4fa35963b6..f3abd3b6d3 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -399,8 +399,8 @@ def _prepare_url(self) -> httpx.URL: else: base_url = self.__client._base_url.copy_with(scheme="wss") - merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" - return base_url.copy_with(raw_path=merge_raw_path) + path = base_url.path.rstrip("/") + "/realtime" + return base_url.copy_with(path=path) async def __aexit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None @@ -582,8 +582,8 @@ def _prepare_url(self) -> httpx.URL: else: base_url = self.__client._base_url.copy_with(scheme="wss") - merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" - return base_url.copy_with(raw_path=merge_raw_path) + path = base_url.path.rstrip("/") + "/realtime" + return base_url.copy_with(path=path) def __exit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 44f14cd3aa..2f91d2c93f 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -410,8 +410,8 @@ def _prepare_url(self) -> httpx.URL: else: base_url = self.__client._base_url.copy_with(scheme="wss") - merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" - return base_url.copy_with(raw_path=merge_raw_path) + path = base_url.path.rstrip("/") + "/realtime" + return base_url.copy_with(path=path) async def __aexit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None @@ -599,8 +599,8 @@ def _prepare_url(self) -> httpx.URL: else: base_url = self.__client._base_url.copy_with(scheme="wss") - merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" - return base_url.copy_with(raw_path=merge_raw_path) + path = base_url.path.rstrip("/") + "/realtime" + return base_url.copy_with(path=path) def __exit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None diff --git a/tests/test_client.py b/tests/test_client.py index 396f6dea99..4243d7f037 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -696,6 +696,27 @@ def test_base_url_env(self) -> None: client = OpenAI(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" + def test_realtime_url_preserves_http_query_param(self) -> None: + client = OpenAI( + base_url="https://proxy.com/forward?target=http://backend.internal/v1", + api_key=api_key, + _strict_response_validation=True, + ) + assert client.base_url == "https://proxy.com/forward/?target=http://backend.internal/v1" + + url = ( + client.realtime.connect(model="gpt-realtime") + ._prepare_url() + .copy_with( + params={**client.base_url.params, "model": "gpt-realtime"}, + ) + ) + + assert str(url) == ( + "wss://proxy.com/forward/realtime?target=http%3A%2F%2Fbackend.internal%2Fv1&model=gpt-realtime" + ) + client.close() + @pytest.mark.parametrize( "client", [