diff --git a/fastvideo/api/compat.py b/fastvideo/api/compat.py index e5c433b5c..94afe03df 100644 --- a/fastvideo/api/compat.py +++ b/fastvideo/api/compat.py @@ -26,10 +26,7 @@ from fastvideo.api.schema import ( ) from fastvideo.api.sampling_param import SamplingParam from fastvideo.fastvideo_args import FastVideoArgs -from fastvideo.pipelines.basic.ltx2.stage_overrides import ( - refine_preset_override_fields, - refine_stage_override_fields, -) +from fastvideo.pipelines.basic.ltx2.stage_overrides import REFINE_FLAT_KEYS from fastvideo.utils import shallow_asdict _INPUT_FIELD_NAMES = {field.name for field in fields(InputConfig)} @@ -43,10 +40,7 @@ _LEGACY_REQUEST_ALIASES = { _REQUEST_PIPELINE_OVERRIDE_FIELDS = frozenset({ "embedded_cfg_scale", }) -# torch.compile kwargs that map to first-class CompileConfig fields. _COMPILE_TYPED_KEYS = ("backend", "fullgraph", "mode", "dynamic") -# LTX-2 refine flat kwargs (init + per-request) known to FastVideoArgs. -_LTX2_REFINE_FLAT_KEYS = (refine_preset_override_fields() | refine_stage_override_fields()) def normalize_generator_config(config: GeneratorConfig | Mapping[str, Any], ) -> GeneratorConfig: @@ -118,7 +112,7 @@ def legacy_from_pretrained_to_config( elif key == "enable_torch_compile": compile_config["enabled"] = value elif key == "torch_compile_kwargs": - remaining: dict[str, Any] = (dict(deepcopy(value)) if isinstance(value, Mapping) else {}) + remaining: dict[str, Any] = (dict(value) if isinstance(value, Mapping) else {}) for first_class in _COMPILE_TYPED_KEYS: if first_class in remaining: compile_config[first_class] = remaining.pop(first_class) @@ -264,7 +258,7 @@ def generator_config_to_fastvideo_args(config: GeneratorConfig | Mapping[str, An preset_overrides = deepcopy(normalized.pipeline.preset_overrides) refine = preset_overrides.pop("refine", None) if isinstance(refine, Mapping): - for key in _LTX2_REFINE_FLAT_KEYS: + for key in REFINE_FLAT_KEYS: if key in refine: kwargs[f"ltx2_refine_{key}"] = refine[key] kwargs.update(preset_overrides) @@ -363,14 +357,9 @@ def _looks_like_run_or_serve_config(raw: Mapping[str, Any]) -> bool: def _compile_config_to_torch_kwargs(compile_config: CompileConfig, ) -> dict[str, Any]: - """Flatten typed ``CompileConfig`` back to a ``torch_compile_kwargs`` - dict that the legacy ``FastVideoArgs`` path still expects. - - Typed first-class fields (:attr:`backend`, :attr:`fullgraph`, - :attr:`mode`, :attr:`dynamic`) are only emitted when the user set - them explicitly (non-``None``). ``extras`` is merged on top for any - uncommon kwargs. - """ + """Flatten typed ``CompileConfig`` back to the legacy + ``torch_compile_kwargs`` dict, emitting only explicitly-set typed + fields and merging ``extras`` on top.""" out: dict[str, Any] = {} for key in _COMPILE_TYPED_KEYS: value = getattr(compile_config, key) diff --git a/fastvideo/pipelines/basic/ltx2/presets.py b/fastvideo/pipelines/basic/ltx2/presets.py index 1419e1378..d13b03b80 100644 --- a/fastvideo/pipelines/basic/ltx2/presets.py +++ b/fastvideo/pipelines/basic/ltx2/presets.py @@ -2,7 +2,7 @@ """LTX2 model family pipeline presets.""" from fastvideo.api.presets import InferencePreset, PresetStageSpec from fastvideo.pipelines.basic.ltx2.stage_overrides import ( - refine_stage_override_fields, ) + REFINE_STAGE_OVERRIDE_FIELDS, ) _LTX2_NEGATIVE_PROMPT = ("blurry, out of focus, overexposed, underexposed, low contrast, " "washed out colors, excessive noise, grainy texture, poor lighting, " @@ -36,7 +36,7 @@ _REFINE_STAGE = PresetStageSpec( name="refine", kind="refinement", description="Latent-upsample + second-pass refine", - allowed_overrides=refine_stage_override_fields(), + allowed_overrides=REFINE_STAGE_OVERRIDE_FIELDS, ) LTX2_BASE = InferencePreset( diff --git a/fastvideo/pipelines/basic/ltx2/stage_overrides.py b/fastvideo/pipelines/basic/ltx2/stage_overrides.py index 7e9c0786c..abcc175be 100644 --- a/fastvideo/pipelines/basic/ltx2/stage_overrides.py +++ b/fastvideo/pipelines/basic/ltx2/stage_overrides.py @@ -27,8 +27,6 @@ class LTX2RefinePresetOverride: class LTX2RefineStageOverride: """Per-request refine tuning under ``stage_overrides.refine``.""" - # Stage-2 refine only validates 2 (reduced) and 3 (official distilled) - # sigma schedules; other values raise at pipeline construction. num_inference_steps: int | None = None guidance_scale: float | None = None image_crf: int | None = None @@ -42,18 +40,15 @@ def refine_override_to_dict(override: LTX2RefinePresetOverride | LTX2RefineStage return {k: v for k, v in asdict(override).items() if v is not None} -def refine_preset_override_fields() -> frozenset[str]: - return frozenset(f.name for f in fields(LTX2RefinePresetOverride)) - - -def refine_stage_override_fields() -> frozenset[str]: - return frozenset(f.name for f in fields(LTX2RefineStageOverride)) - +REFINE_PRESET_OVERRIDE_FIELDS: frozenset[str] = frozenset(f.name for f in fields(LTX2RefinePresetOverride)) +REFINE_STAGE_OVERRIDE_FIELDS: frozenset[str] = frozenset(f.name for f in fields(LTX2RefineStageOverride)) +REFINE_FLAT_KEYS: frozenset[str] = (REFINE_PRESET_OVERRIDE_FIELDS | REFINE_STAGE_OVERRIDE_FIELDS) __all__ = [ "LTX2RefinePresetOverride", "LTX2RefineStageOverride", + "REFINE_FLAT_KEYS", + "REFINE_PRESET_OVERRIDE_FIELDS", + "REFINE_STAGE_OVERRIDE_FIELDS", "refine_override_to_dict", - "refine_preset_override_fields", - "refine_stage_override_fields", ] diff --git a/fastvideo/tests/api/test_ltx2_gpu_pool_translation.py b/fastvideo/tests/api/test_ltx2_gpu_pool_translation.py index cd4fb7fb3..e7fc12f0e 100644 --- a/fastvideo/tests/api/test_ltx2_gpu_pool_translation.py +++ b/fastvideo/tests/api/test_ltx2_gpu_pool_translation.py @@ -4,14 +4,8 @@ Mirrors the ``load_kwargs`` dict that the FastVideo-internal ``ui/ltx2-streaming/server/gpu_pool.py`` passes to ``VideoGenerator.from_pretrained(**load_kwargs)`` and asserts that the -public typed ``GeneratorConfig`` surface (introduced across PRs 0-6) -can represent it end-to-end, with no fields silently falling through -to ``pipeline.experimental``. - -This is the parity guard PR 7.6 depends on: the public gpu_pool -upstream must be able to construct a typed ``GeneratorConfig`` without -knowing any legacy LTX-2 kwarg name, and downstream Dynamo -(``FastVideoArgGroup``) must be able to do the same. +public typed ``GeneratorConfig`` surface can represent it end-to-end, +with no fields silently falling through to ``pipeline.experimental``. """ from __future__ import annotations @@ -122,11 +116,9 @@ class TestGpuPoolForwardTranslation: class TestGpuPoolReverseTranslation: """typed GeneratorConfig -> FastVideoArgs kwargs reproduces the - original gpu_pool flat-kwarg shape. - - This is what lets PR 7.6 wire the public ``gpu_pool`` through - ``generator_config_to_fastvideo_args`` without the runtime noticing. - """ + original gpu_pool flat-kwarg shape, so callers can wire a public + ``gpu_pool`` through ``generator_config_to_fastvideo_args`` without + the runtime noticing.""" @pytest.fixture def args_kwargs(self, monkeypatch): @@ -196,9 +188,7 @@ class TestRefineFlattenCoversAllTypedFields: ) from fastvideo.api.schema import GeneratorConfig, PipelineSelection from fastvideo.pipelines.basic.ltx2.stage_overrides import ( - refine_preset_override_fields, - refine_stage_override_fields, - ) + REFINE_FLAT_KEYS, ) captured: dict[str, object] = {} @@ -223,9 +213,7 @@ class TestRefineFlattenCoversAllTypedFields: "image_crf": 18, "video_position_offset_sec": 2.5, } - all_fields = (refine_preset_override_fields() - | refine_stage_override_fields()) - assert set(refine_payload) == all_fields, ( + assert set(refine_payload) == REFINE_FLAT_KEYS, ( "payload must cover every typed field to exercise the flatten loop") config = GeneratorConfig( diff --git a/fastvideo/tests/api/test_ltx2_stage_overrides.py b/fastvideo/tests/api/test_ltx2_stage_overrides.py index 2f207e7d7..0c9df46a1 100644 --- a/fastvideo/tests/api/test_ltx2_stage_overrides.py +++ b/fastvideo/tests/api/test_ltx2_stage_overrides.py @@ -9,9 +9,9 @@ from fastvideo.api.presets import get_preset, validate_stage_overrides from fastvideo.pipelines.basic.ltx2.stage_overrides import ( LTX2RefinePresetOverride, LTX2RefineStageOverride, + REFINE_PRESET_OVERRIDE_FIELDS, + REFINE_STAGE_OVERRIDE_FIELDS, refine_override_to_dict, - refine_preset_override_fields, - refine_stage_override_fields, ) @@ -57,7 +57,7 @@ class TestRefineStageOverrideDataclass: } def test_fields_accessor_matches_dataclass(self) -> None: - assert refine_stage_override_fields() == frozenset({ + assert REFINE_STAGE_OVERRIDE_FIELDS == frozenset({ "num_inference_steps", "guidance_scale", "image_crf", @@ -86,7 +86,7 @@ class TestRefinePresetOverrideDataclass: } def test_fields_accessor_matches_dataclass(self) -> None: - assert refine_preset_override_fields() == frozenset({ + assert REFINE_PRESET_OVERRIDE_FIELDS == frozenset({ "enabled", "add_noise", }) @@ -101,7 +101,7 @@ class TestStageOverridesMirrorPresetSchema: preset = get_preset("ltx2_two_stage", "ltx2") refine_schema = next( s for s in preset.stage_schemas if s.name == "refine") - assert refine_schema.allowed_overrides == refine_stage_override_fields() + assert refine_schema.allowed_overrides == REFINE_STAGE_OVERRIDE_FIELDS def test_roundtrip_through_validate_stage_overrides(self) -> None: import fastvideo.registry # noqa: F401 diff --git a/fastvideo/tests/api/test_schema_parity_inventory.py b/fastvideo/tests/api/test_schema_parity_inventory.py index 8dc97e807..7321bc500 100644 --- a/fastvideo/tests/api/test_schema_parity_inventory.py +++ b/fastvideo/tests/api/test_schema_parity_inventory.py @@ -71,7 +71,13 @@ def _get_extra_dataclass_fields( continue for _, modname, is_pkg in pkgutil.walk_packages( package.__path__, prefix=f"{package_name}."): - if modname.endswith(".__pycache__"): + # Flat ``configs.pipelines.`` modules carry the config + # directly; colocated ``basic..pipeline_configs`` + # submodules do too. Everything else under ``basic`` is heavy + # model code we don't need to import for a schema check. + basename = modname.rsplit(".", 1)[-1] + is_flat = modname.startswith("fastvideo.configs.pipelines.") + if not is_flat and basename != "pipeline_configs": continue module = importlib.import_module(modname) for obj in vars(module).values():