Skip to content

Commit 7f203fd

Browse files
fix(api): retain WebSocket endpoint paths and query parameters (openai#3999)
## Summary Preserve WebSocket endpoint paths and query parameters when using a custom base URL. ## Changes - Keep the Responses endpoint when the base URL includes a query string. - Preserve query parameters on an explicit WebSocket base URL for Responses, Realtime, and Live. - Let connection query parameters override the selected URL, and the selected URL override client defaults. Preserve repeated URL query values. Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
1 parent 50f95ac commit 7f203fd

9 files changed

Lines changed: 251 additions & 86 deletions

File tree

‎src/openai/lib/azure.py‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -521,13 +521,18 @@ def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx2.UR
521521

522522
if self.websocket_base_url is not None:
523523
base_url = normalize_httpx_url(self.websocket_base_url)
524-
merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime"
524+
path, separator, url_query = base_url.raw_path.partition(b"?")
525+
merge_raw_path = path.rstrip(b"/") + b"/realtime" + separator + url_query
525526
realtime_url = base_url.copy_with(raw_path=merge_raw_path)
526527
else:
527528
base_url = self._prepare_url("/realtime")
528529
realtime_url = base_url.copy_with(scheme="wss")
529530

530-
url = realtime_url.copy_with(params={**query})
531+
url = realtime_url.copy_with(
532+
params=httpx2.QueryParams(self.qs.stringify(cast(Any, self.default_query)))
533+
.merge(realtime_url.params)
534+
.merge(cast(Any, query))
535+
)
531536
return url, auth_headers
532537

533538

@@ -883,11 +888,16 @@ async def _configure_realtime(self, model: str, extra_query: Query) -> tuple[htt
883888

884889
if self.websocket_base_url is not None:
885890
base_url = normalize_httpx_url(self.websocket_base_url)
886-
merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime"
891+
path, separator, url_query = base_url.raw_path.partition(b"?")
892+
merge_raw_path = path.rstrip(b"/") + b"/realtime" + separator + url_query
887893
realtime_url = base_url.copy_with(raw_path=merge_raw_path)
888894
else:
889895
base_url = self._prepare_url("/realtime")
890896
realtime_url = base_url.copy_with(scheme="wss")
891897

892-
url = realtime_url.copy_with(params={**query})
898+
url = realtime_url.copy_with(
899+
params=httpx2.QueryParams(self.qs.stringify(cast(Any, self.default_query)))
900+
.merge(realtime_url.params)
901+
.merge(cast(Any, query))
902+
)
893903
return url, auth_headers

‎src/openai/resources/beta/realtime/realtime.py‎

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -364,12 +364,19 @@ async def __aenter__(self) -> AsyncRealtimeConnection:
364364

365365
url, auth_headers = await self.__client._configure_realtime(self.__model, extra_query)
366366
else:
367-
url = self._prepare_url().copy_with(
368-
params={
369-
**self.__client.base_url.params,
370-
"model": self.__model,
371-
**extra_query,
372-
},
367+
url = self._prepare_url()
368+
url = url.copy_with(
369+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
370+
.merge(url.params)
371+
.merge(
372+
cast(
373+
Any,
374+
{
375+
"model": self.__model,
376+
**extra_query,
377+
},
378+
)
379+
),
373380
)
374381
log.debug("Connecting to WebSocket API")
375382
if self.__websocket_connection_options:
@@ -400,7 +407,8 @@ def _prepare_url(self) -> httpx2.URL:
400407
else:
401408
base_url = self.__client._base_url.copy_with(scheme="wss")
402409

403-
merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime"
410+
path, separator, query = base_url.raw_path.partition(b"?")
411+
merge_raw_path = path.rstrip(b"/") + b"/realtime" + separator + query
404412
return base_url.copy_with(raw_path=merge_raw_path)
405413

406414
async def __aexit__(
@@ -547,12 +555,19 @@ def __enter__(self) -> RealtimeConnection:
547555
if is_azure_client(self.__client):
548556
url, auth_headers = self.__client._configure_realtime(self.__model, extra_query)
549557
else:
550-
url = self._prepare_url().copy_with(
551-
params={
552-
**self.__client.base_url.params,
553-
"model": self.__model,
554-
**extra_query,
555-
},
558+
url = self._prepare_url()
559+
url = url.copy_with(
560+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
561+
.merge(url.params)
562+
.merge(
563+
cast(
564+
Any,
565+
{
566+
"model": self.__model,
567+
**extra_query,
568+
},
569+
)
570+
),
556571
)
557572
log.debug("Connecting to WebSocket API")
558573
if self.__websocket_connection_options:
@@ -583,7 +598,8 @@ def _prepare_url(self) -> httpx2.URL:
583598
else:
584599
base_url = self.__client._base_url.copy_with(scheme="wss")
585600

586-
merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime"
601+
path, separator, query = base_url.raw_path.partition(b"?")
602+
merge_raw_path = path.rstrip(b"/") + b"/realtime" + separator + query
587603
return base_url.copy_with(raw_path=merge_raw_path)
588604

589605
def __exit__(

‎src/openai/resources/beta/responses/responses.py‎

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4674,11 +4674,11 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
46744674
except ImportError as exc:
46754675
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
46764676

4677-
url = self._prepare_url().copy_with(
4678-
params={
4679-
**self.__client.base_url.params,
4680-
**extra_query,
4681-
},
4677+
url = self._prepare_url()
4678+
url = url.copy_with(
4679+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
4680+
.merge(url.params)
4681+
.merge(cast(Any, extra_query)),
46824682
)
46834683
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
46844684
options = await self.__client._prepare_options(
@@ -4722,7 +4722,8 @@ def _prepare_url(self) -> httpx2.URL:
47224722
ws_scheme = "ws" if scheme == "http" else "wss"
47234723
base_url = self.__client._base_url.copy_with(scheme=ws_scheme)
47244724

4725-
merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses"
4725+
path, separator, query = base_url.raw_path.partition(b"?")
4726+
merge_raw_path = path.rstrip(b"/") + b"/responses" + separator + query
47264727
return base_url.copy_with(raw_path=merge_raw_path)
47274728

47284729
async def __aexit__(
@@ -5160,11 +5161,11 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
51605161
except ImportError as exc:
51615162
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
51625163

5163-
url = self._prepare_url().copy_with(
5164-
params={
5165-
**self.__client.base_url.params,
5166-
**extra_query,
5167-
},
5164+
url = self._prepare_url()
5165+
url = url.copy_with(
5166+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
5167+
.merge(url.params)
5168+
.merge(cast(Any, extra_query)),
51685169
)
51695170
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
51705171
options = self.__client._prepare_options(
@@ -5208,7 +5209,8 @@ def _prepare_url(self) -> httpx2.URL:
52085209
ws_scheme = "ws" if scheme == "http" else "wss"
52095210
base_url = self.__client._base_url.copy_with(scheme=ws_scheme)
52105211

5211-
merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses"
5212+
path, separator, query = base_url.raw_path.partition(b"?")
5213+
merge_raw_path = path.rstrip(b"/") + b"/responses" + separator + query
52125214
return base_url.copy_with(raw_path=merge_raw_path)
52135215

52145216
def __exit__(

‎src/openai/resources/live/forks.py‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -542,11 +542,11 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
542542
except ImportError as exc:
543543
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
544544

545-
url = self._prepare_url().copy_with(
546-
params={
547-
**self.__client.base_url.params,
548-
**extra_query,
549-
},
545+
url = self._prepare_url()
546+
url = url.copy_with(
547+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
548+
.merge(url.params)
549+
.merge(cast(Any, extra_query)),
550550
)
551551
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
552552
options = await self.__client._prepare_options(
@@ -1025,11 +1025,11 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
10251025
except ImportError as exc:
10261026
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
10271027

1028-
url = self._prepare_url().copy_with(
1029-
params={
1030-
**self.__client.base_url.params,
1031-
**extra_query,
1032-
},
1028+
url = self._prepare_url()
1029+
url = url.copy_with(
1030+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
1031+
.merge(url.params)
1032+
.merge(cast(Any, extra_query)),
10331033
)
10341034
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
10351035
options = self.__client._prepare_options(

‎src/openai/resources/live/live.py‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -761,11 +761,11 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
761761
except ImportError as exc:
762762
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
763763

764-
url = self._prepare_url().copy_with(
765-
params={
766-
**self.__client.base_url.params,
767-
**extra_query,
768-
},
764+
url = self._prepare_url()
765+
url = url.copy_with(
766+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
767+
.merge(url.params)
768+
.merge(cast(Any, extra_query)),
769769
)
770770
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
771771
options = await self.__client._prepare_options(
@@ -1240,11 +1240,11 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
12401240
except ImportError as exc:
12411241
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
12421242

1243-
url = self._prepare_url().copy_with(
1244-
params={
1245-
**self.__client.base_url.params,
1246-
**extra_query,
1247-
},
1243+
url = self._prepare_url()
1244+
url = url.copy_with(
1245+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
1246+
.merge(url.params)
1247+
.merge(cast(Any, extra_query)),
12481248
)
12491249
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
12501250
options = self.__client._prepare_options(

‎src/openai/resources/live/sideband.py‎

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -549,12 +549,19 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
549549
except ImportError as exc:
550550
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
551551

552-
url = self._prepare_url().copy_with(
553-
params={
554-
**self.__client.base_url.params,
555-
**({"graceful_close": self.__graceful_close} if self.__graceful_close is not omit else {}),
556-
**extra_query,
557-
},
552+
url = self._prepare_url()
553+
url = url.copy_with(
554+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
555+
.merge(url.params)
556+
.merge(
557+
cast(
558+
Any,
559+
{
560+
**({"graceful_close": self.__graceful_close} if self.__graceful_close is not omit else {}),
561+
**extra_query,
562+
},
563+
)
564+
),
558565
)
559566
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
560567
options = await self.__client._prepare_options(
@@ -1037,12 +1044,19 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
10371044
except ImportError as exc:
10381045
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
10391046

1040-
url = self._prepare_url().copy_with(
1041-
params={
1042-
**self.__client.base_url.params,
1043-
**({"graceful_close": self.__graceful_close} if self.__graceful_close is not omit else {}),
1044-
**extra_query,
1045-
},
1047+
url = self._prepare_url()
1048+
url = url.copy_with(
1049+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
1050+
.merge(url.params)
1051+
.merge(
1052+
cast(
1053+
Any,
1054+
{
1055+
**({"graceful_close": self.__graceful_close} if self.__graceful_close is not omit else {}),
1056+
**extra_query,
1057+
},
1058+
)
1059+
),
10461060
)
10471061
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
10481062
options = self.__client._prepare_options(

‎src/openai/resources/realtime/realtime.py‎

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -717,12 +717,19 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
717717
url, auth_headers = await self.__client._configure_realtime(model, extra_query)
718718
prepared_headers: Headers = extra_headers
719719
else:
720-
url = self._prepare_url().copy_with(
721-
params={
722-
**self.__client.base_url.params,
723-
**({"model": self.__model} if self.__model is not omit else {}),
724-
**extra_query,
725-
},
720+
url = self._prepare_url()
721+
url = url.copy_with(
722+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
723+
.merge(url.params)
724+
.merge(
725+
cast(
726+
Any,
727+
{
728+
**({"model": self.__model} if self.__model is not omit else {}),
729+
**extra_query,
730+
},
731+
)
732+
),
726733
)
727734
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
728735
options = await self.__client._prepare_options(
@@ -1223,12 +1230,19 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
12231230
url, auth_headers = self.__client._configure_realtime(model, extra_query)
12241231
prepared_headers: Headers = extra_headers
12251232
else:
1226-
url = self._prepare_url().copy_with(
1227-
params={
1228-
**self.__client.base_url.params,
1229-
**({"model": self.__model} if self.__model is not omit else {}),
1230-
**extra_query,
1231-
},
1233+
url = self._prepare_url()
1234+
url = url.copy_with(
1235+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
1236+
.merge(url.params)
1237+
.merge(
1238+
cast(
1239+
Any,
1240+
{
1241+
**({"model": self.__model} if self.__model is not omit else {}),
1242+
**extra_query,
1243+
},
1244+
)
1245+
),
12321246
)
12331247
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
12341248
options = self.__client._prepare_options(

‎src/openai/resources/responses/responses.py‎

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4525,11 +4525,11 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
45254525
except ImportError as exc:
45264526
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
45274527

4528-
url = self._prepare_url().copy_with(
4529-
params={
4530-
**self.__client.base_url.params,
4531-
**extra_query,
4532-
},
4528+
url = self._prepare_url()
4529+
url = url.copy_with(
4530+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
4531+
.merge(url.params)
4532+
.merge(cast(Any, extra_query)),
45334533
)
45344534
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
45354535
options = await self.__client._prepare_options(
@@ -4573,7 +4573,8 @@ def _prepare_url(self) -> httpx2.URL:
45734573
ws_scheme = "ws" if scheme == "http" else "wss"
45744574
base_url = self.__client._base_url.copy_with(scheme=ws_scheme)
45754575

4576-
merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses"
4576+
path, separator, query = base_url.raw_path.partition(b"?")
4577+
merge_raw_path = path.rstrip(b"/") + b"/responses" + separator + query
45774578
return base_url.copy_with(raw_path=merge_raw_path)
45784579

45794580
async def __aexit__(
@@ -5023,11 +5024,11 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
50235024
except ImportError as exc:
50245025
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
50255026

5026-
url = self._prepare_url().copy_with(
5027-
params={
5028-
**self.__client.base_url.params,
5029-
**extra_query,
5030-
},
5027+
url = self._prepare_url()
5028+
url = url.copy_with(
5029+
params=httpx2.QueryParams(self.__client.qs.stringify(cast(Any, self.__client.default_query)))
5030+
.merge(url.params)
5031+
.merge(cast(Any, extra_query)),
50315032
)
50325033
url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
50335034
options = self.__client._prepare_options(
@@ -5071,7 +5072,8 @@ def _prepare_url(self) -> httpx2.URL:
50715072
ws_scheme = "ws" if scheme == "http" else "wss"
50725073
base_url = self.__client._base_url.copy_with(scheme=ws_scheme)
50735074

5074-
merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses"
5075+
path, separator, query = base_url.raw_path.partition(b"?")
5076+
merge_raw_path = path.rstrip(b"/") + b"/responses" + separator + query
50755077
return base_url.copy_with(raw_path=merge_raw_path)
50765078

50775079
def __exit__(

0 commit comments

Comments
 (0)