Fix: Passing of image and parameters in videos api

This commit is contained in:
Sameer Kankute
2026-02-26 14:49:46 +05:30
parent f78104d34c
commit aeb723816f
3 changed files with 193 additions and 6 deletions
@@ -119,6 +119,12 @@ class VertexAIVideoConfig(BaseVideoConfig, VertexBase):
# Map input_reference to image (will be processed in transform_video_create_request)
if "input_reference" in video_create_optional_params:
mapped_params["image"] = video_create_optional_params["input_reference"]
elif "image" in video_create_optional_params:
mapped_params["image"] = video_create_optional_params["image"]
# Pass through a provider-specific parameters block if provided directly
if "parameters" in video_create_optional_params:
mapped_params["parameters"] = video_create_optional_params["parameters"]
# Map size to aspectRatio
if "size" in video_create_optional_params:
@@ -270,7 +276,17 @@ class VertexAIVideoConfig(BaseVideoConfig, VertexBase):
instance_dict.update(params_copy["instances"])
params_copy.pop("instances")
elif "image" in params_copy and params_copy["image"] is not None:
image_data = _convert_image_to_vertex_format(params_copy["image"])
image = params_copy["image"]
if isinstance(image, dict):
# Already in Vertex format e.g. {"gcsUri": "gs://..."} or
# {"bytesBase64Encoded": "...", "mimeType": "..."}
image_data = image
elif isinstance(image, str) and image.startswith("gs://"):
# Bare GCS URI — Vertex AI accepts gcsUri natively, no download needed
image_data = {"gcsUri": image}
else:
# File-like object — encode to base64
image_data = _convert_image_to_vertex_format(image)
instance_dict["image"] = image_data
params_copy.pop("image")
+4 -1
View File
@@ -1,7 +1,8 @@
from typing import Any, Dict, List, Literal, Optional
from typing_extensions import TypedDict
from pydantic import BaseModel
from typing_extensions import TypedDict
from litellm.types.utils import FileTypes
@@ -72,6 +73,8 @@ class VideoCreateOptionalRequestParams(TypedDict, total=False):
Params here: https://platform.openai.com/docs/api-reference/videos/create
"""
input_reference: Optional[FileTypes] # File reference for input image
image: Optional[Any] # Image for image-to-video; dict with gcsUri/bytesBase64Encoded, or file-like object
parameters: Optional[Dict[str, Any]] # Provider-specific parameters block passed directly to the API
model: Optional[str]
seconds: Optional[str]
size: Optional[str]
@@ -1,19 +1,20 @@
"""
Tests for Vertex AI (Veo) video generation transformation.
"""
import base64
import json
import os
import pytest
from unittest.mock import Mock, MagicMock, patch
from unittest.mock import MagicMock, Mock, patch
import httpx
import base64
import pytest
from litellm.llms.vertex_ai.videos.transformation import (
VertexAIVideoConfig,
_convert_image_to_vertex_format,
)
from litellm.types.videos.main import VideoObject
from litellm.types.router import GenericLiteLLMParams
from litellm.types.videos.main import VideoObject
class TestVertexAIVideoConfig:
@@ -548,3 +549,170 @@ class TestConvertImageToVertexFormat:
decoded = base64.b64decode(result["bytesBase64Encoded"])
assert decoded == fake_image_data
class TestImageAndParametersPassthrough:
"""
Tests that image (gcsUri / bare gs:// / file-like) and a pre-built
parameters dict are correctly forwarded through map_openai_params and
transform_video_create_request.
"""
def setup_method(self):
self.config = VertexAIVideoConfig()
self.api_base = (
"https://us-central1-aiplatform.googleapis.com/v1/projects/"
"test-project/locations/us-central1/publishers/google/models/veo-002"
)
# ------------------------------------------------------------------ #
# map_openai_params #
# ------------------------------------------------------------------ #
def test_map_openai_params_passes_image_dict(self):
"""image dict (gcsUri format) is forwarded as-is."""
image = {"gcsUri": "gs://my-bucket/boardwalk.jpg"}
mapped = self.config.map_openai_params(
video_create_optional_params={"image": image},
model="veo-002",
drop_params=False,
)
assert mapped["image"] == image
def test_map_openai_params_passes_parameters_dict(self):
"""A pre-built parameters dict is forwarded as-is."""
params = {"sampleCount": 1, "videoLengthSeconds": 5, "aspectRatio": "16:9"}
mapped = self.config.map_openai_params(
video_create_optional_params={"parameters": params},
model="veo-002",
drop_params=False,
)
assert mapped["parameters"] == params
def test_map_openai_params_input_reference_takes_priority_over_image(self):
"""input_reference wins over a directly passed image key."""
mock_file = Mock()
image_dict = {"gcsUri": "gs://my-bucket/other.jpg"}
mapped = self.config.map_openai_params(
video_create_optional_params={
"input_reference": mock_file,
"image": image_dict,
},
model="veo-002",
drop_params=False,
)
assert mapped["image"] is mock_file
# ------------------------------------------------------------------ #
# transform_video_create_request image forms #
# ------------------------------------------------------------------ #
def test_transform_request_image_gcs_uri_dict(self):
"""image passed as {"gcsUri": "gs://..."} is placed in instances as-is."""
image = {"gcsUri": "gs://my-bucket/boardwalk.jpg"}
data, _, url = self.config.transform_video_create_request(
model="veo-002",
prompt="Cinematic drone shot",
api_base=self.api_base,
video_create_optional_request_params={"image": image},
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert data["instances"][0]["image"] == image
assert url.endswith(":predictLongRunning")
def test_transform_request_image_bare_gs_uri_string(self):
"""A bare gs:// string is wrapped in {"gcsUri": ...} without downloading."""
gs_uri = "gs://my-bucket/boardwalk.jpg"
data, _, _ = self.config.transform_video_create_request(
model="veo-002",
prompt="Cinematic drone shot",
api_base=self.api_base,
video_create_optional_request_params={"image": gs_uri},
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert data["instances"][0]["image"] == {"gcsUri": gs_uri}
def test_transform_request_image_bytes_base64_dict(self):
"""image already in bytesBase64Encoded format is passed through unchanged."""
image = {"bytesBase64Encoded": "abc123", "mimeType": "image/jpeg"}
data, _, _ = self.config.transform_video_create_request(
model="veo-002",
prompt="Cinematic drone shot",
api_base=self.api_base,
video_create_optional_request_params={"image": image},
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert data["instances"][0]["image"] == image
# ------------------------------------------------------------------ #
# transform_video_create_request parameters dict #
# ------------------------------------------------------------------ #
def test_transform_request_parameters_dict_not_double_nested(self):
"""A pre-built parameters dict becomes request_data["parameters"] directly."""
params = {"sampleCount": 1, "videoLengthSeconds": 5, "aspectRatio": "16:9"}
data, _, _ = self.config.transform_video_create_request(
model="veo-002",
prompt="Cinematic drone shot",
api_base=self.api_base,
video_create_optional_request_params={"parameters": params},
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert data["parameters"] == params
# Must NOT be double-nested
assert "parameters" not in data["parameters"]
# ------------------------------------------------------------------ #
# Full user scenario #
# ------------------------------------------------------------------ #
def test_transform_request_full_user_scenario(self):
"""
Reproduces the exact user request:
image: {"gcsUri": "gs://your-bucket-name/path/to/boardwalk.jpg"}
parameters: {"sampleCount": 1, "videoLengthSeconds": 5,
"aspectRatio": "16:9", "storageUri": "gs://test/outputs/"}
"""
image = {"gcsUri": "gs://your-bucket-name/path/to/boardwalk.jpg"}
parameters = {
"sampleCount": 1,
"videoLengthSeconds": 5,
"aspectRatio": "16:9",
"storageUri": "gs://test/outputs/",
}
# Simulate the full pipeline: map_openai_params → transform_video_create_request
mapped = self.config.map_openai_params(
video_create_optional_params={"image": image, "parameters": parameters},
model="veo-3.1-generate-preview",
drop_params=False,
)
data, _, url = self.config.transform_video_create_request(
model="veo-3.1-generate-preview",
prompt="Cinematic drone shot moving forward along the beach boardwalk",
api_base=self.api_base,
video_create_optional_request_params=mapped,
litellm_params=GenericLiteLLMParams(),
headers={},
)
# instances contains prompt + image
assert len(data["instances"]) == 1
instance = data["instances"][0]
assert instance["prompt"] == "Cinematic drone shot moving forward along the beach boardwalk"
assert instance["image"] == image
# parameters block is correct and not double-nested
assert data["parameters"] == parameters
assert "parameters" not in data["parameters"]
assert url.endswith(":predictLongRunning")