Skip to content
Open
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
27 changes: 17 additions & 10 deletions src/openai/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,21 @@ class OpenAI(SyncAPIClient):
project: str | None
webhook_secret: str | None
_workload_identity_auth: WorkloadIdentityAuth | None
_websocket_base_url: str | httpx.URL | None

websocket_base_url: str | httpx.URL | None
"""Base URL for WebSocket connections.
@property
def websocket_base_url(self) -> httpx.URL:
"""Base URL for WebSocket connections.

If not specified, the default base URL will be used, with 'wss://' replacing the
'http://' or 'https://' scheme. For example: 'http://example.com' becomes
'wss://example.com'
"""
If not explicitly set, derives from base_url by replacing only the scheme
prefix with 'wss://' or 'ws://'. This correctly handles URLs that contain
'http://' or 'https://' in query parameters or paths.
"""
if self._websocket_base_url is not None:
return httpx.URL(self._websocket_base_url)
scheme = self._base_url.scheme
ws_scheme = "ws" if scheme == "http" else "wss"
return self._base_url.copy_with(scheme=ws_scheme)

def __init__(
self,
Expand Down Expand Up @@ -207,7 +214,7 @@ def __init__(
webhook_secret = os.environ.get("OPENAI_WEBHOOK_SECRET")
self.webhook_secret = webhook_secret

self.websocket_base_url = websocket_base_url
self._websocket_base_url = websocket_base_url

if base_url is None:
base_url = os.environ.get("OPENAI_BASE_URL")
Expand Down Expand Up @@ -557,7 +564,7 @@ def copy(
organization=organization or self.organization,
project=project or self.project,
webhook_secret=webhook_secret or self.webhook_secret,
websocket_base_url=websocket_base_url or self.websocket_base_url,
websocket_base_url=websocket_base_url or self._websocket_base_url,
base_url=base_url or self.base_url,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
http_client=http_client,
Expand Down Expand Up @@ -713,7 +720,7 @@ def __init__(
webhook_secret = os.environ.get("OPENAI_WEBHOOK_SECRET")
self.webhook_secret = webhook_secret

self.websocket_base_url = websocket_base_url
self._websocket_base_url = websocket_base_url
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore AsyncOpenAI.websocket_base_url attribute

Assigning only self._websocket_base_url here removes the public websocket_base_url instance attribute on AsyncOpenAI, but async websocket code still reads self.__client.websocket_base_url (for example in src/openai/resources/realtime/realtime.py and src/openai/resources/responses/responses.py). Because AsyncOpenAI has no corresponding property, those accesses now raise AttributeError at runtime for async clients, breaking realtime/response streaming flows.

Useful? React with 👍 / 👎.


if base_url is None:
base_url = os.environ.get("OPENAI_BASE_URL")
Expand Down Expand Up @@ -1062,7 +1069,7 @@ def copy(
organization=organization or self.organization,
project=project or self.project,
webhook_secret=webhook_secret or self.webhook_secret,
websocket_base_url=websocket_base_url or self.websocket_base_url,
websocket_base_url=websocket_base_url or self._websocket_base_url,
base_url=base_url or self.base_url,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
http_client=http_client,
Expand Down