fix(fal_ai): keep status ids pollable and size resolution by the short side

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
kerry 2026-09-19 16:54:11 +00:00
parent 0f4ce95492
commit 141548dcf3
2 changed files with 50 additions and 4 deletions

View file

@ -75,8 +75,16 @@ def _duration_value(value: object) -> str | None:
return None
def _resolution_for_height(height: int) -> str:
return next((resolution for threshold, resolution in _RESOLUTION_TIERS if height <= threshold), "4k")
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:
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
def _size_params(size: object) -> Mapping[str, str]:
@ -95,7 +103,7 @@ def _size_params(size: object) -> Mapping[str, str]:
return MappingProxyType({})
reduced_gcd: Final[int] = math.gcd(width, height)
aspect_ratio: Final[str] = f"{width // reduced_gcd}:{height // reduced_gcd}"
resolution: Final[str] = _resolution_for_height(height)
resolution: Final[str] = _resolution_for_short_side(min(width, height))
if aspect_ratio in _ALLOWED_ASPECT_RATIOS:
return MappingProxyType({"resolution": resolution, "aspect_ratio": aspect_ratio})
return MappingProxyType({"resolution": resolution})
@ -287,8 +295,9 @@ 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"))
return VideoObject(
id=encode_video_id_with_provider(_response_string(response_data, "request_id"), provider),
id=encode_video_id_with_provider(_response_string(response_data, "request_id"), provider, model_path),
object="video",
status="failed" if error else status,
created_at=0,

View file

@ -51,6 +51,14 @@ class TestFalAIVideoTransformation:
"aspect_ratio": "1:1",
}
assert self.config.map_openai_params({"size": "720p"}, MODEL, False) == {"resolution": "720p"}
assert self.config.map_openai_params({"size": "720x1280"}, MODEL, False) == {
"resolution": "720p",
"aspect_ratio": "9:16",
}
assert self.config.map_openai_params({"size": "1080x1920"}, MODEL, False) == {
"resolution": "1080p",
"aspect_ratio": "9:16",
}
def test_map_openai_params_rejects_non_url_input_reference(self):
with pytest.raises(ValueError, match="public image URL"):
@ -165,6 +173,35 @@ class TestFalAIVideoTransformation:
assert video.status == expected_status
assert video.created_at == 0
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(
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"
def test_status_response_error(self):
response = Mock(spec=httpx.Response)
response.json.return_value = {