From 133457b9b9349679ea27a15d9e5d1dfb2f4c0647 Mon Sep 17 00:00:00 2001 From: shrey kharbanda Date: Tue, 27 Oct 2026 22:15:38 +0000 Subject: [PATCH] fix(azure): keep GA audio settings and upstream headers on transcription sessions --- .../azure/realtime/transcription_sessions.py | 36 +++++- .../realtime/test_http_transformation.py | 122 ++++++++++++++++++ 2 files changed, 151 insertions(+), 7 deletions(-) diff --git a/litellm/llms/azure/realtime/transcription_sessions.py b/litellm/llms/azure/realtime/transcription_sessions.py index 08c35bf934d..da56530a416 100644 --- a/litellm/llms/azure/realtime/transcription_sessions.py +++ b/litellm/llms/azure/realtime/transcription_sessions.py @@ -25,18 +25,27 @@ def azure_transcription_client_secret_request( expires_after: Final = transcription_session.get( "expires_after", raw_secret.get("expires_at") if isinstance(raw_secret, dict) else raw_secret ) + raw_audio: Final = transcription_session.get("audio") + audio: Final = raw_audio if isinstance(raw_audio, dict) else {} + raw_audio_input: Final = audio.get("input") + existing_audio_input: Final = raw_audio_input if isinstance(raw_audio_input, dict) else {} + raw_audio_transcription: Final = existing_audio_input.get("transcription") + audio_transcription: Final = raw_audio_transcription if isinstance(raw_audio_transcription, dict) else {} raw_transcription: Final = transcription_session.get("input_audio_transcription") transcription: Final = raw_transcription if isinstance(raw_transcription, dict) else {} raw_format: Final = transcription_session.get("input_audio_format") audio_input: Final[JsonValue] = { + **existing_audio_input, **( - {"format": _AUDIO_FORMATS[raw_format]} - if isinstance(raw_format, str) and raw_format in _AUDIO_FORMATS - else {"format": raw_format} - if raw_format is not None + { + "format": _AUDIO_FORMATS[raw_format] + if isinstance(raw_format, str) and raw_format in _AUDIO_FORMATS + else raw_format + } + if "input_audio_format" in transcription_session else {} ), - "transcription": {**transcription, "model": model}, + "transcription": {**audio_transcription, **transcription, "model": model}, **( {"noise_reduction": transcription_session["input_audio_noise_reduction"]} if "input_audio_noise_reduction" in transcription_session @@ -55,6 +64,7 @@ def azure_transcription_client_secret_request( if key not in ( "model", + "audio", "client_secret", "expires_after", "modalities", @@ -65,7 +75,10 @@ def azure_transcription_client_secret_request( ) }, "type": "transcription", - "audio": {"input": audio_input}, + "audio": { + **{key: value for key, value in audio.items() if key != "input"}, + "input": audio_input, + }, } return MappingProxyType( {"session": session, **({"expires_after": expires_after} if expires_after is not None else {})} @@ -101,4 +114,13 @@ def azure_transcription_client_secret_response(response: httpx.Response) -> http **({"turn_detection": audio_input["turn_detection"]} if "turn_detection" in audio_input else {}), "client_secret": client_secret, } - return httpx.Response(response.status_code, json=response_body) + return httpx.Response( + response.status_code, + headers={ + key: value + for key, value in response.headers.items() + if key.lower() not in ("content-length", "content-encoding", "transfer-encoding") + }, + json=response_body, + request=response.request, + ) diff --git a/tests/unit/llms/azure/realtime/test_http_transformation.py b/tests/unit/llms/azure/realtime/test_http_transformation.py index 531cb0ad784..26080ce0f1b 100644 --- a/tests/unit/llms/azure/realtime/test_http_transformation.py +++ b/tests/unit/llms/azure/realtime/test_http_transformation.py @@ -1,3 +1,4 @@ +import gzip import json from typing import Final, Literal @@ -149,6 +150,47 @@ def test_azure_transcription_client_secret_response_preserves_non_object_json() assert azure_transcription_client_secret_response(response) is response +def test_azure_transcription_client_secret_response_preserves_upstream_headers_and_request() -> None: + request: Final = httpx.Request("GET", "https://my.openai.azure.com/openai/v1/realtime/client_secrets") + payload: Final = { + "value": "ek_upstream", + "expires_at": 1792775579, + "session": { + "type": "transcription", + "audio": { + "input": { + "format": {"type": "audio/pcm", "rate": 16000}, + "transcription": {"model": "whisper-deployment", "language": "fr"}, + } + }, + }, + } + compressed_body: Final = gzip.compress(json.dumps(payload).encode()) + upstream_response: Final = httpx.Response( + 200, + headers={ + "content-type": "application/json", + "content-encoding": "gzip", + "content-length": str(len(compressed_body)), + "x-ratelimit-remaining-requests": "17", + }, + content=compressed_body, + request=request, + ) + + response: Final = azure_transcription_client_secret_response(upstream_response) + + assert response.request is request + assert response.headers["x-ratelimit-remaining-requests"] == "17" + assert "content-encoding" not in response.headers + response.raise_for_status() + assert response.json() == { + "input_audio_format": "pcm16", + "input_audio_transcription": {"model": "whisper-deployment", "language": "fr"}, + "client_secret": {"value": "ek_upstream", "expires_at": 1792775579}, + } + + @pytest.mark.asyncio @pytest.mark.parametrize("expiry_style", ("legacy", "ga")) async def test_azure_transcription_session_preserves_requested_secret_expiry( @@ -216,3 +258,83 @@ async def test_azure_transcription_session_preserves_disabled_audio_processing_w }, } } + + +@pytest.mark.asyncio +async def test_azure_transcription_session_preserves_ga_audio_settings() -> None: + azure: Final = _AzureThatRecordsTheRequest() + await litellm.acreate_realtime_transcription_session( + model="azure/whisper-routing-deployment", + transcription_session={ + "audio": { + "input": { + "format": {"type": "audio/pcm", "rate": 16000}, + "transcription": {"model": "nested-model", "language": "fr"}, + "noise_reduction": {"type": "near_field"}, + "turn_detection": {"type": "server_vad"}, + }, + "output": {"format": {"type": "audio/pcm", "rate": 24000}}, + } + }, + api_base="https://my.openai.azure.com", + api_key="azure-key", + client=azure.client(), + ) + + (request,) = azure.requests + assert json.loads(request.content) == { + "session": { + "type": "transcription", + "audio": { + "input": { + "format": {"type": "audio/pcm", "rate": 16000}, + "transcription": {"model": "whisper-routing-deployment", "language": "fr"}, + "noise_reduction": {"type": "near_field"}, + "turn_detection": {"type": "server_vad"}, + }, + "output": {"format": {"type": "audio/pcm", "rate": 24000}}, + }, + } + } + + +@pytest.mark.asyncio +async def test_azure_transcription_session_legacy_audio_fields_override_ga_settings() -> None: + azure: Final = _AzureThatRecordsTheRequest() + await litellm.acreate_realtime_transcription_session( + model="azure/whisper-routing-deployment", + transcription_session={ + "audio": { + "input": { + "format": {"type": "audio/pcm", "rate": 16000}, + "transcription": {"model": "nested-model", "language": "fr"}, + "noise_reduction": {"type": "near_field"}, + "turn_detection": {"type": "server_vad"}, + }, + "output": {"format": {"type": "audio/pcm", "rate": 24000}}, + }, + "input_audio_format": "g711_alaw", + "input_audio_transcription": {"language": "de"}, + "input_audio_noise_reduction": None, + "turn_detection": None, + }, + api_base="https://my.openai.azure.com", + api_key="azure-key", + client=azure.client(), + ) + + (request,) = azure.requests + assert json.loads(request.content) == { + "session": { + "type": "transcription", + "audio": { + "input": { + "format": {"type": "audio/pcma"}, + "transcription": {"model": "whisper-routing-deployment", "language": "de"}, + "noise_reduction": None, + "turn_detection": None, + }, + "output": {"format": {"type": "audio/pcm", "rate": 24000}}, + }, + } + }