code review changes

This commit is contained in:
matt-greathouse 2026-04-08 12:55:38 -04:00
parent a080078f40
commit 924df731d2
4 changed files with 132 additions and 27 deletions

View file

@ -540,6 +540,7 @@ xai_models: Set = set()
zai_models: Set = set()
deepseek_models: Set = set()
runwayml_models: Set = set()
ltx_models: Set = set()
azure_ai_models: Set = set()
jina_ai_models: Set = set()
voyage_models: Set = set()
@ -751,6 +752,8 @@ def add_known_models(model_cost_map: Optional[Dict] = None):
deepseek_models.add(key)
elif value.get("litellm_provider") == "runwayml":
runwayml_models.add(key)
elif value.get("litellm_provider") == "ltx":
ltx_models.add(key)
elif value.get("litellm_provider") == "meta_llama":
llama_models.add(key)
elif value.get("litellm_provider") == "nscale":
@ -921,6 +924,7 @@ model_list = list(
| perplexity_models
| set(maritalk_models)
| runwayml_models
| ltx_models
| vertex_language_models
| watsonx_models
| gemini_models
@ -1022,6 +1026,7 @@ models_by_provider: dict = {
"fal_ai": fal_ai_models,
"deepseek": deepseek_models,
"runwayml": runwayml_models,
"ltx": ltx_models,
"mistral": mistral_chat_models,
"azure_ai": azure_ai_models,
"voyage": voyage_models,

View file

@ -27,13 +27,28 @@ else:
LTX_VIDEO_STORAGE_DIR = Path(tempfile.gettempdir()) / "litellm_ltx_videos"
LTX_VIDEO_STORAGE_MAX_AGE_SECONDS = 24 * 60 * 60
def _get_ltx_video_storage_path(video_id: str) -> Path:
return LTX_VIDEO_STORAGE_DIR / f"{video_id}.mp4"
def _cleanup_old_ltx_videos() -> None:
if not LTX_VIDEO_STORAGE_DIR.exists():
return
cutoff = time.time() - LTX_VIDEO_STORAGE_MAX_AGE_SECONDS
for video_path in LTX_VIDEO_STORAGE_DIR.glob("*.mp4"):
try:
if video_path.stat().st_mtime < cutoff:
video_path.unlink(missing_ok=True)
except OSError:
pass
def _persist_ltx_video_bytes(video_id: str, video_bytes: bytes) -> Path:
_cleanup_old_ltx_videos()
LTX_VIDEO_STORAGE_DIR.mkdir(parents=True, exist_ok=True)
video_path = _get_ltx_video_storage_path(video_id)
video_path.write_bytes(video_bytes)
@ -65,7 +80,6 @@ class LTXVideoConfig(BaseVideoConfig):
"input_reference",
"seconds",
"size",
"user",
"extra_headers",
]
@ -98,10 +112,19 @@ class LTXVideoConfig(BaseVideoConfig):
# Ignore invalid seconds values and let the provider fall back to defaults.
pass
if "user" in video_create_optional_params:
if drop_params:
pass
else:
raise ValueError(
f"Parameter user is not supported for model {model}. "
"Set drop_params=True to drop unsupported parameters."
)
# Pass through LTX-specific parameters
supported_openai_params = self.get_supported_openai_params(model)
for key, value in video_create_optional_params.items():
if key not in supported_openai_params:
if key != "user" and key not in supported_openai_params:
mapped_params[key] = value
return mapped_params
@ -266,7 +289,10 @@ class LTXVideoConfig(BaseVideoConfig):
status_code=404,
message=(
"No locally stored LTX video content was found for this video_id. "
"Recreate the video before calling video_content()."
"LTX stores generated video content in the local temp directory of "
"the process that created it, so retrieval will fail from a different "
"instance or after a process restart. Recreate the video before "
"calling video_content()."
),
)

View file

@ -33671,28 +33671,6 @@
}
},
"ltx/ltx-2-3-fast": {
"litellm_provider": "ltx",
"mode": "video_generation",
"output_cost_per_video_per_second": 0.04,
"source": "https://docs.ltx.video/pricing",
"supported_modalities": [
"text",
"image"
],
"supported_output_modalities": [
"video"
],
"supported_resolutions": [
"1280x720",
"1920x1080",
"2560x1440",
"3840x2160"
],
"metadata": {
"comment": "$0.04/sec at 1080p, $0.08/sec at 1440p, $0.16/sec at 4K. Using 1080p as base cost."
}
},
"ltx/ltx-2-3-pro": {
"litellm_provider": "ltx",
"mode": "video_generation",
"output_cost_per_video_per_second": 0.06,
@ -33707,13 +33685,41 @@
"supported_resolutions": [
"1280x720",
"1920x1080",
"1080x1920",
"2560x1440",
"3840x2160"
"1440x2560",
"3840x2160",
"2160x3840"
],
"metadata": {
"comment": "$0.06/sec at 1080p, $0.12/sec at 1440p, $0.24/sec at 4K. Using 1080p as base cost."
}
},
"ltx/ltx-2-3-pro": {
"litellm_provider": "ltx",
"mode": "video_generation",
"output_cost_per_video_per_second": 0.08,
"source": "https://docs.ltx.video/pricing",
"supported_modalities": [
"text",
"image"
],
"supported_output_modalities": [
"video"
],
"supported_resolutions": [
"1280x720",
"1920x1080",
"1080x1920",
"2560x1440",
"1440x2560",
"3840x2160",
"2160x3840"
],
"metadata": {
"comment": "$0.08/sec at 1080p, $0.16/sec at 1440p, $0.32/sec at 4K. Using 1080p as base cost."
}
},
"fireworks_ai/accounts/fireworks/models/qwen3-coder-480b-a35b-instruct": {
"max_tokens": 262144,
"max_input_tokens": 262144,

View file

@ -3,11 +3,13 @@ Tests for LTX Video generation transformation.
"""
import asyncio
import os
from unittest.mock import Mock
import httpx
import pytest
import litellm
import litellm.llms.ltx.videos.transformation as ltx_video_transformation
from litellm.llms.base_llm.chat.transformation import BaseLLMException
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler
@ -37,7 +39,7 @@ class TestLTXVideoTransformation:
assert "input_reference" in params
assert "seconds" in params
assert "size" in params
assert "user" in params
assert "user" not in params
assert "extra_headers" in params
def test_map_openai_params_basic(self):
@ -81,6 +83,25 @@ class TestLTXVideoTransformation:
)
assert mapped["duration"] == 10
def test_map_openai_params_user_raises_when_not_dropping(self):
"""Test unsupported OpenAI user param fails loudly for LTX."""
with pytest.raises(ValueError, match="Parameter user is not supported"):
self.config.map_openai_params(
video_create_optional_params={"user": "end-user-123"},
model="ltx-2-3-fast",
drop_params=False,
)
def test_map_openai_params_user_is_dropped_when_requested(self):
"""Test unsupported user param can be dropped explicitly."""
mapped = self.config.map_openai_params(
video_create_optional_params={"user": "end-user-123"},
model="ltx-2-3-fast",
drop_params=True,
)
assert mapped == {}
def test_validate_environment(self):
"""Test authentication header setup."""
headers = self.config.validate_environment(
@ -254,6 +275,37 @@ class TestLTXVideoTransformation:
assert result.status == "completed"
assert result.id # should have a UUID
def test_transform_video_create_response_cleans_up_expired_files(
self, monkeypatch, tmp_path
):
"""Test stale locally persisted LTX videos are pruned on write."""
monkeypatch.setattr(ltx_video_transformation, "LTX_VIDEO_STORAGE_DIR", tmp_path)
monkeypatch.setattr(
ltx_video_transformation, "LTX_VIDEO_STORAGE_MAX_AGE_SECONDS", 1
)
stale_video_path = tmp_path / "stale-video.mp4"
stale_video_path.parent.mkdir(parents=True, exist_ok=True)
stale_video_path.write_bytes(b"old-bytes")
old_timestamp = ltx_video_transformation.time.time() - 10
os.utime(stale_video_path, (old_timestamp, old_timestamp))
mock_response = Mock(spec=httpx.Response)
mock_response.content = b"new-video-bytes"
mock_response.status_code = 200
mock_response.request = httpx.Request("POST", "https://api.ltx.video/v1")
result = self.config.transform_video_create_response(
model="ltx-2-3-fast",
raw_response=mock_response,
logging_obj=self.mock_logging_obj,
custom_llm_provider=None,
request_data={"model": "ltx-2-3-fast"},
)
assert stale_video_path.exists() is False
assert (tmp_path / f"{result.id}.mp4").read_bytes() == b"new-video-bytes"
def test_transform_video_create_response_empty_binary_raises(self):
"""Test that empty create responses fail loudly."""
mock_response = Mock(spec=httpx.Response)
@ -361,6 +413,16 @@ class TestLTXVideoTransformation:
headers={},
)
with pytest.raises(
BaseLLMException, match="different instance or after a process restart"
):
self.config.transform_video_content_request(
video_id="missing-video",
api_base="",
litellm_params=GenericLiteLLMParams(),
headers={},
)
with pytest.raises(NotImplementedError):
self.config.transform_video_status_retrieve_request(
video_id="test",
@ -492,6 +554,12 @@ class TestLTXVideoTransformation:
assert video_obj.status == "completed"
assert video_obj.size == "1280x720"
def test_ltx_models_are_registered_globally(self):
"""Test LTX models are exposed through LiteLLM's global model registries."""
assert hasattr(litellm, "ltx_models")
assert "ltx" in litellm.models_by_provider
assert litellm.models_by_provider["ltx"] is litellm.ltx_models
if __name__ == "__main__":
pytest.main([__file__, "-v"])