diff --git a/litellm/llms/vertex_ai/videos/transformation.py b/litellm/llms/vertex_ai/videos/transformation.py index 9389a5f419d..bac3d31501b 100644 --- a/litellm/llms/vertex_ai/videos/transformation.py +++ b/litellm/llms/vertex_ai/videos/transformation.py @@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Any, Final, cast import httpx from httpx._types import RequestFiles +import litellm from litellm.constants import DEFAULT_GOOGLE_VIDEO_DURATION_SECONDS from litellm.images.utils import ImageEditRequestUtils from litellm.llms.base_llm.videos.transformation import BaseVideoConfig @@ -146,7 +147,7 @@ class VertexAIVideoConfig(BaseVideoConfig, VertexBase): - prompt → prompt (in instances) - input_reference → image (in instances) - size → aspectRatio (e.g., "1280x720" → "16:9") - - size → resolution for Veo 3 models when inferable + - size → resolution for models with resolution-tier pricing when inferable ("1280x720"/"720x1280" → "720p", "1920x1080"/"1080x1920" → "1080p"); skipped if ``resolution`` is already set - seconds → durationSeconds (defaults to 4 seconds if not provided) @@ -177,7 +178,7 @@ class VertexAIVideoConfig(BaseVideoConfig, VertexBase): has_resolution = "resolution" in mapped_params or ( isinstance(nested_params, dict) and nested_params.get("resolution") is not None ) - supports_resolution = model.removeprefix("vertex_ai/").startswith("veo-3.") + supports_resolution = self._supports_resolution_inference(model) if supports_resolution and not has_resolution: inferred_resolution = self._convert_size_to_resolution(size) if inferred_resolution is not None: @@ -210,6 +211,12 @@ class VertexAIVideoConfig(BaseVideoConfig, VertexBase): def _convert_size_to_resolution(self, size: str) -> str | None: return self._OPENAI_VIDEO_SIZE_TO_RESOLUTION.get(size) + @staticmethod + def _supports_resolution_inference(model: str) -> bool: + model_key = model if model.startswith("vertex_ai/") else f"vertex_ai/{model}" + model_info = litellm.model_cost.get(model_key, {}) + return model_info.get("output_cost_per_second_1080p") is not None + def validate_environment( self, headers: dict, diff --git a/tests/test_litellm/llms/vertex_ai/videos/test_vertex_video_transformation.py b/tests/test_litellm/llms/vertex_ai/videos/test_vertex_video_transformation.py index 1c2952cbb0b..11cea7c3d23 100644 --- a/tests/test_litellm/llms/vertex_ai/videos/test_vertex_video_transformation.py +++ b/tests/test_litellm/llms/vertex_ai/videos/test_vertex_video_transformation.py @@ -288,15 +288,33 @@ class TestVertexAIVideoConfig: assert "resolution" not in mapped @pytest.mark.parametrize( - ("size", "expected_resolution"), - (("1280x720", "720p"), ("1920x1080", "1080p")), + ("model", "size", "expected_resolution"), + ( + (VEO_31_LITE_VERTEX_MODEL, "1280x720", "720p"), + ( + VEO_31_LITE_VERTEX_MODEL.removeprefix("vertex_ai/"), + "1920x1080", + "1080p", + ), + ), ) - def test_map_openai_size_to_resolution_for_veo_3( - self, size: str, expected_resolution: str + def test_map_openai_size_to_resolution_for_resolution_tier_model( + self, + model: str, + size: str, + expected_resolution: str, + monkeypatch: pytest.MonkeyPatch, ): + model_cost = _load_model_cost_map(BACKUP_MODEL_COST_PATH) + monkeypatch.setitem( + litellm.model_cost, + VEO_31_LITE_VERTEX_MODEL, + dict(model_cost[VEO_31_LITE_VERTEX_MODEL]), + ) + mapped = self.config.map_openai_params( video_create_optional_params={"size": size}, - model=VEO_31_LITE_VERTEX_MODEL, + model=model, drop_params=False, ) @@ -313,6 +331,23 @@ class TestVertexAIVideoConfig: assert mapped["aspectRatio"] == "16:9" assert "resolution" not in mapped + def test_map_openai_size_does_not_infer_resolution_for_existing_veo_3( + self, monkeypatch: pytest.MonkeyPatch + ): + model = "veo-3.1-generate-001" + model_key = f"vertex_ai/{model}" + model_cost = _load_model_cost_map(BACKUP_MODEL_COST_PATH) + monkeypatch.setitem(litellm.model_cost, model_key, dict(model_cost[model_key])) + + mapped = self.config.map_openai_params( + video_create_optional_params={"size": "1920x1080"}, + model=model, + drop_params=False, + ) + + assert mapped["aspectRatio"] == "16:9" + assert "resolution" not in mapped + def test_map_openai_size_does_not_override_provider_resolution(self): mapped = self.config.map_openai_params( video_create_optional_params={