mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-26 01:12:21 +00:00
fix(fal_ai): keep model in polled video ids and pick resolution from the short side
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
141548dcf3
commit
c359ef763e
2 changed files with 58 additions and 34 deletions
|
|
@ -79,12 +79,22 @@ def _resolution_for_short_side(short_side: int) -> str:
|
|||
return next((resolution for threshold, resolution in _RESOLUTION_TIERS if short_side <= threshold), "4k")
|
||||
|
||||
|
||||
def _model_path_from_queue_url(url: object) -> str | None:
|
||||
if not isinstance(url, str) or not url:
|
||||
def _model_path_from_request_url(raw_response: httpx.Response) -> str | None:
|
||||
segments: Final[tuple[str, ...]] = tuple(segment for segment in raw_response.request.url.path.split("/") if segment)
|
||||
if "requests" not in segments:
|
||||
return None
|
||||
path: Final[str] = httpx.URL(url).path.strip("/")
|
||||
model_path, separator, _ = path.partition("/requests/")
|
||||
return model_path if separator and model_path else None
|
||||
model_segments: Final[tuple[str, ...]] = segments[: segments.index("requests")]
|
||||
segment_count: Final[int] = 3 if len(model_segments) >= 3 and model_segments[-3] in _QUEUE_NAMESPACES else 2
|
||||
return "/".join(model_segments[-segment_count:]) if len(model_segments) >= segment_count else None
|
||||
|
||||
|
||||
def _request_id_from_request_url(raw_response: httpx.Response) -> str | None:
|
||||
segments: Final[tuple[str, ...]] = tuple(segment for segment in raw_response.request.url.path.split("/") if segment)
|
||||
if "requests" not in segments:
|
||||
return None
|
||||
request_index: Final[int] = segments.index("requests")
|
||||
request_id_index: Final[int] = request_index + 1
|
||||
return segments[request_id_index] if len(segments) > request_id_index else None
|
||||
|
||||
|
||||
def _size_params(size: object) -> Mapping[str, str]:
|
||||
|
|
@ -295,12 +305,16 @@ class FalAIVideoConfig(BaseVideoConfig):
|
|||
error_value: Final[object] = response_data.get("error")
|
||||
error: Final[str | None] = error_value if isinstance(error_value, str) else None
|
||||
provider: Final[str] = custom_llm_provider or _FAL_AI_PROVIDER
|
||||
model_path: Final[str | None] = _model_path_from_queue_url(response_data.get("response_url"))
|
||||
model_path: Final[str | None] = _model_path_from_request_url(raw_response)
|
||||
request_id: Final[str] = _response_string(response_data, "request_id") or (
|
||||
_request_id_from_request_url(raw_response) or ""
|
||||
)
|
||||
return VideoObject(
|
||||
id=encode_video_id_with_provider(_response_string(response_data, "request_id"), provider, model_path),
|
||||
id=encode_video_id_with_provider(request_id, provider, model_path),
|
||||
object="video",
|
||||
status="failed" if error else status,
|
||||
created_at=0,
|
||||
model=model_path,
|
||||
error=(
|
||||
{"code": "fal_error", "message": error} if error else None # mutable-ok: VideoObject requires a dict
|
||||
),
|
||||
|
|
|
|||
|
|
@ -161,8 +161,8 @@ class TestFalAIVideoTransformation:
|
|||
],
|
||||
)
|
||||
def test_status_response_mapping(self, response_data, expected_status):
|
||||
response = Mock(spec=httpx.Response)
|
||||
response.json.return_value = response_data
|
||||
status_url = "https://queue.fal.run/bytedance/seedance-2.5/requests/abc/status"
|
||||
response = httpx.Response(200, json=response_data, request=httpx.Request("GET", status_url))
|
||||
|
||||
video = self.config.transform_video_status_retrieve_response(
|
||||
raw_response=response,
|
||||
|
|
@ -172,43 +172,32 @@ class TestFalAIVideoTransformation:
|
|||
|
||||
assert video.status == expected_status
|
||||
assert video.created_at == 0
|
||||
decoded = decode_video_id_with_provider(video.id)
|
||||
assert decoded["model_id"] == "bytedance/seedance-2.5"
|
||||
assert decoded["video_id"] == "abc"
|
||||
|
||||
def test_status_response_id_stays_pollable(self):
|
||||
response = Mock(spec=httpx.Response)
|
||||
response.json.return_value = {
|
||||
"request_id": "abc",
|
||||
"status": "IN_PROGRESS",
|
||||
"response_url": "https://queue.fal.run/bytedance/seedance-2.5/requests/abc",
|
||||
}
|
||||
|
||||
video = self.config.transform_video_status_retrieve_response(
|
||||
raw_response=response,
|
||||
logging_obj=self.logging_obj,
|
||||
custom_llm_provider="fal_ai",
|
||||
)
|
||||
|
||||
status_url, _ = self.config.transform_video_status_retrieve_request(
|
||||
poll_url, _ = self.config.transform_video_status_retrieve_request(
|
||||
video_id=video.id,
|
||||
api_base="https://queue.fal.run",
|
||||
litellm_params=GenericLiteLLMParams(),
|
||||
headers={},
|
||||
)
|
||||
content_url, _ = self.config.transform_video_content_request(
|
||||
video_id=video.id,
|
||||
api_base="https://queue.fal.run",
|
||||
litellm_params=GenericLiteLLMParams(),
|
||||
headers={},
|
||||
)
|
||||
assert status_url == "https://queue.fal.run/bytedance/seedance-2.5/requests/abc/status"
|
||||
assert content_url == "https://queue.fal.run/bytedance/seedance-2.5/requests/abc"
|
||||
assert poll_url == status_url
|
||||
|
||||
def test_status_response_error(self):
|
||||
response = Mock(spec=httpx.Response)
|
||||
response.json.return_value = {
|
||||
response_data = {
|
||||
"request_id": "abc",
|
||||
"status": "COMPLETED",
|
||||
"error": "generation failed",
|
||||
}
|
||||
response = httpx.Response(
|
||||
200,
|
||||
json=response_data,
|
||||
request=httpx.Request(
|
||||
"GET",
|
||||
"https://queue.fal.run/bytedance/seedance-2.5/requests/abc/status",
|
||||
),
|
||||
)
|
||||
|
||||
video = self.config.transform_video_status_retrieve_response(
|
||||
raw_response=response,
|
||||
|
|
@ -219,6 +208,27 @@ class TestFalAIVideoTransformation:
|
|||
assert video.status == "failed"
|
||||
assert video.error == {"code": "fal_error", "message": "generation failed"}
|
||||
|
||||
def test_status_response_uses_namespaced_request_url(self):
|
||||
response = httpx.Response(
|
||||
200,
|
||||
json={"status": "IN_PROGRESS"},
|
||||
request=httpx.Request(
|
||||
"GET",
|
||||
"https://example.com/proxy/workflows/owner/app/requests/xyz/status",
|
||||
),
|
||||
)
|
||||
|
||||
video = self.config.transform_video_status_retrieve_response(
|
||||
raw_response=response,
|
||||
logging_obj=self.logging_obj,
|
||||
custom_llm_provider="fal_ai",
|
||||
)
|
||||
|
||||
decoded = decode_video_id_with_provider(video.id)
|
||||
assert decoded["model_id"] == "workflows/owner/app"
|
||||
assert decoded["video_id"] == "xyz"
|
||||
assert video.model == "workflows/owner/app"
|
||||
|
||||
def test_content_response_downloads_video_url(self, monkeypatch):
|
||||
content_response = httpx.Response(
|
||||
200,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue