[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:
co-authored by
Claude Opus 5.5
parent
d728a0dc5f
commit
3f6893a098
@@ -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
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user