-
Notifications
You must be signed in to change notification settings - Fork 4.6k
fix(voice): honor client config for streamed STT #4532
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
sylvesterkaczmarek
wants to merge
17
commits into
openai:main
from
sylvesterkaczmarek:fix/stt-websocket-client-config
Closed
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
2a89812
fix(voice): honor OpenAI client config for streamed STT
sylvesterkaczmarek fff641c
test(voice): cover streamed STT client configuration
sylvesterkaczmarek 0d40f22
test(voice): avoid assigning client properties
sylvesterkaczmarek 9a18176
test(voice): cover client query and omitted headers
sylvesterkaczmarek 8b6b1a3
fix(voice): honor client query and omission sentinels
sylvesterkaczmarek a0b7ac0
fix(voice): refresh client auth before streamed STT handshake
sylvesterkaczmarek 86d59ef
test(voice): cover streamed STT API key refresh
sylvesterkaczmarek 941ebc9
test(voice): give streamed STT client doubles routing metadata
sylvesterkaczmarek d7c5eae
refactor(models): share OpenAI websocket client normalization
sylvesterkaczmarek bcc8d57
refactor(voice): use shared OpenAI websocket normalization
sylvesterkaczmarek f175739
refactor(models): use shared OpenAI websocket normalization
sylvesterkaczmarek 832db7e
chore(voice): restore final newline
sylvesterkaczmarek 101108b
chore(voice): restore test file newline
sylvesterkaczmarek 5d163b5
fix(models): honor omitted inherited websocket headers
sylvesterkaczmarek e51f40c
test(voice): cover omitted inherited websocket header
sylvesterkaczmarek 1743799
fix(voice): replace fixed session header case-insensitively
sylvesterkaczmarek 40a10d4
test(voice): cover case-insensitive session header override
sylvesterkaczmarek File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from collections.abc import Mapping | ||
| from typing import Any | ||
|
|
||
| import httpx2 | ||
| from openai import AsyncOpenAI, NotGiven, Omit | ||
|
|
||
| from .._httpx_compat import is_legacy_httpx_instance | ||
| from ..exceptions import UserError | ||
|
|
||
|
|
||
| def _is_openai_omitted_value(value: Any) -> bool: | ||
| return isinstance(value, Omit | NotGiven) | ||
|
|
||
|
|
||
| async def refresh_openai_client_api_key_if_supported(client: Any) -> None: | ||
| """Refresh dynamic OpenAI client credentials before materializing handshake headers.""" | ||
| refresh_api_key = getattr(client, "_refresh_api_key", None) | ||
| if callable(refresh_api_key): | ||
| await refresh_api_key() | ||
|
|
||
|
|
||
| def _remove_header(headers: dict[str, str], key: object) -> None: | ||
| header_key = str(key) | ||
| for existing_key in list(headers): | ||
| if existing_key.lower() == header_key.lower(): | ||
| del headers[existing_key] | ||
|
|
||
|
|
||
| def _set_header(headers: dict[str, str], key: object, value: object) -> None: | ||
| header_key = str(key) | ||
| _remove_header(headers, header_key) | ||
| headers[header_key] = str(value) | ||
|
|
||
|
|
||
| def merge_openai_client_websocket_headers( | ||
| client: AsyncOpenAI, | ||
| *, | ||
| extra_headers: Mapping[str, Any] | None = None, | ||
| ) -> dict[str, str]: | ||
| """Materialize OpenAI client auth/default headers for a WebSocket handshake.""" | ||
| headers: dict[str, str] = {} | ||
| for source in ( | ||
| getattr(client, "auth_headers", {}), | ||
| getattr(client, "default_headers", {}), | ||
| ): | ||
| for key, value in source.items(): | ||
| if isinstance(value, NotGiven): | ||
| continue | ||
| if isinstance(value, Omit): | ||
| _remove_header(headers, key) | ||
| continue | ||
|
sylvesterkaczmarek marked this conversation as resolved.
|
||
| _set_header(headers, key, value) | ||
|
|
||
| for key, value in (extra_headers or {}).items(): | ||
| if isinstance(value, NotGiven): | ||
| continue | ||
| _remove_header(headers, key) | ||
| if isinstance(value, Omit): | ||
| continue | ||
| headers[str(key)] = str(value) | ||
|
|
||
| return headers | ||
|
|
||
|
|
||
| def _merge_query_values(params: dict[str, Any], values: Mapping[str, Any]) -> None: | ||
| for key, value in values.items(): | ||
| query_key = str(key) | ||
| if isinstance(value, Omit): | ||
| params.pop(query_key, None) | ||
| continue | ||
| if isinstance(value, NotGiven): | ||
| continue | ||
| params[query_key] = value | ||
|
|
||
|
|
||
| def prepare_openai_client_websocket_base_url( | ||
| client: AsyncOpenAI, | ||
| *, | ||
| extra_query: Any = None, | ||
| context: str, | ||
| ) -> httpx2.URL: | ||
| """Build the client-derived WebSocket base URL and normalized query parameters. | ||
|
|
||
| Endpoint suffixes and transport-specific fixed query parameters are intentionally left to | ||
| each caller. | ||
| """ | ||
| websocket_base_url = getattr(client, "websocket_base_url", None) | ||
| if websocket_base_url is not None: | ||
| if is_legacy_httpx_instance(websocket_base_url, "URL"): | ||
| websocket_base_url = str(websocket_base_url) | ||
| base_url = httpx2.URL(websocket_base_url) | ||
| else: | ||
| client_base_url = client.base_url | ||
| if is_legacy_httpx_instance(client_base_url, "URL"): | ||
| client_base_url = str(client_base_url) | ||
| base_url = httpx2.URL(client_base_url) | ||
|
|
||
| ws_scheme = {"http": "ws", "https": "wss"}.get(base_url.scheme, base_url.scheme) | ||
| base_url = base_url.copy_with(scheme=ws_scheme) | ||
| params: dict[str, Any] = dict(base_url.params) | ||
|
|
||
| default_query = getattr(client, "default_query", None) | ||
| if default_query is not None and not _is_openai_omitted_value(default_query): | ||
| if not isinstance(default_query, Mapping): | ||
| raise UserError(f"{context} client default_query must be a mapping.") | ||
| _merge_query_values(params, default_query) | ||
|
|
||
| if extra_query is not None and not _is_openai_omitted_value(extra_query): | ||
| if not isinstance(extra_query, Mapping): | ||
| raise UserError(f"{context} extra_query must be a mapping.") | ||
| _merge_query_values(params, extra_query) | ||
|
|
||
| return base_url.copy_with(params=params) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.