Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion litellm/llms/base_llm/videos/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,11 @@ def transform_video_content_request(
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
variant: Optional[str] = None,
) -> Tuple[str, Dict]:
"""
Transform the video content request into a URL and data/params

Returns:
Tuple[str, Dict]: (url, params) for the video content request
"""
Expand Down
5 changes: 5 additions & 0 deletions litellm/llms/custom_httpx/llm_http_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -5400,6 +5400,7 @@ def video_content_handler(
api_key: Optional[str] = None,
client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None,
_is_async: bool = False,
variant: Optional[str] = None,
) -> Union[bytes, Coroutine[Any, Any, bytes]]:
"""
Handle video content download requests.
Expand All @@ -5415,6 +5416,7 @@ def video_content_handler(
extra_headers=extra_headers,
api_key=api_key,
client=client,
variant=variant,
)

if client is None or not isinstance(client, HTTPHandler):
Expand Down Expand Up @@ -5446,6 +5448,7 @@ def video_content_handler(
api_base=api_base,
litellm_params=litellm_params,
headers=headers,
variant=variant,
)

try:
Expand Down Expand Up @@ -5488,6 +5491,7 @@ async def async_video_content_handler(
extra_headers: Optional[Dict[str, Any]] = None,
api_key: Optional[str] = None,
client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None,
variant: Optional[str] = None,
) -> bytes:
"""
Async version of the video content download handler.
Expand Down Expand Up @@ -5522,6 +5526,7 @@ async def async_video_content_handler(
api_base=api_base,
litellm_params=litellm_params,
headers=headers,
variant=variant,
)

try:
Expand Down
3 changes: 2 additions & 1 deletion litellm/llms/gemini/videos/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,10 +393,11 @@ def transform_video_content_request(
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
variant: Optional[str] = None,
) -> Tuple[str, Dict]:
"""
Transform the video content request for Veo API.

For Veo, we need to:
1. Get operation status to extract video URI
2. Return download URL for the video
Expand Down
10 changes: 7 additions & 3 deletions litellm/llms/openai/videos/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,18 +172,22 @@ def transform_video_content_request(
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
variant: Optional[str] = None,
) -> Tuple[str, Dict]:
"""
Transform the video content request for OpenAI API.

OpenAI API expects the following request:
- GET /v1/videos/{video_id}/content
- GET /v1/videos/{video_id}/content?variant=thumbnail
"""
original_video_id = extract_original_video_id(video_id)

# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"

if variant is not None:
url = f"{url}?variant={variant}"

# No additional data needed for GET content request
data: Dict[str, Any] = {}

Expand Down
3 changes: 2 additions & 1 deletion litellm/llms/runwayml/videos/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,11 @@ def transform_video_content_request(
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
variant: Optional[str] = None,
) -> Tuple[str, Dict]:
"""
Transform the video content request for RunwayML API.

RunwayML doesn't have a separate content download endpoint.
The video URL is returned in the task output field.
We'll retrieve the task and extract the video URL.
Expand Down
1 change: 1 addition & 0 deletions litellm/llms/vertex_ai/videos/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ def transform_video_content_request(
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
variant: Optional[str] = None,
) -> Tuple[str, Dict]:
"""
Transform the video content request for Veo API.
Expand Down
4 changes: 4 additions & 0 deletions litellm/videos/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ def video_content(
video_id: str,
timeout: Optional[float] = None,
custom_llm_provider: Optional[str] = None,
variant: Optional[str] = None,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Optional[Dict[str, Any]] = None,
Expand Down Expand Up @@ -367,6 +368,7 @@ def video_content(
extra_headers=extra_headers,
client=kwargs.get("client"),
_is_async=_is_async,
variant=variant,
)

except Exception as e:
Expand All @@ -385,6 +387,7 @@ async def avideo_content(
video_id: str,
timeout: Optional[float] = None,
custom_llm_provider: Optional[str] = None,
variant: Optional[str] = None,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Optional[Dict[str, Any]] = None,
Expand Down Expand Up @@ -422,6 +425,7 @@ async def avideo_content(
video_id=video_id,
timeout=timeout,
custom_llm_provider=custom_llm_provider,
variant=variant,
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
Expand Down
77 changes: 77 additions & 0 deletions tests/test_litellm/test_video_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,83 @@ def test_openai_transform_video_content_request_empty_params():
assert params == {}


@pytest.mark.parametrize(
"variant,expected_suffix",
[
("thumbnail", "?variant=thumbnail"),
("spritesheet", "?variant=spritesheet"),
],
)
def test_openai_transform_video_content_request_with_variant(variant, expected_suffix):
"""OpenAI content transform should append ?variant= when variant is provided."""
config = OpenAIVideoConfig()
url, params = config.transform_video_content_request(
video_id="video_123",
api_base="https://api.openai.com/v1/videos",
litellm_params={},
headers={},
variant=variant,
)

assert url == f"https://api.openai.com/v1/videos/video_123/content{expected_suffix}"
assert params == {}


def test_openai_transform_video_content_request_variant_none_no_query_param():
"""OpenAI content transform should NOT append ?variant= when variant is None."""
config = OpenAIVideoConfig()
url, params = config.transform_video_content_request(
video_id="video_123",
api_base="https://api.openai.com/v1/videos",
litellm_params={},
headers={},
variant=None,
)

assert "variant" not in url
assert url == "https://api.openai.com/v1/videos/video_123/content"


def test_video_content_handler_passes_variant_to_url():
"""HTTP handler should pass variant through to the final URL."""
from litellm.llms.custom_httpx.http_handler import HTTPHandler
from litellm.types.router import GenericLiteLLMParams

if hasattr(litellm, "in_memory_llm_clients_cache"):
litellm.in_memory_llm_clients_cache.flush_cache()

handler = BaseLLMHTTPHandler()
config = OpenAIVideoConfig()

mock_client = MagicMock(spec=HTTPHandler)
mock_response = MagicMock()
mock_response.content = b"thumbnail-bytes"
mock_client.get.return_value = mock_response

with patch(
"litellm.llms.custom_httpx.llm_http_handler._get_httpx_client",
return_value=mock_client,
):
result = handler.video_content_handler(
video_id="video_abc",
video_content_provider_config=config,
custom_llm_provider="openai",
litellm_params=GenericLiteLLMParams(
api_base="https://api.openai.com/v1"
),
logging_obj=MagicMock(),
timeout=5.0,
api_key="sk-test",
client=mock_client,
_is_async=False,
variant="thumbnail",
)

assert result == b"thumbnail-bytes"
called_url = mock_client.get.call_args.kwargs["url"]
assert called_url == "https://api.openai.com/v1/videos/video_abc/content?variant=thumbnail"


def test_video_content_handler_uses_get_for_openai():
"""HTTP handler must use GET (not POST) for OpenAI content download."""
from litellm.llms.custom_httpx.http_handler import HTTPHandler
Expand Down
Loading