[feat] [6/n] Improve API: add ltx2_two_stage preset
Land the ltx2_two_stage inference preset (PR 6 commit 1/5) that exposes
the public LTX-2 two-stage distilled flow: half-resolution denoise
followed by 2x spatial upsample + stage-2 refine denoise (3 steps with
the official distilled sigma schedule, or 2 steps with the reduced
variant).
Schema:
- new _REFINE_STAGE PresetStageSpec with kind="refinement" and
allowed per-request overrides {num_inference_steps, guidance_scale,
image_crf, video_position_offset_sec}
- new LTX2_TWO_STAGE preset with stage_schemas=(denoise, refine) and
stage_defaults.refine={num_inference_steps: 2, guidance_scale: 1.0}
matching the load_kwargs the internal ltx2-streaming gpu_pool uses
- LTX2_TWO_STAGE registered via ALL_PRESETS -> _register_presets()
Init-time refine wiring (enabled, add_noise, upsampler/LoRA/transformer
paths) flows through generator.pipeline.preset_overrides.refine.* and
generator.pipeline.components.{upsampler_weights, lora_path} in a
follow-up commit — the refine stage's internal implementation
(fastvideo/pipelines/stages/ltx2_refine.py in FastVideo-internal) already
treats those as FastVideoArgs fields; this commit only ships the public
preset surface.
5 new tests under fastvideo/tests/api/test_presets.py::TestLtx2Presets:
registration count (3 presets now), two-stage topology, stage defaults,
valid per-request refine override keys, and unknown-override rejection.
This commit is contained in:
@@ -30,6 +30,18 @@ _DENOISE_STAGE = PresetStageSpec(
|
||||
}),
|
||||
)
|
||||
|
||||
_REFINE_STAGE = PresetStageSpec(
|
||||
name="refine",
|
||||
kind="refinement",
|
||||
description="Latent-upsample + second-pass refine",
|
||||
allowed_overrides=frozenset({
|
||||
"num_inference_steps",
|
||||
"guidance_scale",
|
||||
"image_crf",
|
||||
"video_position_offset_sec",
|
||||
}),
|
||||
)
|
||||
|
||||
LTX2_BASE = InferencePreset(
|
||||
name="ltx2_base",
|
||||
version=1,
|
||||
@@ -77,4 +89,36 @@ LTX2_DISTILLED = InferencePreset(
|
||||
},
|
||||
)
|
||||
|
||||
ALL_PRESETS = (LTX2_BASE, LTX2_DISTILLED)
|
||||
# Two-stage distilled flow: half-res denoise (stage 1) followed by
|
||||
# spatial-upsample + second denoise (stage 2 "refine"). Stage-2
|
||||
# sigmas are fixed in fastvideo.pipelines.stages.ltx2_refine — only
|
||||
# num_inference_steps (2 or 3) and guidance_scale are user-tunable.
|
||||
# Refine wiring (upsampler weights, optional LoRA, enable/add-noise)
|
||||
# lives on generator.pipeline.preset_overrides.refine at init-time;
|
||||
# per-request tuning goes through request.stage_overrides.refine.
|
||||
LTX2_TWO_STAGE = InferencePreset(
|
||||
name="ltx2_two_stage",
|
||||
version=1,
|
||||
model_family="ltx2",
|
||||
description="LTX-2 distilled with 2x spatial refine (stage 1 half-res + stage 2 upsample+denoise)",
|
||||
workload_type="t2v",
|
||||
stage_schemas=(_DENOISE_STAGE, _REFINE_STAGE),
|
||||
defaults={
|
||||
"seed": 10,
|
||||
"height": 1024,
|
||||
"width": 1536,
|
||||
"num_frames": 121,
|
||||
"fps": 24,
|
||||
"guidance_scale": 1.0,
|
||||
"num_inference_steps": 8,
|
||||
"negative_prompt": "",
|
||||
},
|
||||
stage_defaults={
|
||||
"refine": {
|
||||
"num_inference_steps": 2,
|
||||
"guidance_scale": 1.0,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
ALL_PRESETS = (LTX2_BASE, LTX2_DISTILLED, LTX2_TWO_STAGE)
|
||||
|
||||
@@ -334,7 +334,7 @@ class TestLtx2Presets:
|
||||
import fastvideo.registry # noqa: F401
|
||||
presets = get_presets_for_family("ltx2")
|
||||
names = {p.name for p in presets}
|
||||
assert names == {"ltx2_base", "ltx2_distilled"}
|
||||
assert names == {"ltx2_base", "ltx2_distilled", "ltx2_two_stage"}
|
||||
|
||||
def test_ltx2_base_lookup(self) -> None:
|
||||
import fastvideo.registry # noqa: F401
|
||||
@@ -349,6 +349,43 @@ class TestLtx2Presets:
|
||||
assert p.defaults["num_inference_steps"] == 8
|
||||
assert p.defaults["guidance_scale"] == 1.0
|
||||
|
||||
def test_ltx2_two_stage_is_two_stage(self) -> None:
|
||||
import fastvideo.registry # noqa: F401
|
||||
p = get_preset("ltx2_two_stage", "ltx2")
|
||||
assert len(p.stage_schemas) == 2
|
||||
assert p.stage_schemas[0].name == "denoise"
|
||||
assert p.stage_schemas[1].name == "refine"
|
||||
assert p.stage_schemas[1].kind == "refinement"
|
||||
|
||||
def test_ltx2_two_stage_stage_defaults(self) -> None:
|
||||
import fastvideo.registry # noqa: F401
|
||||
p = get_preset("ltx2_two_stage", "ltx2")
|
||||
refine = p.stage_defaults["refine"]
|
||||
# stage-2 refine only supports 2 or 3 denoising steps; preset
|
||||
# defaults to 2 (matches gpu_pool.py load_kwargs).
|
||||
assert refine["num_inference_steps"] == 2
|
||||
assert refine["guidance_scale"] == 1.0
|
||||
|
||||
def test_ltx2_two_stage_refine_overrides_valid(self) -> None:
|
||||
import fastvideo.registry # noqa: F401
|
||||
p = get_preset("ltx2_two_stage", "ltx2")
|
||||
validate_stage_overrides(
|
||||
p, {"refine": {"num_inference_steps": 3}})
|
||||
validate_stage_overrides(
|
||||
p, {"refine": {"guidance_scale": 1.0}})
|
||||
validate_stage_overrides(
|
||||
p, {"refine": {"image_crf": 18}})
|
||||
validate_stage_overrides(
|
||||
p, {"refine": {"video_position_offset_sec": 2.5}})
|
||||
|
||||
def test_ltx2_two_stage_rejects_unknown_refine_override(self) -> None:
|
||||
import fastvideo.registry # noqa: F401
|
||||
from fastvideo.api.errors import ConfigValidationError
|
||||
p = get_preset("ltx2_two_stage", "ltx2")
|
||||
with pytest.raises(ConfigValidationError):
|
||||
validate_stage_overrides(
|
||||
p, {"refine": {"bogus_field": 1}})
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Hunyuan preset integration
|
||||
|
||||
Reference in New Issue
Block a user