[refactor]: accept only the convenience keywords in from_pretrained

VideoGenerator.from_pretrained accepts the 24 common engine and offload
keywords in FROM_PRETRAINED_KWARGS. Any other keyword raises TypeError
with the config path to use with VideoGenerator.from_config. The
conversion of the removed flat keywords (LTX-2 refine names, component
config prefixes, pipeline_config objects, empty-string paths) is removed
from from_pretrained_kwargs_to_config.

The golden kwargs cases that used removed keywords move to a config
category of typed configs; their golden files are byte-identical. The
DreamVerse contract test and the GPU pool forward-translation tests
checked only the removed keyword mapping; the GPU pool tests keep the
typed-config flattening checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
This commit is contained in:
Davids048
2026-10-03 02:23:41 +00:00
co-authored by Claude Opus 5.5
parent d728a0dc5f
commit 3f6893a098
16 changed files with 255 additions and 487 deletions
+8 -10
View File
@@ -30,28 +30,26 @@ bring-up: [Pair two NVIDIA DGX Sparks](../getting_started/installation/spark_pai
## Customizing Generation
- `PipelineConfig`: Initialization time parameters
- `SamplingParam`: Generation time parameters
You can customize generation behavior using `PipelineConfig` and
`SamplingParam`:
`VideoGenerator.from_pretrained` accepts only common engine and offload
keywords, such as `num_gpus` and `dit_cpu_offload`. The full list is
`fastvideo.api.compat.FROM_PRETRAINED_KWARGS`. Pass every other startup
setting to `VideoGenerator.from_config` at its config path, and pass
generation settings to `VideoGenerator.generate` as a request:
```python
from fastvideo import VideoGenerator, PipelineConfig
from fastvideo import VideoGenerator
def main():
model_name = "Wan-AI/Wan2.1-T2V-1.3B-Diffusers"
config = PipelineConfig.from_pretrained(model_name)
config.vae_precision = "fp16"
# Create the generator
generator = VideoGenerator.from_config({
"model_path": model_name,
"engine": {
"num_gpus": 1,
"offload": {"dit_layerwise": True}, # FastVideoArgs option
"offload": {"dit_layerwise": True},
"precision": {"vae": "fp16"},
},
"pipeline": {"experimental": {"pipeline_config": config}},
})
# Generate video with custom parameters
+56 -30
View File
@@ -5,7 +5,7 @@ from collections.abc import Iterator, Mapping
from copy import deepcopy
from dataclasses import fields, is_dataclass
from pathlib import Path
from typing import Any, get_args, get_origin, get_type_hints
from typing import Any, get_args, get_type_hints
from fastvideo.api.inference_resolution import resolve_inference_config
from fastvideo.api.overrides import apply_overrides, normalize_overrides
@@ -42,6 +42,34 @@ REQUEST_BATCH_EXTRA_PASSTHROUGH_FIELDS = (
"vsa_dense_first_n_steps",
"vsa_dense_layers",
)
# The VideoGenerator.from_pretrained keywords besides model_path, config, and log_queue. Every other setting goes
# through VideoGenerator.from_config at its typed path.
FROM_PRETRAINED_KWARGS = frozenset({
"num_gpus",
"revision",
"trust_remote_code",
"distributed_executor_backend",
"tp_size",
"sp_size",
"hsdp_replicate_dim",
"hsdp_shard_dim",
"dist_timeout",
"use_fsdp_inference",
"disable_autocast",
"enable_stage_verification",
"dit_cpu_offload",
"dit_layerwise_offload",
"text_encoder_cpu_offload",
"image_encoder_cpu_offload",
"vae_cpu_offload",
"pin_cpu_memory",
"enable_torch_compile",
"torch_compile_kwargs",
"lora_path",
"lora_strength",
"output_type",
"nvfp4_fa4",
})
# 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.
@@ -75,18 +103,23 @@ def load_generator_config_from_file(
return parse_config(GeneratorConfig, raw)
def legacy_from_pretrained_to_config(
def from_pretrained_kwargs_to_config(
model_path: str,
kwargs: Mapping[str, Any],
) -> GeneratorConfig:
"""Build a ``GeneratorConfig`` from ``VideoGenerator.from_pretrained`` keyword arguments.
A keyword that a schema field declares as its flat name sets that field. The keywords in the branches below
need a conversion instead, and any other keyword is kept in ``pipeline.experimental``.
A keyword that a schema field declares as its flat name sets that field. ``torch_compile_kwargs`` is split across
``engine.compile``, and the keywords without a typed field are kept in ``pipeline.experimental``. A keyword outside
``FROM_PRETRAINED_KWARGS`` raises ``TypeError`` that names the typed path to use with ``from_config``.
"""
raw: dict[str, Any] = {"model_path": model_path}
experimental: dict[str, Any] = {}
unsupported = sorted(set(kwargs) - FROM_PRETRAINED_KWARGS)
if unsupported:
paths = ", ".join(f"{key} -> {_typed_path_of_keyword(key, kwargs[key])}" for key in unsupported)
raise TypeError("VideoGenerator.from_pretrained(...) does not accept these keywords; pass them to "
f"VideoGenerator.from_config(...) at these config paths: {paths}")
raw: dict[str, Any] = {"model_path": model_path}
for key, value in kwargs.items():
if key == "torch_compile_kwargs":
remaining: dict[str, Any] = (dict(deepcopy(value)) if isinstance(value, Mapping) else {})
@@ -95,29 +128,25 @@ def legacy_from_pretrained_to_config(
_set_dotted_path(raw, ["engine", "compile", first_class], remaining.pop(first_class))
if remaining:
_set_dotted_path(raw, ["engine", "compile", "extras"], remaining)
elif key == "pipeline_config" and not isinstance(value, str):
experimental[key] = deepcopy(value)
elif key.startswith(tuple(_COMPONENT_OVERRIDE_PREFIXES)):
component, field_name = key.split(".", 1)
_set_dotted_path(raw, ["pipeline", _COMPONENT_OVERRIDE_PREFIXES[component + "."], field_name], value)
elif key in _LTX2_REFINE_PRESET_KEYWORDS:
_set_dotted_path(raw, ["pipeline", "preset_overrides", "refine", key[len("ltx2_refine_"):]], value)
elif key in _EMPTY_MEANS_UNSET_KEYWORDS:
# An empty string means "no file"; keep typed None.
_set_dotted_path(raw, _EMPTY_MEANS_UNSET_KEYWORDS[key].split("."), value or None)
elif key in _FLAT_NAME_FIELDS:
dotted_path, annotation = _FLAT_NAME_FIELDS[key]
if get_origin(annotation) is dict:
value = dict(deepcopy(value)) if isinstance(value, Mapping) else {}
_set_dotted_path(raw, dotted_path.split("."), value)
_set_dotted_path(raw, _FLAT_NAME_FIELDS[key][0].split("."), value)
else:
experimental[key] = deepcopy(value)
if experimental:
_set_dotted_path(raw, ["pipeline", "experimental"], experimental)
_set_dotted_path(raw, ["pipeline", "experimental", key], deepcopy(value))
return parse_config(GeneratorConfig, raw)
def _typed_path_of_keyword(key: str, value: Any) -> str:
"""The ``GeneratorConfig`` path that holds the setting of a flat ``FastVideoArgs`` keyword."""
if key in _FLAT_NAME_FIELDS and not (key == "pipeline_config" and not isinstance(value, str)):
return _FLAT_NAME_FIELDS[key][0]
if key in _LTX2_REFINE_PRESET_KEYWORDS:
return f"pipeline.preset_overrides.refine.{key[len('ltx2_refine_'):]}"
for prefix, section in _COMPONENT_OVERRIDE_PREFIXES.items():
if key.startswith(prefix):
return f"pipeline.{section}.{key[len(prefix):]}"
return f"pipeline.experimental.{key}"
def generator_config_to_fastvideo_args(
config: GeneratorConfig | Mapping[str, Any] | ResolvedGeneratorConfig, ) -> FastVideoArgs:
"""Resolve a ``GeneratorConfig`` and flatten it into the ``FastVideoArgs`` that runtime code reads.
@@ -282,11 +311,7 @@ _COMPONENT_OVERRIDE_PREFIXES = {
"dit_config.": "dit",
"vae_config.": "vae",
}
# from_pretrained keywords whose empty-string value means "unset", and the field each one sets.
_EMPTY_MEANS_UNSET_KEYWORDS = {
"ltx2_refine_upsampler_path": "pipeline.components.upsampler_weights",
}
# from_pretrained keywords that set pipeline.preset_overrides.refine.<key without the ltx2_refine_ prefix>.
# Flat keywords whose setting is pipeline.preset_overrides.refine.<key without the ltx2_refine_ prefix>.
_LTX2_REFINE_PRESET_KEYWORDS = frozenset({
"ltx2_refine_enabled",
"ltx2_refine_add_noise",
@@ -506,11 +531,12 @@ def _validate_batched_input_length(
__all__ = [
"FROM_PRETRAINED_KWARGS",
"REQUEST_BATCH_EXTRA_PASSTHROUGH_FIELDS",
"explicit_request_raw",
"explicit_request_updates",
"from_pretrained_kwargs_to_config",
"generator_config_to_fastvideo_args",
"legacy_from_pretrained_to_config",
"load_generator_config_from_file",
"normalize_generation_request",
"normalize_generator_config",
+8 -48
View File
@@ -30,8 +30,8 @@ import fastvideo.envs as envs
from fastvideo.api.compat import (
REQUEST_BATCH_EXTRA_PASSTHROUGH_FIELDS,
expand_request_prompt_batch,
from_pretrained_kwargs_to_config,
generator_config_to_fastvideo_args,
legacy_from_pretrained_to_config,
load_generator_config_from_file,
normalize_generation_request,
normalize_generator_config,
@@ -71,33 +71,6 @@ _FFMPEG_ENCODER_OPTION_CACHE: dict[tuple[str, str, str], bool] = {}
_BATCH_EXTRA_PASSTHROUGH_KEYS = tuple(REQUEST_BATCH_EXTRA_PASSTHROUGH_FIELDS)
_FROM_PRETRAINED_CONVENIENCE_KWARGS = frozenset({
"num_gpus",
"revision",
"trust_remote_code",
"distributed_executor_backend",
"tp_size",
"sp_size",
"hsdp_replicate_dim",
"hsdp_shard_dim",
"dist_timeout",
"use_fsdp_inference",
"disable_autocast",
"enable_stage_verification",
"dit_cpu_offload",
"dit_layerwise_offload",
"text_encoder_cpu_offload",
"image_encoder_cpu_offload",
"vae_cpu_offload",
"pin_cpu_memory",
"enable_torch_compile",
"torch_compile_kwargs",
"lora_path",
"lora_strength",
"output_type",
"nvfp4_fa4",
})
def _infer_latent_batch_size(batch: ForwardBatch) -> int:
if isinstance(batch.prompt, list):
@@ -185,20 +158,16 @@ class VideoGenerator:
) -> "VideoGenerator":
"""
Create a video generator from a pretrained model.
Args:
model_path: Path or identifier for the pretrained model
pipeline_config: Pipeline config to use for inference
**kwargs: Additional arguments to customize model loading, set any FastVideoArgs or PipelineConfig attributes here.
**kwargs: The common engine and offload keywords in
``fastvideo.api.compat.FROM_PRETRAINED_KWARGS``, such as
``num_gpus`` and ``dit_cpu_offload``. Pass any other setting
through ``VideoGenerator.from_config(...)``.
Returns:
The created video generator
Priority level: Default pipeline config < User's pipeline config < User's kwargs
Stable convenience kwargs remain supported here for common engine and
offload settings. Advanced model- or pipeline-specific options should
move to VideoGenerator.from_config(...).
"""
log_queue = kwargs.pop("log_queue", None)
if kwargs.pop("nvfp4_fa4", False):
@@ -223,17 +192,8 @@ class VideoGenerator:
if model_path is None:
raise TypeError("model_path or config is required")
legacy_only_kwargs = sorted(set(kwargs) - _FROM_PRETRAINED_CONVENIENCE_KWARGS)
if legacy_only_kwargs:
warnings.warn(
"VideoGenerator.from_pretrained(...) received legacy-only kwargs "
f"({', '.join(legacy_only_kwargs)}); prefer VideoGenerator.from_config(...) "
"for advanced configuration.",
DeprecationWarning,
stacklevel=2,
)
return cls.from_config(
legacy_from_pretrained_to_config(model_path, kwargs),
from_pretrained_kwargs_to_config(model_path, kwargs),
log_queue=log_queue,
)
+98 -36
View File
@@ -8,7 +8,9 @@ and a ``SamplingParam`` where the case has a request) through one of the public
- ``yaml``: every repository config file with a ``generator`` section, through ``load_generator_config_from_file``,
``resolve_inference_config``, and ``generator_config_to_fastvideo_args``, with the resolution decisions; its ``request`` or ``default_request`` through the ``fastvideo generate`` and
``fastvideo serve`` loaders and ``request_to_sampling_param``.
- ``kwargs``: ``VideoGenerator.from_pretrained`` keywords, through ``legacy_from_pretrained_to_config`` and the same
- ``kwargs``: ``VideoGenerator.from_pretrained`` keywords, through ``from_pretrained_kwargs_to_config`` and the same
resolution.
- ``config``: typed ``GeneratorConfig`` mappings, as ``VideoGenerator.from_config`` receives them, through the same
resolution.
- ``cli``: argparse flags, through ``FastVideoArgs.add_cli_args`` and ``FastVideoArgs.from_cli_args``.
- ``environment``: environment variables that ``FastVideoArgs.__post_init__`` folds into fields.
@@ -263,9 +265,19 @@ def _yaml_request(path: Path) -> Any:
def _kwargs_case(model_path: str, kwargs: dict[str, Any]) -> Callable[[], dict[str, Any]]:
def build() -> dict[str, Any]:
from fastvideo.api.compat import legacy_from_pretrained_to_config
from fastvideo.api.compat import from_pretrained_kwargs_to_config
return snapshot_resolved_fastvideo_args(legacy_from_pretrained_to_config(model_path, kwargs))
return snapshot_resolved_fastvideo_args(from_pretrained_kwargs_to_config(model_path, kwargs))
return build
def _config_case(raw: dict[str, Any]) -> Callable[[], dict[str, Any]]:
def build() -> dict[str, Any]:
from fastvideo.api.compat import normalize_generator_config
return snapshot_resolved_fastvideo_args(normalize_generator_config(raw))
return build
@@ -332,9 +344,7 @@ def collect_cases() -> list[SnapshotCase]:
SnapshotCase("yaml",
path.relative_to(REPO_ROOT).as_posix(), _yaml_case(path)) for path in _repository_config_files()
]
# One case per kind of branch in legacy_from_pretrained_to_config: flat names of typed fields, keys left in
# pipeline.experimental, compile sub-keys, LTX-2 refine keys, and a pipeline config JSON path.
pipeline_json = str(REPO_ROOT / "fastvideo/configs/fasthunyuan_t2v.json")
# One case per kind of branch in from_pretrained_kwargs_to_config: flat names of typed fields and compile sub-keys.
kwargs_cases = {
"typed_offload_and_parallelism": (WAN_T2V, {
"num_gpus": 2,
@@ -342,17 +352,6 @@ def collect_cases() -> list[SnapshotCase]:
"dit_cpu_offload": False,
"vae_cpu_offload": False,
}),
"attention_precision_and_flow_shift": (WAN_T2V, {
"attention_backend": "TORCH_SDPA",
"flow_shift": 5.0,
"VSA_sparsity": 0.5,
"dit_precision": "fp32",
}),
"keys_without_typed_fields": (WAN_T2V, {
"master_port": 29600,
"refine_enabled": True,
"boundary_ratio": 0.5,
}),
"torch_compile_kwargs_typed_and_extra": (WAN_T2V, {
"enable_torch_compile": True,
"torch_compile_kwargs": {
@@ -364,32 +363,95 @@ def collect_cases() -> list[SnapshotCase]:
},
},
}),
"ltx2_refine_lora_path": (LTX2, {
"ltx2_refine_lora_path": "/checkpoints/refine_lora.safetensors"
}),
"ltx2_refine_lora_disabled": (LTX2, {
"ltx2_refine_lora_path": ""
}),
"pipeline_config_json_path": ("FastVideo/FastHunyuan-diffusers", {
"pipeline_config": pipeline_json
}),
"disable_autocast": (WAN_T2V, {
"disable_autocast": True
}),
"boundary_ratio": (WAN22_T2V, {
"boundary_ratio": 0.8
}),
"vae_tiling_typed": (LTX2, {
"ltx2_vae_tiling": False
}),
"ltx2_vae_tile_sizes": (LTX2, {
"ltx2_vae_spatial_tile_size_in_pixels": 512,
"ltx2_vae_temporal_tile_size_in_frames": 64,
}),
}
cases += [
SnapshotCase("kwargs", name, _kwargs_case(model, kwargs)) for name, (model, kwargs) in kwargs_cases.items()
]
# Typed configs for the settings that only from_config accepts: typed fields, keys left in
# pipeline.experimental, LTX-2 refine and VAE tile fields, and a pipeline config JSON path.
pipeline_json = str(REPO_ROOT / "fastvideo/configs/fasthunyuan_t2v.json")
config_cases = {
"attention_precision_and_flow_shift": {
"model_path": WAN_T2V,
"engine": {
"attention": {
"backend": "TORCH_SDPA",
"vsa_sparsity": 0.5
},
"precision": {
"dit": "fp32"
}
},
"pipeline": {
"flow_shift": 5.0
},
},
"keys_without_typed_fields": {
"model_path": WAN_T2V,
"pipeline": {
"experimental": {
"master_port": 29600,
"refine_enabled": True,
"boundary_ratio": 0.5
}
},
},
"ltx2_refine_lora_path": {
"model_path": LTX2,
"pipeline": {
"ltx2": {
"refine": {
"lora_path": "/checkpoints/refine_lora.safetensors"
}
}
},
},
"ltx2_refine_lora_disabled": {
"model_path": LTX2,
"pipeline": {
"ltx2": {
"refine": {
"lora_path": ""
}
}
},
},
"pipeline_config_json_path": {
"model_path": "FastVideo/FastHunyuan-diffusers",
"pipeline": {
"components": {
"pipeline_config_path": pipeline_json
}
},
},
"boundary_ratio": {
"model_path": WAN22_T2V,
"pipeline": {
"experimental": {
"boundary_ratio": 0.8
}
},
},
"vae_tiling_typed": {
"model_path": LTX2,
"pipeline": {
"vae_tiling": False
},
},
"ltx2_vae_tile_sizes": {
"model_path": LTX2,
"pipeline": {
"ltx2": {
"vae_spatial_tile_size_in_pixels": 512,
"vae_temporal_tile_size_in_frames": 64
}
},
},
}
cases += [SnapshotCase("config", name, _config_case(raw)) for name, raw in config_cases.items()]
# argparse defaults differ from the dataclass defaults, so model_path_only is the CLI baseline.
cli_cases = {
"model_path_only": ["--model-path", WAN_T2V],
+19 -43
View File
@@ -5,9 +5,11 @@ PR 6.
"""
from __future__ import annotations
import pytest
from fastvideo.api.compat import (
from_pretrained_kwargs_to_config,
generator_config_to_fastvideo_args,
legacy_from_pretrained_to_config,
request_to_sampling_param,
)
from fastvideo.api.parser import parse_config
@@ -15,13 +17,14 @@ from fastvideo.api.schema import CompileConfig, GenerationRequest, GeneratorConf
from fastvideo.api.sampling_param import SamplingParam
class TestLegacyTorchCompileKwargsTranslation:
"""Legacy ``torch_compile_kwargs={...}`` gets split across the four
first-class :class:`CompileConfig` fields and anything unknown falls
into ``extras``."""
class TestFromPretrainedKwargsTranslation:
"""The ``from_pretrained`` keyword ``torch_compile_kwargs={...}`` gets
split across the four first-class :class:`CompileConfig` fields and
anything unknown falls into ``extras``. Keywords outside the
``from_pretrained`` set are rejected with their typed path."""
def test_empty_kwargs_produces_empty_extras(self) -> None:
config = legacy_from_pretrained_to_config(
config = from_pretrained_kwargs_to_config(
"/models/ltx2",
{"torch_compile_kwargs": {}},
)
@@ -29,6 +32,10 @@ class TestLegacyTorchCompileKwargsTranslation:
assert compile_config.extras == {}
assert compile_config.backend is None
def test_other_keyword_names_its_typed_path(self) -> None:
with pytest.raises(TypeError, match="ltx2_vae_tiling -> pipeline.vae_tiling"):
from_pretrained_kwargs_to_config("/models/ltx2", {"ltx2_vae_tiling": True})
class TestCompileConfigRoundTrip:
"""typed CompileConfig -> FastVideoArgs.torch_compile_kwargs
@@ -78,21 +85,9 @@ class TestCompileConfigRoundTrip:
assert args.kwargs["torch_compile_kwargs"] == {}
class TestLegacyLtx2VaeTilingTranslation:
"""``ltx2_vae_tiling`` flat kwarg promotes to
``generator.pipeline.vae_tiling``; reverse direction emits the
legacy name back to FastVideoArgs."""
def test_true_round_trips(self) -> None:
config = legacy_from_pretrained_to_config(
"/models/ltx2",
{"ltx2_vae_tiling": True},
)
assert config.pipeline.vae_tiling is True
def test_unset_stays_none(self) -> None:
config = legacy_from_pretrained_to_config("/models/ltx2", {})
assert config.pipeline.vae_tiling is None
class TestLtx2VaeTilingFlattening:
"""``generator.pipeline.vae_tiling`` reaches FastVideoArgs as the flat
keyword ``ltx2_vae_tiling``."""
def test_reverse_emits_legacy_name(self, monkeypatch) -> None:
_stub_fastvideo_args_from_kwargs(monkeypatch)
@@ -114,31 +109,12 @@ class TestLegacyLtx2VaeTilingTranslation:
assert "ltx2_vae_tiling" not in args.kwargs
class TestLegacyTextEncoderCompileTranslation:
"""``enable_torch_compile_text_encoder`` flat kwarg promotes to
``generator.engine.compile.text_encoder_enabled``; reverse direction
emits the legacy name back onto the FastVideoArgs kwargs dict so
class TestTextEncoderCompileFlattening:
"""``generator.engine.compile.text_encoder_enabled`` reaches the
FastVideoArgs kwargs dict as ``enable_torch_compile_text_encoder`` so
realtime-runtime consumers can read it before FastVideoArgs filters
unknown fields."""
def test_forward_routes_to_compile_text_encoder_enabled(self) -> None:
config = legacy_from_pretrained_to_config(
"/models/ltx2",
{"enable_torch_compile_text_encoder": True},
)
assert config.engine.compile.text_encoder_enabled is True
def test_false_round_trips(self) -> None:
config = legacy_from_pretrained_to_config(
"/models/ltx2",
{"enable_torch_compile_text_encoder": False},
)
assert config.engine.compile.text_encoder_enabled is False
def test_unset_stays_none(self) -> None:
config = legacy_from_pretrained_to_config("/models/ltx2", {})
assert config.engine.compile.text_encoder_enabled is None
def test_reverse_emits_legacy_name(self, monkeypatch) -> None:
_stub_fastvideo_args_from_kwargs(monkeypatch)
config = GeneratorConfig(
@@ -1,142 +1,67 @@
# SPDX-License-Identifier: Apache-2.0
"""gpu_pool-style flat-kwarg integration tests.
"""LTX-2 streaming-server config flattening tests.
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.
``GPU_POOL_CONFIG`` is the typed ``GeneratorConfig`` form of the settings that the FastVideo-internal
``ui/ltx2-streaming/server/gpu_pool.py`` loads. The tests check that ``generator_config_to_fastvideo_args`` turns it
into the flat ``FastVideoArgs`` keywords that the LTX-2 runtime reads.
"""
from __future__ import annotations
from copy import deepcopy
import pytest
from fastvideo.api.compat import (
from_pretrained_kwargs_to_config,
generator_config_to_fastvideo_args,
legacy_from_pretrained_to_config,
normalize_generator_config,
)
# Mirrors FastVideo-internal/ui/ltx2-streaming/server/gpu_pool.py
# :lines 233-260 (load_kwargs constructed for VideoGenerator.from_pretrained).
#
# One item from gpu_pool.py's load_kwargs is deliberately excluded:
# - ``pipeline_config=<PipelineConfig instance>`` — an opaque Python
# object; internal mutates it in place (``dit_config.quant_config =
# NVFP4Config()``). The typed path for quantization is tracked in
# "Known Technical Debt" in PR plan.md; ``pipeline_config`` as an
# instance legitimately belongs in ``pipeline.experimental``.
#
# ``enable_torch_compile_text_encoder`` IS included below: its typed
# home is ``CompileConfig.text_encoder_enabled`` (added post-review).
# The legacy ``FastVideoArgs`` path does not yet consume it; the
# realtime runtime (PR 7.6) reads it off the kwargs dict before
# FastVideoArgs filtering.
GPU_POOL_LOAD_KWARGS = {
"config_model_path": "/models/ltx2-distilled/config",
"num_gpus": 1,
"dit_layerwise_offload": False,
"use_fsdp_inference": False,
"dit_cpu_offload": False,
"vae_cpu_offload": False,
"text_encoder_cpu_offload": False,
"pin_cpu_memory": True,
"ltx2_vae_tiling": False,
"ltx2_refine_enabled": True,
"ltx2_refine_upsampler_path": "/models/ltx2-distilled/spatial_upsampler",
"ltx2_refine_lora_path": "",
"ltx2_refine_num_inference_steps": 2,
"ltx2_refine_guidance_scale": 1.0,
"ltx2_refine_add_noise": True,
"enable_torch_compile": True,
"enable_torch_compile_text_encoder": True,
"torch_compile_kwargs": {
"backend": "inductor",
"fullgraph": True,
"mode": "max-autotune-no-cudagraphs",
"dynamic": False,
GPU_POOL_CONFIG = {
"model_path": "FastVideo/LTX2-Distilled-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False,
"offload": {
"dit": False,
"dit_layerwise": False,
"vae": False,
"text_encoder": False,
"pin_cpu_memory": True,
},
"compile": {
"enabled": True,
"text_encoder_enabled": True,
"backend": "inductor",
"fullgraph": True,
"mode": "max-autotune-no-cudagraphs",
"dynamic": False,
},
},
"pipeline": {
"components": {
"config_root": "/models/ltx2-distilled/config",
"upsampler_weights": "/models/ltx2-distilled/spatial_upsampler",
},
"vae_tiling": False,
# An empty refine LoRA path keeps the refine LoRA disabled; None would load the checkpoint default.
"ltx2": {
"refine": {
"lora_path": ""
}
},
"preset_overrides": {
"refine": {
"enabled": True,
"num_inference_steps": 2,
"guidance_scale": 1.0,
"add_noise": True,
}
},
},
}
class TestGpuPoolForwardTranslation:
"""gpu_pool flat kwargs -> typed GeneratorConfig."""
@pytest.fixture(scope="class")
def config(self):
return legacy_from_pretrained_to_config(
"FastVideo/LTX2-Distilled-Diffusers",
GPU_POOL_LOAD_KWARGS,
)
def test_model_path_set(self, config) -> None:
assert config.model_path == "FastVideo/LTX2-Distilled-Diffusers"
def test_engine_basics(self, config) -> None:
assert config.engine.num_gpus == 1
assert config.engine.use_fsdp_inference is False
def test_offload_config(self, config) -> None:
assert config.engine.offload.dit is False
assert config.engine.offload.dit_layerwise is False
assert config.engine.offload.vae is False
assert config.engine.offload.text_encoder is False
assert config.engine.offload.pin_cpu_memory is True
def test_compile_config_typed_fields_extracted(self, config) -> None:
compile_config = config.engine.compile
assert compile_config.enabled is True
assert compile_config.text_encoder_enabled is True
assert compile_config.backend == "inductor"
assert compile_config.fullgraph is True
assert compile_config.mode == "max-autotune-no-cudagraphs"
assert compile_config.dynamic is False
assert compile_config.extras == {}
def test_vae_tiling_routed_to_pipeline(self, config) -> None:
assert config.pipeline.vae_tiling is False
def test_config_model_path_routed_to_components(self, config) -> None:
assert config.pipeline.components.config_root == "/models/ltx2-distilled/config"
def test_refine_upsampler_routed_to_components(self, config) -> None:
assert config.pipeline.components.upsampler_weights == ("/models/ltx2-distilled/spatial_upsampler")
def test_empty_refine_lora_stays_disabled(self, config) -> None:
# gpu_pool passes "" to keep the refine LoRA disabled. None would
# load the checkpoint's default refine LoRA instead.
assert config.pipeline.ltx2.refine.lora_path == ""
assert config.pipeline.components.lora_path is None
def test_refine_preset_overrides(self, config) -> None:
refine = config.pipeline.preset_overrides.get("refine", {})
assert refine == {
"enabled": True,
"num_inference_steps": 2,
"guidance_scale": 1.0,
"add_noise": True,
}
def test_no_experimental_leakage(self, config) -> None:
"""Every gpu_pool kwarg should have a typed home — nothing should
silently fall through to ``pipeline.experimental``."""
assert config.pipeline.experimental == {}
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.
"""
class TestGpuPoolFlattening:
"""The typed gpu_pool config -> the FastVideoArgs keywords that the LTX-2 runtime reads."""
@pytest.fixture
def args_kwargs(self, monkeypatch):
@@ -155,13 +80,12 @@ class TestGpuPoolReverseTranslation:
monkeypatch.setattr(fva.FastVideoArgs, "from_kwargs", _capture)
config = legacy_from_pretrained_to_config(
"FastVideo/LTX2-Distilled-Diffusers",
GPU_POOL_LOAD_KWARGS,
)
generator_config_to_fastvideo_args(config)
generator_config_to_fastvideo_args(normalize_generator_config(GPU_POOL_CONFIG))
return captured
def test_empty_refine_lora_path_reemitted(self, args_kwargs) -> None:
assert args_kwargs["ltx2_refine_lora_path"] == ""
def test_ltx2_refine_flags_reemitted(self, args_kwargs) -> None:
assert args_kwargs["ltx2_refine_enabled"] is True
assert args_kwargs["ltx2_refine_add_noise"] is True
@@ -274,15 +198,17 @@ class TestCompileExtrasPreserved:
monkeypatch.setattr(fva.FastVideoArgs, "from_kwargs", _capture)
kwargs = deepcopy(GPU_POOL_LOAD_KWARGS)
kwargs["torch_compile_kwargs"] = {
"backend": "inductor",
"options": {
"triton.cudagraphs": False
kwargs = {
"enable_torch_compile": True,
"torch_compile_kwargs": {
"backend": "inductor",
"options": {
"triton.cudagraphs": False
},
"disable": False,
},
"disable": False,
}
config = legacy_from_pretrained_to_config("FastVideo/LTX2-Distilled-Diffusers", kwargs)
config = from_pretrained_kwargs_to_config("FastVideo/LTX2-Distilled-Diffusers", kwargs)
assert config.engine.compile.backend == "inductor"
assert config.engine.compile.extras == {
"options": {
@@ -1,176 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
"""Contract test: Dreamverse-style inputs normalize through the public
typed API without needing any private-only compatibility promise.
The private Dreamverse UI server (``FastVideo-internal/ui/ltx2-streaming/
server/gpu_pool.py``) has historically called
``VideoGenerator.from_pretrained(**load_kwargs)`` with a flat kwarg bag
containing LTX-2-specific names (``ltx2_refine_enabled``,
``ltx2_refine_upsampler_path``, etc.). PR 6 gave every one of those
kwargs a typed home under ``GeneratorConfig``.
This test makes sure:
1. The public typed API can represent everything Dreamverse currently
passes at init time (``legacy_from_pretrained_to_config``).
2. Private-only Dreamverse fields that don't belong on the public
surface either go to ``pipeline.experimental`` / ``request.extensions``
(the documented escape hatch) or raise explicitly, rather than
silently becoming part of the public compatibility promise.
Regression guard for the scoping rule in ``apirefactor.md`` §"Schema
Parity Requirement".
"""
from __future__ import annotations
import pytest
from fastvideo.api import (
ComponentConfig,
CompileConfig,
GeneratorConfig,
GenerationRequest,
)
from fastvideo.api.compat import (
legacy_from_pretrained_to_config,
normalize_generation_request,
)
def _dreamverse_load_kwargs() -> dict:
"""The exact shape internal ``gpu_pool.py`` passes to
``VideoGenerator.from_pretrained(**load_kwargs)`` today."""
return {
"config_model_path": "/models/ltx2-config",
"ltx2_refine_enabled": True,
"ltx2_refine_upsampler_path": "/models/ltx2-refine",
"ltx2_refine_lora_path": "/models/ltx2-refine-lora",
"ltx2_refine_num_inference_steps": 2,
"ltx2_refine_guidance_scale": 1.0,
"ltx2_refine_add_noise": True,
"ltx2_vae_tiling": True,
"torch_compile_kwargs": {
"backend": "inductor",
"mode": "reduce-overhead",
"fullgraph": True,
},
"dit_cpu_offload": False,
"vae_cpu_offload": False,
"text_encoder_cpu_offload": False,
"pin_cpu_memory": True,
"use_fsdp_inference": False,
"enable_torch_compile": True,
}
class TestDreamverseLoadKwargsShape:
"""Every current Dreamverse init-time kwarg must land on a typed
field, not in the ``experimental`` escape hatch."""
def test_all_kwargs_land_on_typed_fields(self):
config = legacy_from_pretrained_to_config("/models/ltx2", _dreamverse_load_kwargs())
assert isinstance(config, GeneratorConfig)
# None of the kwargs should have been routed to experimental.
assert config.pipeline.experimental == {}, ("Dreamverse kwargs leaked into pipeline.experimental: "
f"{config.pipeline.experimental}")
def test_refine_enabled_reaches_preset_overrides(self):
config = legacy_from_pretrained_to_config("/models/ltx2", _dreamverse_load_kwargs())
refine = config.pipeline.preset_overrides.get("refine") or {}
assert refine.get("enabled") is True
assert refine.get("add_noise") is True
assert refine.get("num_inference_steps") == 2
assert refine.get("guidance_scale") == 1.0
def test_refine_assets_reach_component_config(self):
config = legacy_from_pretrained_to_config("/models/ltx2", _dreamverse_load_kwargs())
assert isinstance(config.pipeline.components, ComponentConfig)
assert (config.pipeline.components.upsampler_weights == "/models/ltx2-refine")
assert config.pipeline.ltx2.refine.lora_path == "/models/ltx2-refine-lora"
assert config.pipeline.components.lora_path is None
assert config.pipeline.components.config_root == "/models/ltx2-config"
def test_torch_compile_kwargs_reach_typed_fields(self):
config = legacy_from_pretrained_to_config("/models/ltx2", _dreamverse_load_kwargs())
assert isinstance(config.engine.compile, CompileConfig)
assert config.engine.compile.enabled is True
assert config.engine.compile.backend == "inductor"
assert config.engine.compile.mode == "reduce-overhead"
assert config.engine.compile.fullgraph is True
# extras should be empty — all four common kwargs are first class.
assert config.engine.compile.extras == {}
def test_uncommon_compile_kwargs_fall_to_extras(self):
kwargs = _dreamverse_load_kwargs()
kwargs["torch_compile_kwargs"] = {
**kwargs["torch_compile_kwargs"],
"options": {
"epilogue_fusion": True
},
}
config = legacy_from_pretrained_to_config("/models/ltx2", kwargs)
assert config.engine.compile.extras == {
"options": {
"epilogue_fusion": True
},
}
def test_vae_tiling_reaches_pipeline_selection(self):
config = legacy_from_pretrained_to_config("/models/ltx2", _dreamverse_load_kwargs())
assert config.pipeline.vae_tiling is True
def test_offload_fields_reach_typed_offload_config(self):
config = legacy_from_pretrained_to_config("/models/ltx2", _dreamverse_load_kwargs())
assert config.engine.offload.dit is False
assert config.engine.offload.vae is False
assert config.engine.offload.text_encoder is False
assert config.engine.offload.pin_cpu_memory is True
class TestDreamversePrivateOnlyFields:
"""Dreamverse carries a handful of private-only names (e.g. legacy
internal aliases). These must NOT silently turn into a public
compatibility promise — the documented contract is that unknown
fields land on ``pipeline.experimental`` so integrators see them
but FastVideo does not promise to preserve them."""
def test_unknown_kwarg_routes_to_experimental(self):
kwargs = _dreamverse_load_kwargs()
kwargs["dreamverse_internal_only_flag"] = "private"
config = legacy_from_pretrained_to_config("/models/ltx2", kwargs)
assert config.pipeline.experimental == {
"dreamverse_internal_only_flag": "private",
}
class TestDreamverseRequestShape:
"""The per-segment Dreamverse request path mirrors OpenAI's shape
plus a few LTX-2 knobs. All of them must have a typed home."""
def test_return_state_reaches_output_config(self):
"""PR 7 added ``output.return_state`` — must survive the legacy
translation path so Dreamverse callers can opt in."""
request = GenerationRequest(
prompt="x",
output=__import__("fastvideo.api", fromlist=["OutputConfig"]).OutputConfig(return_state=True),
)
normalized = normalize_generation_request(request)
assert normalized.output.return_state is True
class TestDreamverseNoPrivateImports:
"""The public entry points must not force a Dreamverse integrator
to import from ``fastvideo.pipelines.*`` or other internal paths."""
@pytest.mark.parametrize(
"import_path",
[
"fastvideo",
"fastvideo.api",
"fastvideo.api.compat", # public in that it's re-exported
],
)
def test_public_imports_resolve(self, import_path):
import importlib
importlib.import_module(import_path)
@@ -671,22 +671,18 @@ def test_from_pretrained_convenience_kwargs_do_not_warn(monkeypatch):
assert generator.config.engine.num_gpus == 4
def test_from_pretrained_legacy_only_kwargs_warn(monkeypatch):
def test_from_pretrained_rejects_other_kwargs_with_their_config_path(monkeypatch):
captured = _patch_from_fastvideo_args(monkeypatch)
_patch_fastvideo_args_from_kwargs(monkeypatch)
with pytest.warns(DeprecationWarning, match="legacy-only kwargs"):
generator = VideoGenerator.from_pretrained(
with pytest.raises(TypeError, match="workload_type -> pipeline.workload_type"):
VideoGenerator.from_pretrained(
"test-model",
num_gpus=4,
workload_type="t2v",
)
assert captured["fastvideo_args"].model_path == "test-model"
assert captured["fastvideo_args"].num_gpus == 4
assert captured["fastvideo_args"].workload_type.value == "t2v"
assert generator.config is not None
assert generator.config.pipeline.workload_type == "t2v"
assert "fastvideo_args" not in captured
def test_generate_uses_typed_request_path(monkeypatch):