diff --git a/plugins/video_gen/fal/__init__.py b/plugins/video_gen/fal/__init__.py index 3b126a7008..059defe78a 100644 --- a/plugins/video_gen/fal/__init__.py +++ b/plugins/video_gen/fal/__init__.py @@ -31,6 +31,7 @@ _SIX_ASPECTS = ("21:9", "16:9", "4:3", "1:1", "3:4", "9:16") # MiniMax H3 uses capitalized/2K-style resolution enums; aliases map the tool's usual values. Max tops out at 768P. _H3_ALIASES = {"480p": "768P", "540p": "768P", "720p": "768P", "768p": "768P", "1080p": "2K", "2k": "2K", "4k": "4K", "2160p": "4K"} _H3_MAX_ALIASES = {"480p": "480P", "540p": "480P", "720p": "768P", "768p": "768P", "1080p": "768P", "2k": "768P", "4k": "768P", "2160p": "768P"} +_H3_MAX_TURBO_ALIASES = {"480p": "480P", "540p": "480P", "720p": "768P", "768p": "768P", "1080p": "1080P", "2k": "1080P", "4k": "1080P", "2160p": "1080P"} FAL_FAMILIES: Dict[str, Dict[str, Any]] = { # ─── Cheap / fast tier ───────────────────────────────────────────── @@ -59,6 +60,13 @@ FAL_FAMILIES: Dict[str, Dict[str, Any]] = { "adherence/aesthetics, 768p in seconds, 5-15s.", "minimax/h3-max/text-to-video", "minimax/h3-max/image-to-video", duration_int=True, image_drop_keys=("aspect_ratio",), aspect_ratios=_SIX_ASPECTS, resolutions=("480P", "768P"), resolution_aliases=_H3_MAX_ALIASES, durations=(5, 15), static_payload={"prompt_expansion_mode": "balanced"}, audio_native=True, seed=True), + # Same schema shape as Max (required prompt_expansion_mode, no i2v aspect_ratio) but adds a 1080P tier and an end_image_url + # the tool surface doesn't expose; throughput-tuned so it's the fastest premium H3 tier ($0.025-0.08/s list). + "minimax-h3-max-turbo": _family("MiniMax H3 Max Turbo (fal post-train)", "~5-20s", "premium", "fal's throughput-tuned H3 Max variant. Near-Max " + "quality at a fraction of the price/latency, 480P-1080P, 5-15s.", "minimax/h3-max-turbo/text-to-video", + "minimax/h3-max-turbo/image-to-video", duration_int=True, image_drop_keys=("aspect_ratio",), aspect_ratios=_SIX_ASPECTS, + resolutions=("480P", "768P", "1080P"), resolution_aliases=_H3_MAX_TURBO_ALIASES, durations=(5, 15), + static_payload={"prompt_expansion_mode": "balanced"}, audio_native=True, seed=True), "flux-3": _family("FLUX 3 (via FAL)", "~60-120s", "premium", "Black Forest Labs frontier video. Native audio, 5-20s, 8 aspect ratios.", "blackforestlabs/flux-3/text-to-video", "blackforestlabs/flux-3/image-to-video", duration_int=True, # enum "auto" | 5..20 ints aspect_ratios=("21:9", "2:1", "16:9", "4:3", "1:1", "3:4", "9:16"), resolutions=("720p", "1080p"), durations=(5, 20), audio=True), diff --git a/tests/plugins/video_gen/test_fal_plugin.py b/tests/plugins/video_gen/test_fal_plugin.py index 821d2db24d..3cad506c2a 100644 --- a/tests/plugins/video_gen/test_fal_plugin.py +++ b/tests/plugins/video_gen/test_fal_plugin.py @@ -81,6 +81,30 @@ def test_minimax_h3_int_duration_and_resolution_alias(): assert hi["resolution"] == "2K" +def test_h3_max_turbo_static_key_and_1080p_alias(): + """H3 Max Turbo requires prompt_expansion_mode on both endpoints, adds a real + 1080P tier (unlike Max, which caps at 768P), and its i2v drops aspect_ratio.""" + from plugins.video_gen.fal import FAL_FAMILIES, _build_payload + + meta = FAL_FAMILIES["minimax-h3-max-turbo"] + t2v = _build_payload( + meta, prompt="x", image_url=None, duration=7, aspect_ratio="16:9", + resolution="1080p", negative_prompt=None, audio=None, seed=11, + ) + assert t2v["prompt_expansion_mode"] == "balanced" + assert t2v["resolution"] == "1080P" + assert t2v["duration"] == 7 and isinstance(t2v["duration"], int) + assert t2v["seed"] == 11 + + i2v = _build_payload( + meta, prompt="x", image_url="https://example.com/i.png", duration=5, + aspect_ratio="16:9", resolution="480p", negative_prompt=None, audio=None, seed=None, + ) + assert i2v["prompt_expansion_mode"] == "balanced" + assert "aspect_ratio" not in i2v + assert i2v["image_url"] == "https://example.com/i.png" + + def test_image_drop_keys_strips_aspect_ratio_on_i2v(): """Seedance 2.5 / MiniMax H3 / Grok 1.5 i2v endpoints derive the aspect ratio from the input image; sending the key is rejected."""