fix(fal_ai): give MiniMax H3 Max video endpoints their own resolution profile (#51348)

* fix(fal_ai): give MiniMax H3 Max video endpoints their own resolution profile

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(fal_ai): keep the H3 Max test signature on one line

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(fal_ai): type the H3 Max test locals and signatures

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Kerry Lu 2026-11-11 21:05:41 -08:00 • committed by GitHub
parent 19ffe3a5f2
commit 7c7e02b06f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 59 additions and 2 deletions

View file

@ -45,7 +45,7 @@ _ALLOWED_ASPECT_RATIOS: Final[frozenset[str]] = frozenset({"auto", "16:9", "9:16
class _ModelProfile:
resolutions: frozenset[str]
resolution_tiers: tuple[tuple[int, str], ...]
default_resolution: str
default_resolution: str | None
integer_duration: bool
reference_key: str
reference_as_list: bool
@ -67,6 +67,14 @@ _H3_PROFILE: Final[_ModelProfile] = _ModelProfile(
reference_key="reference_image_urls",
reference_as_list=True,
)
_H3_MAX_PROFILE: Final[_ModelProfile] = _ModelProfile(
resolutions=frozenset({"480P", "768P", "1080P"}),
resolution_tiers=((480, "480P"), (768, "768P"), (sys.maxsize, "1080P")),
default_resolution=None,
integer_duration=False,
reference_key="image_url",
reference_as_list=False,
)
_QUEUE_NAMESPACES: Final[frozenset[str]] = frozenset(("workflows", "comfy"))
_STATUS_MAP: Final[Mapping[str, str]] = MappingProxyType(
{
@ -101,7 +109,11 @@ def _duration_value(value: object) -> str | None:
def _profile_for_model(model: str) -> _ModelProfile:
return _H3_PROFILE if model.startswith("minimax/h3/") else _SEEDANCE_PROFILE
if model.startswith("minimax/h3/"):
return _H3_PROFILE
if model.startswith("minimax/h3-max"):
return _H3_MAX_PROFILE
return _SEEDANCE_PROFILE
def _resolution_for_short_side(short_side: int, profile: _ModelProfile) -> str:

View file

@ -23,6 +23,9 @@ from litellm.utils import ProviderConfigManager
MODEL = "bytedance/seedance-2.5/text-to-video"
H3_TEXT_MODEL = "minimax/h3/text-to-video"
H3_REFERENCE_MODEL = "minimax/h3/reference-to-video"
H3_MAX_TEXT_MODEL = "minimax/h3-max/text-to-video"
H3_MAX_TURBO_IMAGE_MODEL = "minimax/h3-max-turbo/image-to-video"
H3_MAX_RECAST_MODEL = "minimax/h3-max/recast"
@pytest.mark.parametrize("drop_params", [False, True])
@ -96,6 +99,24 @@ class TestFalAIVideoTransformation:
assert mapped["reference_image_urls"] == [url]
assert "image_url" not in mapped
def test_map_openai_params_supports_h3_max_profile(self) -> None:
url: Final = "https://example.com/image.png"
assert self.config.map_openai_params({"size": "768p"}, H3_MAX_TEXT_MODEL, False) == {"resolution": "768P"}
assert self.config.map_openai_params({"size": "1920x1080"}, H3_MAX_TEXT_MODEL, False) == {
"resolution": "1080P",
"aspect_ratio": "16:9",
}
mapped: Final = self.config.map_openai_params(
{"seconds": 5.5, "input_reference": url, "size": "1280x768"},
H3_MAX_TURBO_IMAGE_MODEL,
False,
)
assert mapped["resolution"] == "768P"
assert mapped["image_url"] == url
assert "reference_image_urls" not in mapped
assert mapped["duration"] == "5"
def test_map_openai_params_h3_omits_auto_duration(self):
assert self.config.map_openai_params({"seconds": "auto"}, H3_TEXT_MODEL, False) == {}
assert self.config.map_openai_params({"seconds": "auto"}, MODEL, False) == {"duration": "auto"}
@ -198,6 +219,30 @@ class TestFalAIVideoTransformation:
assert video.usage == {"duration_seconds": 5.0, "video_resolution": "2K"}
def test_transform_video_create_response_h3_max_leaves_default_resolution_to_cost_map(
self, local_model_cost_map: None
) -> None:
response: Final = Mock(spec=httpx.Response)
response.json.return_value = {"request_id": "abc"}
video: Final = self.config.transform_video_create_response(
model=H3_MAX_RECAST_MODEL,
raw_response=response,
logging_obj=self.logging_obj,
custom_llm_provider="fal_ai",
request_data={"duration": 5},
)
assert video.usage == {"duration_seconds": 5.0}
row: Final = litellm.model_cost[f"fal_ai/{H3_MAX_RECAST_MODEL}"]
assert default_video_cost_calculator(
model=H3_MAX_RECAST_MODEL,
duration_seconds=5.0,
custom_llm_provider="fal_ai",
model_info=None,
video_resolution=None,
) == pytest.approx(row["output_cost_per_second"] * 5.0)
def test_status_request_uses_queue_base_path(self):
response = Mock(spec=httpx.Response)
response.json.return_value = {"request_id": "abc"}