mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
fix(speech): send a number or boolean deployment header as its config text through the TTS bridge
A header value has to be text, and the merge base never sent deployment headers, so failing like chat turned a TTS deployment with a number in extra_headers from 200 into 500. Null, list and map values still fail like chat
This commit is contained in:
parent
9dafd85341
commit
2519b429ea
2 changed files with 29 additions and 23 deletions
|
|
@ -2,6 +2,7 @@
|
|||
Handler for transforming /chat/completions api requests to litellm.responses requests
|
||||
"""
|
||||
|
||||
import json
|
||||
from collections.abc import Coroutine, Mapping
|
||||
from typing import TYPE_CHECKING, Final
|
||||
|
||||
|
|
@ -17,6 +18,16 @@ if TYPE_CHECKING:
|
|||
from litellm.types.utils import CacheControlsHint
|
||||
|
||||
|
||||
def _header_text(value: object) -> object:
|
||||
return json.dumps(value) if isinstance(value, bool | int | float) else value
|
||||
|
||||
|
||||
def _headers_as_text(extra_headers: Mapping[str, object] | None) -> Mapping[str, object] | None:
|
||||
if extra_headers is None:
|
||||
return None
|
||||
return {name: _header_text(value) for name, value in extra_headers.items()}
|
||||
|
||||
|
||||
class SpeechToCompletionBridgeHandlerInputKwargs(TypedDict):
|
||||
model: str
|
||||
input: str
|
||||
|
|
@ -117,7 +128,7 @@ class SpeechToCompletionBridgeHandler:
|
|||
litellm_params={
|
||||
**litellm_params,
|
||||
"api_base": api_base,
|
||||
"extra_headers": extra_headers,
|
||||
"extra_headers": _headers_as_text(extra_headers),
|
||||
"timeout": timeout,
|
||||
**cache_controls,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6379,33 +6379,28 @@ def test_speech_gemini_tts_keeps_cached_audio_only_for_the_request_ttl(
|
|||
assert route.call_count == 2
|
||||
|
||||
|
||||
def test_speech_gemini_tts_fails_like_chat_on_a_non_string_deployment_header(respx_mock: respx.MockRouter):
|
||||
def test_speech_gemini_tts_sends_number_and_boolean_deployment_headers_as_their_config_text(
|
||||
respx_mock: respx.MockRouter,
|
||||
):
|
||||
route: Final = respx_mock.post(url__regex=GEMINI_DEFAULT_GENERATE_CONTENT_URL).mock(
|
||||
return_value=httpx.Response(200, json=_gemini_tts_generate_content_response())
|
||||
)
|
||||
deployment_headers: Final = {"x-gateway-key": "gateway-secret", "x-org-id": 12345}
|
||||
|
||||
with pytest.raises(litellm.APIConnectionError) as chat_error:
|
||||
litellm.completion(
|
||||
model=f"gemini/{GEMINI_TTS_MODEL}",
|
||||
messages=[{"role": "user", "content": "hello"}],
|
||||
modalities=["audio"],
|
||||
audio={"voice": "Kore", "format": "pcm16"},
|
||||
api_key="fake-gemini-key",
|
||||
extra_headers=deployment_headers,
|
||||
)
|
||||
with pytest.raises(litellm.APIConnectionError) as speech_error:
|
||||
litellm.speech(
|
||||
model=f"gemini/{GEMINI_TTS_MODEL}",
|
||||
input="hello",
|
||||
voice="Kore",
|
||||
api_key="fake-gemini-key",
|
||||
extra_headers=deployment_headers,
|
||||
)
|
||||
litellm.speech(
|
||||
model=f"gemini/{GEMINI_TTS_MODEL}",
|
||||
input="hello",
|
||||
voice="Kore",
|
||||
api_key="fake-gemini-key",
|
||||
extra_headers={"x-gateway-key": "gateway-secret", "x-org-id": 12345, "x-ratio": 1.5, "x-audit": True},
|
||||
)
|
||||
|
||||
assert "Header value must be str or bytes" in str(chat_error.value)
|
||||
assert "Header value must be str or bytes" in str(speech_error.value)
|
||||
assert route.call_count == 0
|
||||
sent: Final = route.calls.last.request.headers
|
||||
assert (sent["x-gateway-key"], sent["x-org-id"], sent["x-ratio"], sent["x-audit"]) == (
|
||||
"gateway-secret",
|
||||
"12345",
|
||||
"1.5",
|
||||
"true",
|
||||
)
|
||||
|
||||
|
||||
async def test_aspeech_gemini_tts_waits_for_a_stalled_upstream_without_holding_a_worker_thread(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue