From 9012842c2ffd3d93f0ff2a372de87c787280b3fb Mon Sep 17 00:00:00 2001 From: Emerson Gomes Date: Wed, 15 Jul 2026 21:04:48 -0500 Subject: [PATCH] fix(vertex-ai): gate veo resolution inference --- .../llms/vertex_ai/videos/transformation.py | 30 ++++++++----------- litellm/types/videos/main.py | 1 + .../test_vertex_video_transformation.py | 24 ++++++++++++--- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/litellm/llms/vertex_ai/videos/transformation.py b/litellm/llms/vertex_ai/videos/transformation.py index 3e7859c49a8..9389a5f419d 100644 --- a/litellm/llms/vertex_ai/videos/transformation.py +++ b/litellm/llms/vertex_ai/videos/transformation.py @@ -97,6 +97,12 @@ class VertexAIVideoConfig(BaseVideoConfig, VertexBase): "720x1280": "9:16", "1080x1920": "9:16", } + _OPENAI_VIDEO_SIZE_TO_RESOLUTION: dict[str, str] = { + "1280x720": "720p", + "1920x1080": "1080p", + "720x1280": "720p", + "1080x1920": "1080p", + } def __init__(self): BaseVideoConfig.__init__(self) @@ -140,8 +146,9 @@ class VertexAIVideoConfig(BaseVideoConfig, VertexBase): - prompt → prompt (in instances) - input_reference → image (in instances) - size → aspectRatio (e.g., "1280x720" → "16:9") - - size → resolution when inferable ("1280x720"/"720x1280" → "720p", - "1920x1080"/"1080x1920" → "1080p"); skipped if ``resolution`` is already set + - size → resolution for Veo 3 models when inferable + ("1280x720"/"720x1280" → "720p", "1920x1080"/"1080x1920" → "1080p"); + skipped if ``resolution`` is already set - seconds → durationSeconds (defaults to 4 seconds if not provided) """ mapped_params: Final[dict[str, Any]] = {} @@ -168,10 +175,10 @@ class VertexAIVideoConfig(BaseVideoConfig, VertexBase): mapped_params["aspectRatio"] = aspect_ratio nested_params: Final = video_create_optional_params.get("parameters") has_resolution = "resolution" in mapped_params or ( - isinstance(nested_params, dict) - and nested_params.get("resolution") is not None + isinstance(nested_params, dict) and nested_params.get("resolution") is not None ) - if not has_resolution: + supports_resolution = model.removeprefix("vertex_ai/").startswith("veo-3.") + if supports_resolution and not has_resolution: inferred_resolution = self._convert_size_to_resolution(size) if inferred_resolution is not None: mapped_params["resolution"] = inferred_resolution @@ -201,18 +208,7 @@ class VertexAIVideoConfig(BaseVideoConfig, VertexBase): return self._OPENAI_VIDEO_SIZE_TO_ASPECT_RATIO.get(size, "16:9") def _convert_size_to_resolution(self, size: str) -> str | None: - if not size or size not in self._OPENAI_VIDEO_SIZE_TO_ASPECT_RATIO: - return None - try: - width, height = size.split("x", 1) - smaller_edge = min(int(width), int(height)) - except (ValueError, TypeError): - return None - if smaller_edge == 720: - return "720p" - if smaller_edge == 1080: - return "1080p" - return None + return self._OPENAI_VIDEO_SIZE_TO_RESOLUTION.get(size) def validate_environment( self, diff --git a/litellm/types/videos/main.py b/litellm/types/videos/main.py index 3677cec3c8f..4c57efe05b0 100644 --- a/litellm/types/videos/main.py +++ b/litellm/types/videos/main.py @@ -76,6 +76,7 @@ class VideoCreateOptionalRequestParams(TypedDict, total=False): image: Any | None # Image for image-to-video; dict with gcsUri/bytesBase64Encoded, or file-like object parameters: dict[str, Any] | None # Provider-specific parameters block passed directly to the API model: str | None + resolution: str | None seconds: str | None size: str | None characters: list[dict[str, str]] | None 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 ec0aa187ff0..1c2952cbb0b 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 @@ -285,17 +285,33 @@ class TestVertexAIVideoConfig: assert mapped["durationSeconds"] == 8 assert mapped["aspectRatio"] == "16:9" - assert mapped["resolution"] == "720p" + assert "resolution" not in mapped - def test_map_openai_size_to_1080p_resolution(self): + @pytest.mark.parametrize( + ("size", "expected_resolution"), + (("1280x720", "720p"), ("1920x1080", "1080p")), + ) + def test_map_openai_size_to_resolution_for_veo_3( + self, size: str, expected_resolution: str + ): mapped = self.config.map_openai_params( - video_create_optional_params={"size": "1920x1080"}, + video_create_optional_params={"size": size}, model=VEO_31_LITE_VERTEX_MODEL, drop_params=False, ) assert mapped["aspectRatio"] == "16:9" - assert mapped["resolution"] == "1080p" + assert mapped["resolution"] == expected_resolution + + def test_map_openai_size_does_not_infer_resolution_for_veo_2(self): + mapped = self.config.map_openai_params( + video_create_optional_params={"size": "1920x1080"}, + model="vertex_ai/veo-2.0-generate-001", + 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(