mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
fix(speech): use the deployment's api_base and timeout for Gemini and Vertex AI TTS
Backport of #50803 to lts/1.108.x.
Cherry-picked from 86cd49d476 (litellm_speech_bridge_api_base).
This commit is contained in:
parent
2a56160037
commit
fa4baa139f
4 changed files with 116 additions and 1 deletions
|
|
@ -7,6 +7,8 @@ from typing import TYPE_CHECKING, Final
|
|||
from typing_extensions import TypedDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import httpx
|
||||
|
||||
from litellm import LiteLLMLoggingObj
|
||||
from litellm.types.llms.openai import HttpxBinaryResponseContent
|
||||
|
||||
|
|
@ -85,6 +87,8 @@ class SpeechToCompletionBridgeHandler:
|
|||
headers: dict,
|
||||
logging_obj: "LiteLLMLoggingObj",
|
||||
custom_llm_provider: str,
|
||||
api_base: str | None,
|
||||
timeout: "float | httpx.Timeout | None",
|
||||
) -> "HttpxBinaryResponseContent":
|
||||
received_args: Final = locals()
|
||||
from litellm import completion
|
||||
|
|
@ -104,7 +108,7 @@ class SpeechToCompletionBridgeHandler:
|
|||
model=model,
|
||||
input=input,
|
||||
optional_params=optional_params,
|
||||
litellm_params=litellm_params,
|
||||
litellm_params={**litellm_params, "api_base": api_base, "timeout": timeout},
|
||||
headers=headers,
|
||||
litellm_logging_obj=logging_obj,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
|
|
|
|||
|
|
@ -8751,6 +8751,8 @@ def speech(
|
|||
headers=headers or {},
|
||||
logging_obj=logging_obj,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
# Vertex AI Text-to-Speech (Google Cloud TTS)
|
||||
|
|
@ -8802,6 +8804,8 @@ def speech(
|
|||
headers=headers or {},
|
||||
logging_obj=logging_obj,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
)
|
||||
elif custom_llm_provider == "runwayml":
|
||||
from litellm.llms.runwayml.text_to_speech.transformation import (
|
||||
|
|
|
|||
|
|
@ -1098,3 +1098,49 @@ def test_lyria_malformed_item_position_does_not_change_the_mapped_error(payload:
|
|||
custom_llm_provider="vertex_ai",
|
||||
original_exception=exc_info.value,
|
||||
)
|
||||
|
||||
|
||||
_PCM_AUDIO: Final = b"\x00\x00\x01\x00" * 2400
|
||||
_GENERATE_CONTENT_RESPONSE: Final = {
|
||||
"candidates": [
|
||||
{
|
||||
"content": {
|
||||
"role": "model",
|
||||
"parts": [
|
||||
{
|
||||
"inlineData": {
|
||||
"mimeType": "audio/L16;codec=pcm;rate=24000",
|
||||
"data": base64.b64encode(_PCM_AUDIO).decode(),
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"finishReason": "STOP",
|
||||
}
|
||||
],
|
||||
"usageMetadata": {"promptTokenCount": 5, "candidatesTokenCount": 10, "totalTokenCount": 15},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("custom_llm_provider", ("vertex_ai", "vertex_ai_beta"))
|
||||
@pytest.mark.asyncio
|
||||
async def test_aspeech_vertex_ai_gemini_tts_routes_to_configured_api_base(
|
||||
respx_mock: respx.MockRouter, google_token_endpoint: responses.RequestsMock, custom_llm_provider: str
|
||||
) -> None:
|
||||
gateway_route: Final = respx_mock.post(
|
||||
"https://vertex.gateway.internal/v1/projects/test-project/locations/global"
|
||||
"/publishers/google/models/gemini-3.8-flash-tts:generateContent"
|
||||
).mock(return_value=httpx.Response(200, json=_GENERATE_CONTENT_RESPONSE))
|
||||
|
||||
response: Final = await litellm.aspeech(
|
||||
model=f"{custom_llm_provider}/gemini-3.8-flash-tts",
|
||||
input="Say hello",
|
||||
voice="Kore",
|
||||
vertex_credentials=_AUTHORIZED_USER,
|
||||
vertex_location="global",
|
||||
api_base="https://vertex.gateway.internal",
|
||||
)
|
||||
|
||||
assert gateway_route.call_count == 1
|
||||
assert gateway_route.calls.last.request.headers["authorization"] == "Bearer minted-google-token"
|
||||
assert _PCM_AUDIO in response.content
|
||||
|
|
|
|||
|
|
@ -4777,6 +4777,67 @@ def test_speech_mistral_routes_to_configured_api_base(respx_mock: respx.MockRout
|
|||
assert response.content == audio_bytes
|
||||
|
||||
|
||||
GEMINI_TTS_MODEL: Final = "gemini-3.8-flash-tts"
|
||||
GEMINI_GATEWAY_BASE: Final = "https://gemini.gateway.internal/v1beta"
|
||||
GEMINI_DEFAULT_GENERATE_CONTENT_URL: Final = (
|
||||
rf"https://generativelanguage\.googleapis\.com/[^/]+/models/{GEMINI_TTS_MODEL}:generateContent"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("endpoint_param", ["api_base", "base_url"])
|
||||
@pytest.mark.parametrize("sync_mode", [True, False])
|
||||
async def test_speech_gemini_tts_routes_to_configured_api_base(
|
||||
respx_mock: respx.MockRouter, monkeypatch: pytest.MonkeyPatch, endpoint_param: str, sync_mode: bool
|
||||
):
|
||||
monkeypatch.setattr(litellm, "disable_aiohttp_transport", True)
|
||||
gateway_route: Final = respx_mock.post(f"{GEMINI_GATEWAY_BASE}/models/{GEMINI_TTS_MODEL}:generateContent").mock(
|
||||
return_value=httpx.Response(200, json=_gemini_tts_generate_content_response())
|
||||
)
|
||||
speech_kwargs: Final = {
|
||||
"model": f"gemini/{GEMINI_TTS_MODEL}",
|
||||
"input": "hello from litellm",
|
||||
"voice": "Kore",
|
||||
"api_key": "fake-gemini-key",
|
||||
endpoint_param: GEMINI_GATEWAY_BASE,
|
||||
}
|
||||
|
||||
response: Final = litellm.speech(**speech_kwargs) if sync_mode else await litellm.aspeech(**speech_kwargs)
|
||||
|
||||
assert gateway_route.call_count == 1
|
||||
assert gateway_route.calls.last.request.headers["x-goog-api-key"] == "fake-gemini-key"
|
||||
assert b"pcm-audio-bytes" in response.content
|
||||
|
||||
|
||||
def test_speech_gemini_tts_applies_the_caller_timeout(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())
|
||||
)
|
||||
|
||||
litellm.speech(
|
||||
model=f"gemini/{GEMINI_TTS_MODEL}", input="hello", voice="Kore", api_key="fake-gemini-key", timeout=7.5
|
||||
)
|
||||
|
||||
assert route.calls.last.request.extensions["timeout"] == httpx.Timeout(7.5).as_dict()
|
||||
|
||||
|
||||
def test_speech_gemini_tts_without_a_timeout_waits_as_long_as_the_same_chat_completion(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())
|
||||
)
|
||||
|
||||
litellm.speech(model=f"gemini/{GEMINI_TTS_MODEL}", input="hello", voice="Kore", api_key="fake-gemini-key")
|
||||
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",
|
||||
)
|
||||
|
||||
speech_call, chat_call = route.calls
|
||||
assert speech_call.request.extensions["timeout"] == chat_call.request.extensions["timeout"]
|
||||
|
||||
|
||||
FOUNDRY_HOST: Final = "https://my-project.services.ai.azure.com"
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue