diff --git a/ci_cd/generate_model_prices_schema.py b/ci_cd/generate_model_prices_schema.py index ee0dad25c81..ab29b70bdd4 100644 --- a/ci_cd/generate_model_prices_schema.py +++ b/ci_cd/generate_model_prices_schema.py @@ -121,10 +121,6 @@ ARRAY_KEYS: dict[str, JsonSchema] = { } INTEGER_KEYS: dict[str, JsonSchema] = { - "max_audio_per_prompt": { - **NONNEG_INTEGER, - "description": "Maximum number of audio outputs accepted or generated per prompt.", - }, "max_tokens": { **NONNEG_INTEGER, "description": "Legacy field: max output tokens if the provider specifies it, else max input tokens.", @@ -150,14 +146,6 @@ INTEGER_KEYS: dict[str, JsonSchema] = { } NUMBER_KEYS: dict[str, JsonSchema] = { - "audio_seconds_per_prediction": { - **NONNEG_NUMBER, - "description": "Audio duration, in seconds, produced by one prediction.", - }, - "max_audio_length_hours": { - **NONNEG_NUMBER, - "description": "Maximum generated audio duration, expressed in hours.", - }, "regional_processing_uplift_multiplier_eu": { "type": "number", "minimum": 1, diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index fa8e1a5264c..eabf2c6249b 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -80,6 +80,7 @@ from litellm.llms.together_ai.cost_calculator import ( get_model_params_and_category, has_together_registry_pricing, ) +from litellm.llms.vertex_ai.common_utils import get_vertex_ai_lyria_generation_cost from litellm.llms.vertex_ai.cost_calculator import ( cost_per_character as google_cost_per_character, ) @@ -495,16 +496,17 @@ def cost_per_token( # see this https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models if call_type == "speech" or call_type == "aspeech": - if custom_llm_provider in ("vertex_ai", "vertex_ai_beta"): - from litellm.llms.vertex_ai.common_utils import get_vertex_ai_lyria_generation_cost - - lyria_generation_cost: Final = get_vertex_ai_lyria_generation_cost(model_without_prefix) - if lyria_generation_cost is not None: - return 0.0, lyria_generation_cost + lyria_generation_cost: Final = ( + get_vertex_ai_lyria_generation_cost(model=model_without_prefix) + if custom_llm_provider in ("vertex_ai", "vertex_ai_beta") + else None + ) + if lyria_generation_cost is not None: + return 0.0, lyria_generation_cost speech_model_info = litellm.get_model_info(model=model_without_prefix, custom_llm_provider=custom_llm_provider) + cost_metric: Final = select_cost_metric_for_model(speech_model_info) prompt_cost: float = 0.0 completion_cost: float = 0.0 - cost_metric: Final = select_cost_metric_for_model(speech_model_info) if cost_metric == "cost_per_character": if prompt_characters is None: raise ValueError( diff --git a/litellm/llms/vertex_ai/common_utils.py b/litellm/llms/vertex_ai/common_utils.py index 3895ea25434..38cdf334cc3 100644 --- a/litellm/llms/vertex_ai/common_utils.py +++ b/litellm/llms/vertex_ai/common_utils.py @@ -1,4 +1,5 @@ import re +from collections.abc import Mapping from copy import deepcopy from enum import Enum from functools import lru_cache @@ -29,8 +30,6 @@ class VertexAILyriaModelInfo(TypedDict): vertex_ai_audio_api: ReadOnly[Literal["lyria_predict", "lyria_interactions"]] supported_audio_formats: ReadOnly[tuple[Literal["mp3", "wav"], ...]] output_cost_per_image: NotRequired[ReadOnly[float]] - output_cost_per_second: NotRequired[ReadOnly[float]] - audio_seconds_per_prediction: NotRequired[ReadOnly[float]] _VERTEX_AI_LYRIA_MODEL_INFO_ADAPTER: Final = TypeAdapter(VertexAILyriaModelInfo) @@ -45,37 +44,41 @@ def _validate_vertex_ai_lyria_model_info(raw_model_info: object) -> VertexAILyri return None -@lru_cache(maxsize=32) -def _get_bundled_vertex_ai_lyria_model_info(model_key: str) -> VertexAILyriaModelInfo | None: +@lru_cache(maxsize=1) +def _bundled_vertex_ai_lyria_model_infos() -> Mapping[str, VertexAILyriaModelInfo]: from litellm.litellm_core_utils.get_model_cost_map import GetModelCostMap - bundled_model_info: Final = GetModelCostMap.load_local_model_cost_map().get(model_key) - return _validate_vertex_ai_lyria_model_info(bundled_model_info) + return MappingProxyType( + { + model_key: lyria_model_info + for model_key, raw_model_info in GetModelCostMap.load_local_model_cost_map().items() + if (lyria_model_info := _validate_vertex_ai_lyria_model_info(raw_model_info)) is not None + } + ) + + +def _vertex_ai_lyria_model_key(model: str) -> str: + return model if model.startswith("vertex_ai/") else f"vertex_ai/{model}" + + +def _vertex_ai_lyria_generation_cost(model_info: VertexAILyriaModelInfo | None) -> float | None: + return None if model_info is None else model_info.get("output_cost_per_image") def get_vertex_ai_lyria_model_info(model: str) -> VertexAILyriaModelInfo | None: - model_key: Final = model if model.startswith("vertex_ai/") else f"vertex_ai/{model}" + model_key: Final = _vertex_ai_lyria_model_key(model) runtime_model_info: Final = _validate_vertex_ai_lyria_model_info(litellm.model_cost.get(model_key)) - bundled_model_info: Final = _get_bundled_vertex_ai_lyria_model_info(model_key) - if runtime_model_info is None: - return bundled_model_info - if bundled_model_info is None: - return runtime_model_info - return _validate_vertex_ai_lyria_model_info(MappingProxyType({**bundled_model_info, **runtime_model_info})) + return runtime_model_info or _bundled_vertex_ai_lyria_model_infos().get(model_key) def get_vertex_ai_lyria_generation_cost(model: str) -> float | None: - model_info: Final = get_vertex_ai_lyria_model_info(model) - if model_info is None: - return None - generation_cost: Final = model_info.get("output_cost_per_image") - if generation_cost is not None: - return generation_cost - cost_per_second: Final = model_info.get("output_cost_per_second") - seconds_per_prediction: Final = model_info.get("audio_seconds_per_prediction") - if cost_per_second is None or seconds_per_prediction is None: - return None - return cost_per_second * seconds_per_prediction + model_key: Final = _vertex_ai_lyria_model_key(model) + runtime_cost: Final = _vertex_ai_lyria_generation_cost( + _validate_vertex_ai_lyria_model_info(litellm.model_cost.get(model_key)) + ) + if runtime_cost is not None: + return runtime_cost + return _vertex_ai_lyria_generation_cost(_bundled_vertex_ai_lyria_model_infos().get(model_key)) class VertexAIError(BaseLLMException): diff --git a/litellm/llms/vertex_ai/text_to_speech/transformation.py b/litellm/llms/vertex_ai/text_to_speech/transformation.py index 5a93aca3ac9..f489366b8d8 100644 --- a/litellm/llms/vertex_ai/text_to_speech/transformation.py +++ b/litellm/llms/vertex_ai/text_to_speech/transformation.py @@ -15,6 +15,7 @@ import httpx import litellm from litellm.exceptions import UnsupportedParamsError from litellm.litellm_core_utils.audio_utils.utils import ( + DEFAULT_SPEECH_MEDIA_TYPE, speech_media_type_from_audio_bytes, ) from litellm.litellm_core_utils.url_utils import encode_url_path_segment @@ -571,13 +572,11 @@ class VertexAILyriaTextToSpeechConfig(VertexAITextToSpeechConfig): VertexAIInteractionsConfig, ) - resolved_project: Final = project - def mint_access_token( _credentials: VERTEX_CREDENTIALS_TYPES | None, project_id: str | None, ) -> tuple[str, str]: - return "", project_id or resolved_project + return "", project_id or project return VertexAIInteractionsConfig(mint_access_token=mint_access_token).get_complete_url( api_base=api_base, @@ -681,24 +680,12 @@ class VertexAILyriaTextToSpeechConfig(VertexAITextToSpeechConfig): ) # rebind-ok: interactions response supplies its audio MIME type if audio_data is None: raise ValueError(f"No generated audio found in Vertex AI {base_model} response") - decoded_audio: Final = base64.b64decode(audio_data) - default_format: Final = model_info["supported_audio_formats"][0] - mime_type = ( - mime_type - or speech_media_type_from_audio_bytes(decoded_audio) - or { # mutable-ok: short-lived lookup selects the default response MIME type; rebind-ok: absent provider MIME type falls back to model metadata - "mp3": "audio/mpeg", - "wav": "audio/wav", - }[default_format] - ) - response: Final = HttpxBinaryResponseContent( + binary_data: Final = base64.b64decode(audio_data) + media_type: Final = mime_type or speech_media_type_from_audio_bytes(binary_data) or DEFAULT_SPEECH_MEDIA_TYPE + return HttpxBinaryResponseContent( httpx.Response( status_code=raw_response.status_code, - content=decoded_audio, - headers={ # mutable-ok: httpx requires a concrete response header dictionary - "content-type": mime_type - }, + content=binary_data, + headers=MappingProxyType({"content-type": media_type}), ) ) - response.set_audio_mime_type(mime_type) - return response diff --git a/litellm/main.py b/litellm/main.py index 040af897256..44afce08c0a 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -8260,14 +8260,11 @@ def speech( # Vertex AI Text-to-Speech (Google Cloud TTS) if text_to_speech_provider_config is None: - if VertexAILyriaTextToSpeechConfig.is_lyria_model(model): - text_to_speech_provider_config = ( - VertexAILyriaTextToSpeechConfig() - ) # rebind-ok: model metadata selects the Lyria provider implementation - else: - text_to_speech_provider_config = ( - VertexAITextToSpeechConfig() - ) # rebind-ok: non-Lyria Vertex models use the standard TTS implementation + text_to_speech_provider_config = ( # rebind-ok: model metadata selects the Vertex TTS implementation + VertexAILyriaTextToSpeechConfig() + if VertexAILyriaTextToSpeechConfig.is_lyria_model(model) + else VertexAITextToSpeechConfig() + ) # Cast to specific Vertex AI config type to access dispatch method vertex_config: Final = cast(VertexAITextToSpeechConfig, text_to_speech_provider_config) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 9986d5056d1..0bc6c59a4b7 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -45512,12 +45512,9 @@ "supports_tool_choice": true }, "vertex_ai/lyria-002": { - "audio_seconds_per_prediction": 30, "litellm_provider": "vertex_ai", - "max_audio_length_hours": 0.009111111111111111, - "max_audio_per_prompt": 4, - "mode": "audio_speech", - "output_cost_per_second": 0.002, + "mode": "audio_speech", + "output_cost_per_image": 0.06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing#lyria", "supported_audio_formats": [ "wav" diff --git a/litellm/proxy/pass_through_endpoints/llm_provider_handlers/vertex_passthrough_logging_handler.py b/litellm/proxy/pass_through_endpoints/llm_provider_handlers/vertex_passthrough_logging_handler.py index afdbaf3b06c..119a53c2411 100644 --- a/litellm/proxy/pass_through_endpoints/llm_provider_handlers/vertex_passthrough_logging_handler.py +++ b/litellm/proxy/pass_through_endpoints/llm_provider_handlers/vertex_passthrough_logging_handler.py @@ -1,6 +1,5 @@ import asyncio import re -from collections.abc import Mapping from datetime import datetime from typing import TYPE_CHECKING, Any, Final, cast from urllib.parse import urlparse @@ -346,9 +345,7 @@ class VertexPassthroughLoggingHandler: prediction_count: Final = VertexPassthroughLoggingHandler._get_audio_prediction_count( json_response=json_response ) - response_cost: Final = ( - VertexPassthroughLoggingHandler._get_audio_prediction_unit_cost(model=model) or 0.0 - ) * prediction_count + response_cost: Final = (get_vertex_ai_lyria_generation_cost(model=model) or 0.0) * prediction_count logging_obj.model = model # rebind-ok: passthrough attribution records the resolved Vertex model logging_obj.model_call_details[ # rebind-ok: passthrough attribution enriches callback metadata @@ -387,30 +384,9 @@ class VertexPassthroughLoggingHandler: ) -> bool: return ( VertexPassthroughLoggingHandler._get_audio_prediction_count(json_response=json_response) > 0 - and VertexPassthroughLoggingHandler._get_audio_prediction_unit_cost(model=model) is not None + and get_vertex_ai_lyria_generation_cost(model=model) is not None ) - @staticmethod - def _get_audio_prediction_unit_cost(model: str) -> float | None: - runtime_unit_cost: Final = VertexPassthroughLoggingHandler._audio_prediction_unit_cost_from_model_info( - model_info=litellm.model_cost.get(f"vertex_ai/{model}") - ) - if runtime_unit_cost is not None: - return runtime_unit_cost - return get_vertex_ai_lyria_generation_cost(model=model) - - @staticmethod - def _audio_prediction_unit_cost_from_model_info(model_info: object) -> float | None: - if not isinstance(model_info, Mapping): - return None - output_cost_per_second: Final = model_info.get("output_cost_per_second") - audio_seconds_per_prediction: Final = model_info.get("audio_seconds_per_prediction") - if not isinstance(output_cost_per_second, (int, float)) or not isinstance( - audio_seconds_per_prediction, (int, float) - ): - return None - return float(output_cost_per_second * audio_seconds_per_prediction) - @staticmethod def _get_audio_prediction_count( json_response: dict, # mutable-ok: counter inspects the decoded provider response dictionary without mutation diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index f11cbc92224..27132c90e05 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -11176,14 +11176,9 @@ async def audio_speech( upstream_content_type: Final = ( response.response.headers.get("content-type") if isinstance(response, HttpxBinaryResponseContent) else None ) - hidden_audio_mime_type: Final = hidden_params.get("audio_mime_type") - media_type: Final = ( - hidden_audio_mime_type - if isinstance(hidden_audio_mime_type, str) - else resolve_speech_media_type( - upstream_content_type=upstream_content_type, - response_format=requested_format if isinstance(requested_format, str) else None, - ) + media_type: Final = resolve_speech_media_type( + upstream_content_type=upstream_content_type, + response_format=requested_format if isinstance(requested_format, str) else None, ) return StreamingResponse( diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py index 0b13191d977..32d88da0085 100644 --- a/litellm/types/llms/openai.py +++ b/litellm/types/llms/openai.py @@ -119,9 +119,6 @@ class HttpxBinaryResponseContent(_HttpxBinaryResponseContent): return self._hidden_params["response_cost"] = response_cost - def set_audio_mime_type(self, audio_mime_type: str) -> None: - self._hidden_params["audio_mime_type"] = audio_mime_type - class NotGiven: """ diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 6c645e70bee..e29944919ba 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -312,9 +312,6 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False): output_cost_per_video_per_second: float | None # only for vertex ai models output_cost_per_audio_per_second: float | None # only for vertex ai models output_cost_per_second: float | None # for OpenAI Speech models - audio_seconds_per_prediction: ReadOnly[float | None] - max_audio_length_hours: ReadOnly[float | None] - max_audio_per_prompt: ReadOnly[int | None] output_cost_per_second_1080p: ( float | None ) # video_generation tier: key output_cost_per_second_ (e.g. 1080p, 720p) diff --git a/litellm/utils.py b/litellm/utils.py index d6a68b3ce6e..f6b40d33a79 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -5880,9 +5880,6 @@ def _get_model_info_helper( "output_cost_per_token_above_512k_tokens", None ), output_cost_per_second=_model_info.get("output_cost_per_second", None), - audio_seconds_per_prediction=_model_info.get("audio_seconds_per_prediction", None), - max_audio_length_hours=_model_info.get("max_audio_length_hours", None), - max_audio_per_prompt=_model_info.get("max_audio_per_prompt", None), output_cost_per_second_1080p=_model_info.get("output_cost_per_second_1080p", None), output_cost_per_second_480p=_model_info.get("output_cost_per_second_480p", None), output_cost_per_second_4k=_model_info.get("output_cost_per_second_4k", None), diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 9986d5056d1..0bc6c59a4b7 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -45512,12 +45512,9 @@ "supports_tool_choice": true }, "vertex_ai/lyria-002": { - "audio_seconds_per_prediction": 30, "litellm_provider": "vertex_ai", - "max_audio_length_hours": 0.009111111111111111, - "max_audio_per_prompt": 4, - "mode": "audio_speech", - "output_cost_per_second": 0.002, + "mode": "audio_speech", + "output_cost_per_image": 0.06, "source": "https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing#lyria", "supported_audio_formats": [ "wav" diff --git a/model_prices_and_context_window.schema.json b/model_prices_and_context_window.schema.json index 58ca91f3977..6faa957dc16 100644 --- a/model_prices_and_context_window.schema.json +++ b/model_prices_and_context_window.schema.json @@ -53,11 +53,6 @@ "type": "number", "minimum": 0 }, - "audio_seconds_per_prediction": { - "type": "number", - "minimum": 0, - "description": "Audio duration, in seconds, produced by one prediction." - }, "audio_transcription_config": { "type": "string" }, @@ -368,16 +363,6 @@ "type": "string", "description": "LiteLLM provider slug; one of https://docs.litellm.ai/docs/providers." }, - "max_audio_length_hours": { - "type": "number", - "minimum": 0, - "description": "Maximum generated audio duration, expressed in hours." - }, - "max_audio_per_prompt": { - "type": "integer", - "minimum": 0, - "description": "Maximum number of audio outputs accepted or generated per prompt." - }, "max_input_tokens": { "type": "integer", "minimum": 0, diff --git a/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py b/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py index d1d751989ea..48376872a72 100644 --- a/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py +++ b/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py @@ -1696,3 +1696,44 @@ def test_vertex_text_embedding_request_includes_labels_from_metadata(): }, ) assert req.get("labels") == {"project_id": "cost-center-1"} + + +@pytest.mark.parametrize( + ("model", "expected_api"), + [ + ("lyria-002", "lyria_predict"), + ("vertex_ai/lyria-002", "lyria_predict"), + ("lyria-3-clip-preview", "lyria_interactions"), + ("lyria-3-pro-preview", "lyria_interactions"), + ], +) +def test_get_vertex_ai_lyria_model_info_resolves_audio_api(model, expected_api): + from litellm.llms.vertex_ai.common_utils import get_vertex_ai_lyria_model_info + + model_info = get_vertex_ai_lyria_model_info(model=model) + + assert model_info is not None + assert model_info["vertex_ai_audio_api"] == expected_api + + +@pytest.mark.parametrize("model", ["en-US-Studio-O", "gemini-2.5-flash-preview-tts", "chirp-3-hd-charon"]) +def test_get_vertex_ai_lyria_model_info_is_none_for_non_lyria_speech_models(model): + from litellm.llms.vertex_ai.common_utils import get_vertex_ai_lyria_model_info + + assert get_vertex_ai_lyria_model_info(model=model) is None + + +def test_get_vertex_ai_lyria_model_info_falls_back_to_bundled_map(monkeypatch): + import litellm + from litellm.llms.vertex_ai.common_utils import get_vertex_ai_lyria_model_info + + stale_runtime_model_cost = { + key: value for key, value in litellm.model_cost.items() if not key.startswith("vertex_ai/lyria") + } + monkeypatch.setattr(litellm, "model_cost", stale_runtime_model_cost) + + model_info = get_vertex_ai_lyria_model_info(model="lyria-3-pro-preview") + + assert model_info is not None + assert model_info["vertex_ai_audio_api"] == "lyria_interactions" + assert model_info["supported_audio_formats"] == ("mp3", "wav") diff --git a/tests/test_litellm/llms/vertex_ai/test_vertex_passthrough_logging_handler.py b/tests/test_litellm/llms/vertex_ai/test_vertex_passthrough_logging_handler.py index 2cf6689261e..682dadfe854 100644 --- a/tests/test_litellm/llms/vertex_ai/test_vertex_passthrough_logging_handler.py +++ b/tests/test_litellm/llms/vertex_ai/test_vertex_passthrough_logging_handler.py @@ -18,8 +18,9 @@ def test_lyria_predict_response_preserves_audio_response_and_logs_cost( litellm.model_cost, "vertex_ai/lyria-002", { - "audio_seconds_per_prediction": 30, - "output_cost_per_second": 0.002, + "vertex_ai_audio_api": "lyria_predict", + "supported_audio_formats": ["wav"], + "output_cost_per_image": 0.06, }, ) logging_obj = MagicMock() @@ -79,8 +80,9 @@ def test_audio_predict_response_uses_model_map_metadata( litellm.model_cost, "vertex_ai/music-audio-preview", { - "audio_seconds_per_prediction": 12, - "output_cost_per_second": 0.5, + "vertex_ai_audio_api": "lyria_predict", + "supported_audio_formats": ["wav"], + "output_cost_per_image": 0.5, }, ) logging_obj = MagicMock() @@ -109,8 +111,8 @@ def test_audio_predict_response_uses_model_map_metadata( ) assert result["kwargs"]["model"] == "music-audio-preview" - assert result["kwargs"]["response_cost"] == pytest.approx(6.0) - assert logging_obj.model_call_details["response_cost"] == pytest.approx(6.0) + assert result["kwargs"]["response_cost"] == pytest.approx(0.5) + assert logging_obj.model_call_details["response_cost"] == pytest.approx(0.5) def test_audio_predict_response_supports_bytes_base64_encoded( @@ -120,8 +122,9 @@ def test_audio_predict_response_supports_bytes_base64_encoded( litellm.model_cost, "vertex_ai/lyria-002", { - "audio_seconds_per_prediction": 30, - "output_cost_per_second": 0.002, + "vertex_ai_audio_api": "lyria_predict", + "supported_audio_formats": ["wav"], + "output_cost_per_image": 0.06, }, ) logging_obj = MagicMock() @@ -146,27 +149,23 @@ def test_audio_predict_response_supports_bytes_base64_encoded( assert logging_obj.model_call_details["response_cost"] == pytest.approx(0.06) -@pytest.mark.parametrize( - "missing_fields", - ( - None, - ("output_cost_per_second",), - ("audio_seconds_per_prediction",), - ("output_cost_per_second", "audio_seconds_per_prediction"), - ), -) +@pytest.mark.parametrize("runtime_entry_is_missing", (True, False)) def test_lyria_predict_cost_falls_back_to_bundled_map_when_runtime_metadata_is_incomplete( monkeypatch: pytest.MonkeyPatch, - missing_fields: tuple[str, ...] | None, + runtime_entry_is_missing: bool, local_model_cost_map: None, ) -> None: - if missing_fields is None: + if runtime_entry_is_missing: monkeypatch.delitem(litellm.model_cost, "vertex_ai/lyria-002") else: monkeypatch.setitem( litellm.model_cost, "vertex_ai/lyria-002", - {key: value for key, value in litellm.model_cost["vertex_ai/lyria-002"].items() if key not in missing_fields}, + { + key: value + for key, value in litellm.model_cost["vertex_ai/lyria-002"].items() + if key != "output_cost_per_image" + }, ) logging_obj = MagicMock() logging_obj.model_call_details = {} @@ -193,7 +192,7 @@ def test_lyria_predict_cost_falls_back_to_bundled_map_when_runtime_metadata_is_i request_body={"instances": [{"prompt": "ambient piano"}]}, ) - if missing_fields is None: + if runtime_entry_is_missing: assert "vertex_ai/lyria-002" not in litellm.model_cost assert result["kwargs"]["model"] == "lyria-002" assert result["kwargs"]["response_cost"] == pytest.approx(0.06) diff --git a/tests/test_litellm/llms/vertex_ai/text_to_speech/test_transformation.py b/tests/test_litellm/llms/vertex_ai/text_to_speech/test_transformation.py index 4f4df69780a..b5eec42b569 100644 --- a/tests/test_litellm/llms/vertex_ai/text_to_speech/test_transformation.py +++ b/tests/test_litellm/llms/vertex_ai/text_to_speech/test_transformation.py @@ -169,23 +169,6 @@ def test_transform_text_to_speech_response_leaves_unknown_bytes_unlabeled(): class TestVertexAILyriaTextToSpeechConfig: - def test_response_without_mime_type_uses_audio_container(self) -> None: - audio: Final = b"RIFF\x24\x00\x00\x00WAVEfmt \x10\x00\x00\x00" - raw_response: Final = httpx.Response( - 200, - json={"outputs": [{"type": "audio", "data": base64.b64encode(audio).decode()}]}, - ) - - response: Final = VertexAILyriaTextToSpeechConfig().transform_text_to_speech_response( - model="lyria-3-pro-preview", - raw_response=raw_response, - logging_obj=MagicMock(), - ) - - assert response.content == audio - assert response.response.headers["content-type"] == "audio/wav" - assert response._hidden_params["audio_mime_type"] == "audio/wav" - @pytest.mark.parametrize( "model", ["lyria-002", "vertex_ai/lyria-3-clip-preview", "lyria-3-pro-preview"], @@ -385,11 +368,11 @@ class TestVertexAILyriaTextToSpeechConfig: { "predictions": [ { - "bytesBase64Encoded": "bHlyaWEtMi1hdWRpbw==", + "bytesBase64Encoded": "UklGRiQAAABXQVZFZm10IA==", } ] }, - b"lyria-2-audio", + b"RIFF$\x00\x00\x00WAVEfmt ", "audio/wav", ), ( @@ -427,6 +410,19 @@ class TestVertexAILyriaTextToSpeechConfig: b"lyria-3-audio", "audio/mpeg", ), + ( + "lyria-3-pro-preview", + { + "outputs": [ + { + "type": "audio", + "data": "UklGRiQAAABXQVZFZm10IA==", + } + ] + }, + b"RIFF$\x00\x00\x00WAVEfmt ", + "audio/wav", + ), ], ) def test_transform_response( @@ -446,7 +442,7 @@ class TestVertexAILyriaTextToSpeechConfig: ) assert response.content == expected_audio - assert response._hidden_params["audio_mime_type"] == expected_mime_type + assert response.response.headers["content-type"] == expected_mime_type @pytest.mark.parametrize( ("model", "response_format"), diff --git a/tests/test_litellm/test_cost_calculator.py b/tests/test_litellm/test_cost_calculator.py index aa630c4916b..3236253a10a 100644 --- a/tests/test_litellm/test_cost_calculator.py +++ b/tests/test_litellm/test_cost_calculator.py @@ -172,16 +172,15 @@ def test_vertex_lyria_speech_cost( monkeypatch.setitem( litellm.model_cost, model, - { - key: value - for key, value in model_info.items() - if key not in ("output_cost_per_image", "output_cost_per_second", "audio_seconds_per_prediction") - }, + {key: value for key, value in model_info.items() if key != "output_cost_per_image"}, ) elif runtime_state in ("custom_zero", "custom_price"): - cost_key: Final = "output_cost_per_image" if "output_cost_per_image" in model_info else "output_cost_per_second" multiplier: Final = 0 if runtime_state == "custom_zero" else 2 - monkeypatch.setitem(litellm.model_cost, model, {**model_info, cost_key: model_info[cost_key] * multiplier}) + monkeypatch.setitem( + litellm.model_cost, + model, + {**model_info, "output_cost_per_image": model_info["output_cost_per_image"] * multiplier}, + ) cost: Final = completion_cost( model=model, diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py index a179a82fcb2..f934b2aca1e 100644 --- a/tests/test_litellm/test_utils.py +++ b/tests/test_litellm/test_utils.py @@ -944,14 +944,11 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "code_interpreter_cost_per_session": {"type": "number"}, "inference_geo": {"type": "string"}, "litellm_provider": {"type": "string"}, - "max_audio_length_hours": {"type": "number"}, - "max_audio_per_prompt": {"type": "number"}, "max_input_tokens": {"type": "number"}, "max_output_tokens": {"type": "number"}, "max_tokens": {"type": "number"}, "metadata": {"type": "object"}, "provider_specific_entry": {"type": "object"}, - "audio_seconds_per_prediction": {"type": "number"}, "mode": { "type": "string", "enum": [ @@ -2869,8 +2866,7 @@ def test_vertex_ai_lyria_models_in_cost_map(): assert lyria_2["mode"] == "audio_speech" assert clip["mode"] == "audio_speech" assert pro["mode"] == "audio_speech" - assert lyria_2["audio_seconds_per_prediction"] == 30 - assert lyria_2["output_cost_per_second"] == 0.002 + assert lyria_2["output_cost_per_image"] == 0.06 assert lyria_2["supported_modalities"] == ["text"] assert lyria_2["supported_output_modalities"] == ["audio"] assert lyria_2["supports_audio_output"] is True