fix(realtime): preserve upstream authentication and transcription behavior

This commit is contained in:
Emerson Gomes 2026-10-19 23:24:56 -05:00
parent cb710d6b4e
commit 4ffff0a578
No known key found for this signature in database
GPG key ID: D3DF28AB5D1B5E17
6 changed files with 38 additions and 21 deletions

View file

@ -1888,14 +1888,13 @@ def completion_cost(
# the response attribute (for verbose_json responses that
# naturally include duration from the provider).
_hidden = getattr(completion_response, "_hidden_params", {}) or {}
provider_duration = _get_transcription_usage_duration(completion_response)
audio_transcription_file_duration = (
provider_duration
if provider_duration is not None
else _hidden.get(
"audio_transcription_duration",
getattr(completion_response, "duration", 0.0),
)
audio_transcription_file_duration = _hidden.get(
"audio_transcription_duration",
getattr(completion_response, "duration", 0.0),
) or (
(_get_transcription_usage_duration(completion_response) or 0.0)
if _bills_provider_transcription_seconds(requested_model, custom_llm_provider)
else 0.0
)
elif call_type in _RERANK_CALL_TYPES:
if completion_response is not None and isinstance(completion_response, RerankResponse):

View file

@ -59,7 +59,6 @@ class OpenAIGPTTranscribeAudioTranscriptionConfig(OpenAIGPTAudioTranscriptionCon
data: Final = {
"model": model,
"file": audio_file,
"response_format": "json",
**optional_params,
}
return AudioTranscriptionRequestData(data=data)

View file

@ -322,7 +322,9 @@ async def acreate_realtime_translation_client_secret(
request_data["session"].pop("type", None)
return await base_llm_http_handler.async_realtime_translation_client_secret_handler(
api_base=resolved_api_base,
api_key=resolved_api_key,
api_key=await _realtime_http_bearer_token(
custom_llm_provider, resolved_api_key, resolved_api_base, litellm_params
),
request_data=request_data,
logging_obj=litellm_logging_obj,
timeout=timeout or request_timeout,

View file

@ -1,5 +1,6 @@
import gzip
import json
from typing import Final
from unittest.mock import AsyncMock, MagicMock
import httpx
@ -78,14 +79,16 @@ def test_openai_translation_calls_url():
)
def test_azure_translation_urls_use_ga_paths():
config = AzureRealtimeHTTPConfig()
@pytest.mark.parametrize("suffix", ("", "/", "/openai", "/openai/v1/"))
def test_azure_translation_urls_use_ga_paths(suffix: str):
config: Final = AzureRealtimeHTTPConfig()
api_base: Final = f"https://example.openai.azure.com{suffix}"
assert (
config.get_translation_client_secret_url("https://example.openai.azure.com", "translate-deployment")
config.get_translation_client_secret_url(api_base, "translate-deployment")
== "https://example.openai.azure.com/openai/v1/realtime/translations/client_secrets"
)
assert (
config.get_translation_calls_url("https://example.openai.azure.com", "translate-deployment")
config.get_translation_calls_url(api_base, "translate-deployment")
== "https://example.openai.azure.com/openai/v1/realtime/translations/calls"
)

View file

@ -38,7 +38,7 @@ def test_gpt_transcribe_config_uses_native_parameters_and_json():
request = config.transform_audio_transcription_request(
model="gpt-transcribe",
audio_file=audio_file,
optional_params={"keywords": ["LiteLLM"], "languages": ["en", "fr"], "stream": True},
optional_params={"keywords": ["LiteLLM"], "languages": ["en", "fr"], "stream": True, "response_format": "json"},
litellm_params={},
)
assert request.data["response_format"] == "json"

View file

@ -2,6 +2,7 @@ import asyncio
import json
import time
from collections.abc import Awaitable, Callable, Mapping
from datetime import datetime, timezone
from pathlib import Path
from types import TracebackType
from typing import Final
@ -13,6 +14,7 @@ import respx
import litellm
from litellm.integrations.custom_guardrail import CustomGuardrail
from litellm.litellm_core_utils.litellm_logging import Logging
from litellm.litellm_core_utils.realtime_streaming import RealTimeStreaming
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler
from litellm.llms.openai.workload_identity import _workload_identity_auth
@ -1188,20 +1190,32 @@ class _GatewayThatRecordsTheAuthorization:
@pytest.mark.asyncio
@pytest.mark.parametrize(
"route",
[realtime_main.acreate_realtime_client_secret, realtime_main.acreate_realtime_transcription_session],
ids=["client_secrets", "transcription_sessions"],
("route", "model"),
[
(realtime_main.acreate_realtime_client_secret, "gpt-realtime"),
(realtime_main.acreate_realtime_transcription_session, "gpt-realtime"),
(realtime_main.acreate_realtime_translation_client_secret, "gpt-realtime-translate"),
],
ids=["client_secrets", "transcription_sessions", "translation_client_secrets"],
)
@respx.mock
async def test_openai_realtime_http_routes_send_the_federated_bearer_for_a_keyless_deployment(
federated_deployment: dict[str, str], route: Callable[..., Awaitable[object]]
federated_deployment: dict[str, str], route: Callable[..., Awaitable[object]], model: str
) -> None:
_mock_token_exchange()
gateway: Final = _GatewayThatRecordsTheAuthorization()
await route.__wrapped__(
model="gpt-realtime",
model=model,
custom_llm_provider="openai",
litellm_logging_obj=FakeLogging(),
litellm_logging_obj=Logging(
model=model,
messages=[],
stream=False,
call_type="acreate_realtime_client_secret",
start_time=datetime(2026, 1, 1, tzinfo=timezone.utc),
litellm_call_id="test-federated-realtime",
function_id="test-federated-realtime",
),
client=AsyncHTTPHandler(transport=httpx.MockTransport(gateway)),
**federated_deployment,
)