From ab791decfdc3dfd0c9c771fbbe4b3bd3ca7579d5 Mon Sep 17 00:00:00 2001 From: Yash Hayaran Date: Wed, 8 Jul 2026 13:02:33 +0530 Subject: [PATCH 1/6] Add mint API key support for session authentication in realtime client - Introduced `--mint-api-key` argument for specifying the API key. - Implemented `_mint_auth_headers` function to include the API key in session requests. - Updated session initialization and WebSocket connection methods to utilize the client secret token. - Enhanced error handling for session authentication failures. --- riva/client/argparse_utils.py | 5 ++ riva/client/realtime.py | 109 ++++++++++++++++++++++++----- scripts/tts/realtime_tts_client.py | 6 +- 3 files changed, 101 insertions(+), 19 deletions(-) diff --git a/riva/client/argparse_utils.py b/riva/client/argparse_utils.py index d1cdf98..8d1f6e8 100644 --- a/riva/client/argparse_utils.py +++ b/riva/client/argparse_utils.py @@ -189,4 +189,9 @@ def add_connection_argparse_parameters(parser: argparse.ArgumentParser) -> argpa def add_realtime_config_argparse_parameters(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: parser.add_argument("--endpoint", default="/v1/realtime", help="Endpoint to WebSocket server endpoint.") parser.add_argument("--query-params", default="intent=transcription", help="Query parameters to WebSocket server endpoint.") + parser.add_argument( + "--mint-api-key", + default=None, + help="API key for POST /v1/realtime/*_sessions (Authorization: Bearer).", + ) return parser diff --git a/riva/client/realtime.py b/riva/client/realtime.py index bd41313..0d091b5 100644 --- a/riva/client/realtime.py +++ b/riva/client/realtime.py @@ -21,6 +21,69 @@ logger = logging.getLogger(__name__) +def _mint_auth_headers(args: argparse.Namespace) -> Dict[str, str]: + """Build HTTP headers for session mint endpoints, including optional tier-1 auth.""" + headers = {"Content-Type": "application/json"} + api_key = (getattr(args, "mint_api_key", None) or "").strip() + if api_key: + headers["Authorization"] = f"Bearer {api_key}" + return headers + + +def _server_error_detail(response) -> str: + """Extract the FastAPI ``detail`` message from an error response, falling back to raw text.""" + try: + payload = response.json() + except ValueError: + return response.text.strip() + if isinstance(payload, dict) and payload.get("detail"): + return str(payload["detail"]) + return response.text.strip() + + +def _raise_for_session_status(response, provided_key: bool) -> None: + """Raise a clear, actionable error when the session mint request fails.""" + if response.status_code == 200: + return + if response.status_code in (401, 403): + if provided_key: + hint = "The --mint-api-key value was rejected by the server. Verify it matches the server's REALTIME_AUTH_MINT_API_KEY." + else: + hint = "This server requires an API key on the session endpoint. Pass it with --mint-api-key ." + raise Exception( + f"Session authentication failed (HTTP {response.status_code}: {_server_error_detail(response)}). {hint}" + ) + raise Exception( + f"Failed to initialize session. Status: {response.status_code}, Error: {_server_error_detail(response)}" + ) + + +def _extract_client_secret_token(session_data: Dict[str, Any]) -> str: + """Return the ephemeral client_secret token from a session-creation response.""" + client_secret = session_data.get("client_secret") + if not isinstance(client_secret, dict): + raise ValueError("Session response missing client_secret") + token = client_secret.get("value") + if not token: + raise ValueError("Session response missing client_secret.value") + return token + + +def _build_websocket_url( + server: str, + endpoint: str, + query_params: str, + token: str, + use_ssl: bool, +) -> str: + """Build the WebSocket URL with intent and ephemeral token query parameters.""" + query = query_params.strip() + if "token=" not in query: + query = f"{query}&token={token}" if query else f"token={token}" + scheme = "wss" if use_ssl else "ws" + return f"{scheme}://{server}{endpoint}?{query}" + + class RealtimeClientASR: """Client for real-time transcription via WebSocket connection.""" @@ -33,6 +96,7 @@ def __init__(self, args: argparse.Namespace): self.args = args self.websocket = None self.session_config = None + self._client_secret_token: Optional[str] = None # Input audio playback self.input_audio_queue = queue.Queue() @@ -50,6 +114,7 @@ async def connect(self): # Initialize session via HTTP POST session_data = await self._initialize_http_session() self.session_config = session_data + self._client_secret_token = _extract_client_secret_token(session_data) # Connect to WebSocket await self._connect_websocket() @@ -67,7 +132,7 @@ async def connect(self): async def _initialize_http_session(self) -> Dict[str, Any]: """Initialize session via HTTP POST request.""" - headers = {"Content-Type": "application/json"} + headers = _mint_auth_headers(self.args) uri = f"http://{self.args.server}/v1/realtime/transcription_sessions" if self.args.use_ssl: uri = f"https://{self.args.server}/v1/realtime/transcription_sessions" @@ -80,11 +145,7 @@ async def _initialize_http_session(self) -> Dict[str, Any]: verify=self.args.ssl_root_cert if self.args.ssl_root_cert else True ) - if response.status_code != 200: - raise Exception( - f"Failed to initialize session. Status: {response.status_code}, " - f"Error: {response.text}" - ) + _raise_for_session_status(response, provided_key=bool(getattr(self.args, "mint_api_key", None))) session_data = response.json() logger.debug("Session initialized: %s", session_data) @@ -92,11 +153,18 @@ async def _initialize_http_session(self) -> Dict[str, Any]: async def _connect_websocket(self): """Connect to WebSocket endpoint.""" + if not self._client_secret_token: + raise ValueError("client_secret token is required before opening the WebSocket") + ssl_context = None - ws_url = f"ws://{self.args.server}{self.args.endpoint}?{self.args.query_params}" + ws_url = _build_websocket_url( + self.args.server, + self.args.endpoint, + self.args.query_params, + self._client_secret_token, + self.args.use_ssl, + ) if self.args.use_ssl: - ws_url = f"wss://{self.args.server}{self.args.endpoint}?{self.args.query_params}" - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) # Load a custom CA certificate bundle if self.args.ssl_root_cert: @@ -536,6 +604,7 @@ def __init__(self, args: argparse.Namespace): self.args = args self.websocket = None self.session_config = None + self._client_secret_token: Optional[str] = None self.audio_data = [] self.is_synthesis_complete = False self.wav_file = None # WAV file handle for streaming write @@ -564,6 +633,7 @@ async def connect(self): logger.info("Initializing HTTP session...") session_data = await self._initialize_http_session() self.session_config = session_data + self._client_secret_token = _extract_client_secret_token(session_data) logger.info("HTTP session initialized successfully") # Connect to WebSocket @@ -593,7 +663,7 @@ async def connect(self): async def _initialize_http_session(self) -> Dict[str, Any]: """Initialize session via HTTP POST request.""" - headers = {"Content-Type": "application/json"} + headers = _mint_auth_headers(self.args) uri = f"http://{self.args.server}/v1/realtime/synthesis_sessions" if self.args.use_ssl: uri = f"https://{self.args.server}/v1/realtime/synthesis_sessions" @@ -631,11 +701,7 @@ async def _initialize_http_session(self) -> Dict[str, Any]: logger.error("HTTP request failed: %s", e) raise - if response.status_code != 200: - raise Exception( - f"Failed to initialize session. Status: {response.status_code}, " - f"Error: {response.text}" - ) + _raise_for_session_status(response, provided_key=bool(getattr(self.args, "mint_api_key", None))) session_data = response.json() logger.info("Session initialized: %s", session_data) @@ -643,11 +709,18 @@ async def _initialize_http_session(self) -> Dict[str, Any]: async def _connect_websocket(self): """Connect to WebSocket endpoint.""" + if not self._client_secret_token: + raise ValueError("client_secret token is required before opening the WebSocket") + ssl_context = None - ws_url = f"ws://{self.args.server}{self.args.endpoint}?{self.args.query_params}" + ws_url = _build_websocket_url( + self.args.server, + self.args.endpoint, + self.args.query_params, + self._client_secret_token, + self.args.use_ssl, + ) if self.args.use_ssl: - ws_url = f"wss://{self.args.server}{self.args.endpoint}?{self.args.query_params}" - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) if self.args.ssl_root_cert: ssl_context.load_verify_locations(self.args.ssl_root_cert) diff --git a/scripts/tts/realtime_tts_client.py b/scripts/tts/realtime_tts_client.py index 3144450..25fbfda 100644 --- a/scripts/tts/realtime_tts_client.py +++ b/scripts/tts/realtime_tts_client.py @@ -20,7 +20,10 @@ import websockets from websockets.exceptions import WebSocketException -from riva.client.argparse_utils import add_connection_argparse_parameters +from riva.client.argparse_utils import ( + add_connection_argparse_parameters, + add_realtime_config_argparse_parameters, +) try: from riva.client.argparse_utils import cli_main except ImportError: @@ -154,6 +157,7 @@ def parse_args() -> argparse.Namespace: # Add connection parameters parser = add_connection_argparse_parameters(parser) + parser = add_realtime_config_argparse_parameters(parser) # Override default server for realtime TTS (WebSocket endpoint, not gRPC) parser.set_defaults(server="localhost:9000") From 1b8ec310afb5020c9bb5687b0ca10ed72c625892 Mon Sep 17 00:00:00 2001 From: Yash Hayaran Date: Thu, 23 Jul 2026 14:49:15 +0530 Subject: [PATCH 2/6] Refactor WebSocket connection in RealtimeClient to use subprotocols - Updated `_build_websocket_url` to remove token from query parameters. - Introduced `_websocket_subprotocols` function to handle Sec-WebSocket-Protocol entries. - Modified WebSocket connection methods in `RealtimeClientASR` and `RealtimeClientTTS` to include subprotocols for enhanced security and token handling. --- riva/client/realtime.py | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/riva/client/realtime.py b/riva/client/realtime.py index 0d091b5..a2209f5 100644 --- a/riva/client/realtime.py +++ b/riva/client/realtime.py @@ -68,20 +68,31 @@ def _extract_client_secret_token(session_data: Dict[str, Any]) -> str: raise ValueError("Session response missing client_secret.value") return token +TOKEN_SUBPROTO_PREFIX = "realtime-token." +REALTIME_SUBPROTOCOL = "realtime.v1" def _build_websocket_url( server: str, endpoint: str, query_params: str, - token: str, use_ssl: bool, ) -> str: - """Build the WebSocket URL with intent and ephemeral token query parameters.""" + """Build the WebSocket URL (intent only; auth token goes in Sec-WebSocket-Protocol).""" query = query_params.strip() - if "token=" not in query: - query = f"{query}&token={token}" if query else f"token={token}" scheme = "wss" if use_ssl else "ws" - return f"{scheme}://{server}{endpoint}?{query}" + if query: + return f"{scheme}://{server}{endpoint}?{query}" + return f"{scheme}://{server}{endpoint}" + + +def _websocket_subprotocols(token: str) -> List[str]: + """Return Sec-WebSocket-Protocol entries for the realtime handshake. + + Offers ``realtime.v1`` plus ``realtime-token.``. The server + validates the token entry and echoes back ``realtime.v1`` so the secret + never appears in the response headers. + """ + return [REALTIME_SUBPROTOCOL, f"{TOKEN_SUBPROTO_PREFIX}{token}"] class RealtimeClientASR: @@ -161,7 +172,6 @@ async def _connect_websocket(self): self.args.server, self.args.endpoint, self.args.query_params, - self._client_secret_token, self.args.use_ssl, ) if self.args.use_ssl: @@ -176,8 +186,11 @@ async def _connect_websocket(self): ssl_context.check_hostname = False # ssl_context.verify_mode = ssl.CERT_REQUIRED - logger.debug("Connecting to WebSocket: %s", ws_url) - self.websocket = await websockets.connect(ws_url, ssl=ssl_context) + subprotocols = _websocket_subprotocols(self._client_secret_token) + logger.debug("Connecting to WebSocket: %s (subprotocols=%s)", ws_url, [REALTIME_SUBPROTOCOL, TOKEN_SUBPROTO_PREFIX + ""]) + self.websocket = await websockets.connect( + ws_url, ssl=ssl_context, subprotocols=subprotocols, + ) async def _initialize_session(self): """Initialize the WebSocket session.""" @@ -717,7 +730,6 @@ async def _connect_websocket(self): self.args.server, self.args.endpoint, self.args.query_params, - self._client_secret_token, self.args.use_ssl, ) if self.args.use_ssl: @@ -728,8 +740,15 @@ async def _connect_websocket(self): ssl_context.load_cert_chain(self.args.ssl_client_cert, self.args.ssl_client_key) ssl_context.check_hostname = False - logger.info("Connecting to WebSocket: %s", ws_url) - self.websocket = await websockets.connect(ws_url, ssl=ssl_context) + subprotocols = _websocket_subprotocols(self._client_secret_token) + logger.info( + "Connecting to WebSocket: %s (subprotocols=%s)", + ws_url, + [REALTIME_SUBPROTOCOL, TOKEN_SUBPROTO_PREFIX + ""], + ) + self.websocket = await websockets.connect( + ws_url, ssl=ssl_context, subprotocols=subprotocols, + ) async def _initialize_session(self): """Initialize the WebSocket session.""" From 1b482a6d0ecbbc9db4ba4dcb371589040e77a01d Mon Sep 17 00:00:00 2001 From: Yash Hayaran Date: Fri, 24 Jul 2026 11:14:07 +0530 Subject: [PATCH 3/6] Remove WebSocket connection logging in RealtimeClientTTS for improved security and privacy --- riva/client/realtime.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/riva/client/realtime.py b/riva/client/realtime.py index a2209f5..2b055d1 100644 --- a/riva/client/realtime.py +++ b/riva/client/realtime.py @@ -741,11 +741,6 @@ async def _connect_websocket(self): ssl_context.check_hostname = False subprotocols = _websocket_subprotocols(self._client_secret_token) - logger.info( - "Connecting to WebSocket: %s (subprotocols=%s)", - ws_url, - [REALTIME_SUBPROTOCOL, TOKEN_SUBPROTO_PREFIX + ""], - ) self.websocket = await websockets.connect( ws_url, ssl=ssl_context, subprotocols=subprotocols, ) From ed145055c11ae4057593b0ae1316de9a7ee341d5 Mon Sep 17 00:00:00 2001 From: Yash Hayaran Date: Fri, 24 Jul 2026 11:25:56 +0530 Subject: [PATCH 4/6] Update REALTIME_SUBPROTOCOL to "realtime" for consistency in WebSocket handling --- riva/client/realtime.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/riva/client/realtime.py b/riva/client/realtime.py index 2b055d1..1f3e102 100644 --- a/riva/client/realtime.py +++ b/riva/client/realtime.py @@ -69,7 +69,7 @@ def _extract_client_secret_token(session_data: Dict[str, Any]) -> str: return token TOKEN_SUBPROTO_PREFIX = "realtime-token." -REALTIME_SUBPROTOCOL = "realtime.v1" +REALTIME_SUBPROTOCOL = "realtime" def _build_websocket_url( server: str, @@ -88,8 +88,8 @@ def _build_websocket_url( def _websocket_subprotocols(token: str) -> List[str]: """Return Sec-WebSocket-Protocol entries for the realtime handshake. - Offers ``realtime.v1`` plus ``realtime-token.``. The server - validates the token entry and echoes back ``realtime.v1`` so the secret + Offers ``realtime`` plus ``realtime-token.``. The server + validates the token entry and echoes back ``realtime`` so the secret never appears in the response headers. """ return [REALTIME_SUBPROTOCOL, f"{TOKEN_SUBPROTO_PREFIX}{token}"] From 811ed624930098952b56b3b102b8643b85a39d6d Mon Sep 17 00:00:00 2001 From: Yash Hayaran Date: Fri, 24 Jul 2026 12:32:18 +0530 Subject: [PATCH 5/6] Refactor WebSocket connection handling in RealtimeClient - Updated `_extract_client_secret_token` to return `None` if the token is not present, improving error handling. - Introduced `_build_ssl_context` to create an SSL context for secure WebSocket connections. - Refactored `_connect_websocket` to accept parameters for better flexibility and logging. - Removed the old `_connect_websocket` method from `RealtimeClientASR` and `RealtimeClientTTS`, replacing it with the new implementation for improved clarity and maintainability. --- riva/client/realtime.py | 106 ++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/riva/client/realtime.py b/riva/client/realtime.py index 1f3e102..5b5e414 100644 --- a/riva/client/realtime.py +++ b/riva/client/realtime.py @@ -58,15 +58,20 @@ def _raise_for_session_status(response, provided_key: bool) -> None: ) -def _extract_client_secret_token(session_data: Dict[str, Any]) -> str: - """Return the ephemeral client_secret token from a session-creation response.""" +def _extract_client_secret_token(session_data: Dict[str, Any]) -> Optional[str]: + """Return the ephemeral client_secret token when present, else ``None``. + + Older servers that do not mint ``client_secret`` keep the legacy + unauthenticated WebSocket flow. New servers that return a secret require + it on ``Sec-WebSocket-Protocol``. + """ client_secret = session_data.get("client_secret") if not isinstance(client_secret, dict): - raise ValueError("Session response missing client_secret") + return None token = client_secret.get("value") if not token: - raise ValueError("Session response missing client_secret.value") - return token + return None + return str(token) TOKEN_SUBPROTO_PREFIX = "realtime-token." REALTIME_SUBPROTOCOL = "realtime" @@ -95,6 +100,46 @@ def _websocket_subprotocols(token: str) -> List[str]: return [REALTIME_SUBPROTOCOL, f"{TOKEN_SUBPROTO_PREFIX}{token}"] +def _build_ssl_context(args: argparse.Namespace): + """Build an optional SSL context for WebSocket connections.""" + if not getattr(args, "use_ssl", False): + return None + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + if getattr(args, "ssl_root_cert", None): + ssl_context.load_verify_locations(args.ssl_root_cert) + if getattr(args, "ssl_client_cert", None) and getattr(args, "ssl_client_key", None): + ssl_context.load_cert_chain(args.ssl_client_cert, args.ssl_client_key) + ssl_context.check_hostname = False + return ssl_context + + +async def _connect_realtime_websocket( + args: argparse.Namespace, + client_secret_token: Optional[str], +): + """Open the realtime WebSocket, attaching subprotocol auth when available.""" + ws_url = _build_websocket_url( + args.server, + args.endpoint, + args.query_params, + args.use_ssl, + ) + ssl_context = _build_ssl_context(args) + connect_kwargs = {"ssl": ssl_context} + if client_secret_token: + connect_kwargs["subprotocols"] = _websocket_subprotocols(client_secret_token) + logger.debug( + "Connecting to WebSocket: %s (using client_secret; basic authenticated flow)", + ws_url, + ) + else: + logger.debug( + "Connecting to WebSocket: %s (no client_secret; legacy unauthenticated flow)", + ws_url, + ) + return await websockets.connect(ws_url, **connect_kwargs) + + class RealtimeClientASR: """Client for real-time transcription via WebSocket connection.""" @@ -164,32 +209,8 @@ async def _initialize_http_session(self) -> Dict[str, Any]: async def _connect_websocket(self): """Connect to WebSocket endpoint.""" - if not self._client_secret_token: - raise ValueError("client_secret token is required before opening the WebSocket") - - ssl_context = None - ws_url = _build_websocket_url( - self.args.server, - self.args.endpoint, - self.args.query_params, - self.args.use_ssl, - ) - if self.args.use_ssl: - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - # Load a custom CA certificate bundle - if self.args.ssl_root_cert: - ssl_context.load_verify_locations(self.args.ssl_root_cert) - # Load a client certificate and key - if self.args.ssl_client_cert and self.args.ssl_client_key: - ssl_context.load_cert_chain(self.args.ssl_client_cert, self.args.ssl_client_key) - # Disable hostname verification - ssl_context.check_hostname = False - # ssl_context.verify_mode = ssl.CERT_REQUIRED - - subprotocols = _websocket_subprotocols(self._client_secret_token) - logger.debug("Connecting to WebSocket: %s (subprotocols=%s)", ws_url, [REALTIME_SUBPROTOCOL, TOKEN_SUBPROTO_PREFIX + ""]) - self.websocket = await websockets.connect( - ws_url, ssl=ssl_context, subprotocols=subprotocols, + self.websocket = await _connect_realtime_websocket( + self.args, self._client_secret_token, ) async def _initialize_session(self): @@ -722,27 +743,8 @@ async def _initialize_http_session(self) -> Dict[str, Any]: async def _connect_websocket(self): """Connect to WebSocket endpoint.""" - if not self._client_secret_token: - raise ValueError("client_secret token is required before opening the WebSocket") - - ssl_context = None - ws_url = _build_websocket_url( - self.args.server, - self.args.endpoint, - self.args.query_params, - self.args.use_ssl, - ) - if self.args.use_ssl: - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - if self.args.ssl_root_cert: - ssl_context.load_verify_locations(self.args.ssl_root_cert) - if self.args.ssl_client_cert and self.args.ssl_client_key: - ssl_context.load_cert_chain(self.args.ssl_client_cert, self.args.ssl_client_key) - ssl_context.check_hostname = False - - subprotocols = _websocket_subprotocols(self._client_secret_token) - self.websocket = await websockets.connect( - ws_url, ssl=ssl_context, subprotocols=subprotocols, + self.websocket = await _connect_realtime_websocket( + self.args, self._client_secret_token, ) async def _initialize_session(self): From 39e17ef7d12ea441c1fa0eb7e16913ba16b6fa8e Mon Sep 17 00:00:00 2001 From: Yash Hayaran Date: Fri, 24 Jul 2026 14:25:11 +0530 Subject: [PATCH 6/6] Refactor WebSocket connection methods in RealtimeClient - Renamed `_connect_realtime_websocket` to `_connect_websocket` for consistency. - Removed logging statements related to client secret token usage to enhance security. - Updated `RealtimeClientASR` and `RealtimeClientTTS` to utilize the new `_connect_websocket` method directly, improving clarity and maintainability. --- riva/client/realtime.py | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/riva/client/realtime.py b/riva/client/realtime.py index 5b5e414..58f29af 100644 --- a/riva/client/realtime.py +++ b/riva/client/realtime.py @@ -113,7 +113,7 @@ def _build_ssl_context(args: argparse.Namespace): return ssl_context -async def _connect_realtime_websocket( +async def _connect_websocket( args: argparse.Namespace, client_secret_token: Optional[str], ): @@ -128,15 +128,6 @@ async def _connect_realtime_websocket( connect_kwargs = {"ssl": ssl_context} if client_secret_token: connect_kwargs["subprotocols"] = _websocket_subprotocols(client_secret_token) - logger.debug( - "Connecting to WebSocket: %s (using client_secret; basic authenticated flow)", - ws_url, - ) - else: - logger.debug( - "Connecting to WebSocket: %s (no client_secret; legacy unauthenticated flow)", - ws_url, - ) return await websockets.connect(ws_url, **connect_kwargs) @@ -173,7 +164,9 @@ async def connect(self): self._client_secret_token = _extract_client_secret_token(session_data) # Connect to WebSocket - await self._connect_websocket() + self.websocket = await _connect_websocket( + self.args, self._client_secret_token, + ) await self._initialize_session() except requests.exceptions.RequestException as e: @@ -207,12 +200,6 @@ async def _initialize_http_session(self) -> Dict[str, Any]: logger.debug("Session initialized: %s", session_data) return session_data - async def _connect_websocket(self): - """Connect to WebSocket endpoint.""" - self.websocket = await _connect_realtime_websocket( - self.args, self._client_secret_token, - ) - async def _initialize_session(self): """Initialize the WebSocket session.""" try: @@ -672,7 +659,9 @@ async def connect(self): # Connect to WebSocket logger.info("Connecting to WebSocket...") - await self._connect_websocket() + self.websocket = await _connect_websocket( + self.args, self._client_secret_token, + ) logger.info("WebSocket connected successfully") # Initialize WebSocket session @@ -741,12 +730,6 @@ async def _initialize_http_session(self) -> Dict[str, Any]: logger.info("Session initialized: %s", session_data) return session_data - async def _connect_websocket(self): - """Connect to WebSocket endpoint.""" - self.websocket = await _connect_realtime_websocket( - self.args, self._client_secret_token, - ) - async def _initialize_session(self): """Initialize the WebSocket session.""" try: