mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
code review changes
This commit is contained in:
parent
a080078f40
commit
924df731d2
4 changed files with 132 additions and 27 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()."
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"])
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue