Parameters that belong to one model family, or to one component config,
could be set only through argparse flags or pipeline.experimental. They gain
typed GeneratorConfig fields, each with the flat name that FastVideoArgs or
PipelineConfig uses:
- pipeline.dit and pipeline.vae: dicts of DiT and VAE config field
overrides, passed as dit_config.<key> and vae_config.<key>
- pipeline.ltx2: VAE tile sizes, initial video and audio latent paths, noise
order, distilled sigmas, and refine.{transformer,noise,audio_noise}_path
- pipeline.minimax_h3: sequential load, video decode backend, TAEH3
checkpoint and chunk size, and the sequence-parallel VAE switches
- pipeline.longcat: the block sparse attention (BSA) settings
legacy_from_pretrained_to_config routes dit_config.* and vae_config.*
keywords into the new dicts. Every new scalar field defaults to None and is
not passed to FastVideoArgs while unset. Behavior is unchanged: the golden
config snapshots match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
399 lines
13 KiB
Python
399 lines
13 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
import json
|
|
|
|
import yaml
|
|
|
|
from fastvideo.api import (
|
|
config_to_dict,
|
|
ContinuationState,
|
|
GenerationRequest,
|
|
GeneratorConfig,
|
|
GpuPoolConfig,
|
|
load_run_config,
|
|
load_serve_config,
|
|
parse_config,
|
|
PlannedStage,
|
|
PromptEnhancerConfig,
|
|
PromptSafetyConfig,
|
|
RunConfig,
|
|
ServeConfig,
|
|
StreamingConfig,
|
|
WarmupConfig,
|
|
)
|
|
|
|
|
|
def test_parse_config_builds_nested_typed_config() -> None:
|
|
raw = {
|
|
"generator": {
|
|
"model_path": "/models/ltx2",
|
|
"pipeline": {
|
|
"workload_type": "t2v",
|
|
"preset": "ltx2_two_stage",
|
|
},
|
|
},
|
|
"request": {
|
|
"prompt": ["a fox", "a wolf"],
|
|
"sampling": {
|
|
"num_frames": 121,
|
|
"height": 1024,
|
|
"width": 1536,
|
|
"guidance_scale": 1.5,
|
|
},
|
|
"state": {
|
|
"kind": "ltx2_continuation",
|
|
"payload": {
|
|
"segment_index": 1
|
|
},
|
|
},
|
|
"plan": {
|
|
"stages": [{
|
|
"name": "base",
|
|
"kind": "sample",
|
|
}]
|
|
},
|
|
},
|
|
}
|
|
|
|
config = parse_config(RunConfig, raw)
|
|
|
|
assert config.generator.pipeline.preset == "ltx2_two_stage"
|
|
assert config.request.prompt == ["a fox", "a wolf"]
|
|
assert config.request.state == ContinuationState(
|
|
kind="ltx2_continuation",
|
|
payload={"segment_index": 1},
|
|
)
|
|
assert config.request.plan is not None
|
|
assert config.request.plan.stages == [PlannedStage(name="base", kind="sample")]
|
|
|
|
|
|
def test_parse_config_accepts_existing_typed_instance() -> None:
|
|
typed = RunConfig(
|
|
generator=GeneratorConfig(model_path="/models/base"),
|
|
request=GenerationRequest(prompt="hello"),
|
|
)
|
|
|
|
assert parse_config(RunConfig, typed) is typed
|
|
|
|
|
|
def test_load_run_config_supports_yaml_roundtrip(tmp_path) -> None:
|
|
raw = {
|
|
"generator": {
|
|
"model_path": "/models/wan"
|
|
},
|
|
"request": {
|
|
"prompt": "hello",
|
|
"sampling": {
|
|
"num_frames": 16
|
|
},
|
|
},
|
|
}
|
|
path = tmp_path / "run.yaml"
|
|
path.write_text(yaml.safe_dump(raw), encoding="utf-8")
|
|
|
|
loaded = load_run_config(path)
|
|
|
|
assert config_to_dict(loaded) == {
|
|
"generator": {
|
|
"model_path": "/models/wan",
|
|
"revision": None,
|
|
"trust_remote_code": False,
|
|
"engine": {
|
|
"num_gpus": 1,
|
|
"execution_backend": "mp",
|
|
"parallelism": {
|
|
"tp_size": -1,
|
|
"sp_size": -1,
|
|
"hsdp_replicate_dim": 1,
|
|
"hsdp_shard_dim": -1,
|
|
"dist_timeout": None,
|
|
},
|
|
"offload": {
|
|
"dit": True,
|
|
"dit_layerwise": True,
|
|
"text_encoder": True,
|
|
"image_encoder": True,
|
|
"vae": True,
|
|
"pin_cpu_memory": True,
|
|
"lazy_module_load": None,
|
|
},
|
|
"compile": {
|
|
"enabled": False,
|
|
"backend": None,
|
|
"fullgraph": None,
|
|
"mode": None,
|
|
"dynamic": None,
|
|
"extras": {},
|
|
"text_encoder_enabled": None,
|
|
"vae_enabled": None,
|
|
"audio_vae_enabled": None,
|
|
"regional": None,
|
|
"dit_kwargs": {},
|
|
"text_encoder_kwargs": {},
|
|
"vae_kwargs": {},
|
|
"audio_vae_kwargs": {},
|
|
},
|
|
"attention": {
|
|
"backend": None,
|
|
"vsa_sparsity": None,
|
|
"vsa_tile_size": None,
|
|
"moba_config_path": None,
|
|
},
|
|
"precision": {
|
|
"dit": None,
|
|
"vae": None,
|
|
"vae_decode": None,
|
|
"image_encoder": None,
|
|
"text_encoders": None,
|
|
},
|
|
"enable_stage_verification": True,
|
|
"use_fsdp_inference": False,
|
|
"disable_autocast": False,
|
|
"quantization": None,
|
|
},
|
|
"pipeline": {
|
|
"workload_type": None,
|
|
"preset": None,
|
|
"preset_version": None,
|
|
"components": {
|
|
"config_root": None,
|
|
"pipeline_config_path": None,
|
|
"text_encoder_weights": None,
|
|
"transformer_weights": None,
|
|
"transformer_2_weights": None,
|
|
"vae_weights": None,
|
|
"upsampler_weights": None,
|
|
"lora_path": None,
|
|
"lora_nickname": "default",
|
|
"lora_strength": 1.0,
|
|
"lora_target_modules": None,
|
|
"override_pipeline_cls_name": None,
|
|
"override_transformer_cls_name": None,
|
|
},
|
|
"vae_tiling": None,
|
|
"vae_sp": None,
|
|
"flow_shift": None,
|
|
"embedded_cfg_scale": None,
|
|
"dmd_denoising_steps": None,
|
|
"dit": {},
|
|
"vae": {},
|
|
"ltx2": {
|
|
"vae_spatial_tile_size_in_pixels": None,
|
|
"vae_spatial_tile_overlap_in_pixels": None,
|
|
"vae_temporal_tile_size_in_frames": None,
|
|
"vae_temporal_tile_overlap_in_frames": None,
|
|
"initial_latent_path": None,
|
|
"audio_latent_path": None,
|
|
"legacy_native_noise_order": None,
|
|
"use_distilled_sigmas": None,
|
|
"refine": {
|
|
"transformer_path": None,
|
|
"noise_path": None,
|
|
"audio_noise_path": None,
|
|
},
|
|
},
|
|
"minimax_h3": {
|
|
"sequential_load": None,
|
|
"video_decode_backend": None,
|
|
"taeh3_checkpoint": None,
|
|
"taeh3_chunk_size": None,
|
|
"vae_parallel_decode": None,
|
|
"vae_parallel_encode": None,
|
|
"vae_parallel_decode_strategy": None,
|
|
},
|
|
"longcat": {
|
|
"enable_bsa": None,
|
|
"bsa_sparsity": None,
|
|
"bsa_cdf_threshold": None,
|
|
"bsa_chunk_q": None,
|
|
"bsa_chunk_k": None,
|
|
},
|
|
"preset_overrides": {},
|
|
"experimental": {},
|
|
},
|
|
},
|
|
"request": {
|
|
"prompt": "hello",
|
|
"negative_prompt": None,
|
|
"inputs": {
|
|
"prompt_path": None,
|
|
"image_path": None,
|
|
"video_path": None,
|
|
"pil_image": None,
|
|
"last_image": None,
|
|
"references": None,
|
|
"latents": None,
|
|
"audio_latents": None,
|
|
"pose": None,
|
|
"mouse_cond": None,
|
|
"keyboard_cond": None,
|
|
"grid_sizes": None,
|
|
"c2ws_plucker_emb": None,
|
|
"action_path": None,
|
|
"refine_from": None,
|
|
"stage1_video": None,
|
|
},
|
|
"sampling": {
|
|
"num_videos_per_prompt": 1,
|
|
"seed": 1024,
|
|
"max_sequence_length": None,
|
|
"num_frames": 16,
|
|
"height": 720,
|
|
"width": 1280,
|
|
"height_sr": 1072,
|
|
"width_sr": 1920,
|
|
"fps": 24,
|
|
"num_inference_steps": 50,
|
|
"num_inference_steps_sr": 50,
|
|
"guidance_scale": 1.0,
|
|
"batch_cfg": False,
|
|
"guidance_scale_2": None,
|
|
"cfg_normalization": False,
|
|
"cfg_truncation": 1.0,
|
|
"guidance_rescale": 0.0,
|
|
"true_cfg_scale": None,
|
|
"use_embedded_guidance": None,
|
|
"boundary_ratio": None,
|
|
"sigmas": None,
|
|
},
|
|
"runtime": {
|
|
"enable_teacache": False,
|
|
"return_trajectory_latents": False,
|
|
"return_trajectory_decoded": False,
|
|
},
|
|
"output": {
|
|
"output_path": "outputs/",
|
|
"output_video_name": None,
|
|
"save_video": True,
|
|
"return_frames": True,
|
|
"return_state": False,
|
|
},
|
|
"stage_overrides": {},
|
|
"state": None,
|
|
"plan": None,
|
|
"extensions": {},
|
|
},
|
|
}
|
|
|
|
|
|
def test_load_serve_config_supports_json_roundtrip(tmp_path) -> None:
|
|
raw = {
|
|
"generator": {
|
|
"model_path": "/models/server"
|
|
},
|
|
"server": {
|
|
"port": 9000
|
|
},
|
|
"default_request": {
|
|
"prompt": "serve default"
|
|
},
|
|
}
|
|
path = tmp_path / "serve.json"
|
|
path.write_text(json.dumps(raw), encoding="utf-8")
|
|
|
|
loaded = load_serve_config(path)
|
|
|
|
assert isinstance(loaded, ServeConfig)
|
|
assert loaded.server.port == 9000
|
|
assert loaded.default_request.prompt == "serve default"
|
|
|
|
|
|
def test_serve_config_streaming_defaults_to_none() -> None:
|
|
raw = {"generator": {"model_path": "/models/server"}}
|
|
loaded = parse_config(ServeConfig, raw)
|
|
assert loaded.streaming is None
|
|
|
|
|
|
def test_serve_config_parses_streaming_block() -> None:
|
|
raw = {
|
|
"generator": {
|
|
"model_path": "/models/server"
|
|
},
|
|
"streaming": {
|
|
"session_timeout_seconds": 120,
|
|
"generation_segment_cap": 4,
|
|
"stream_mode": "legacy_jpeg",
|
|
"warmup": {
|
|
"enabled": False,
|
|
"prompt": "warmup prompt",
|
|
"timeout_seconds": 600,
|
|
},
|
|
"pool": {
|
|
"num_workers": 2,
|
|
"enable_audio_reencode": False,
|
|
"conditioning_num_frames": 5,
|
|
"conditioning_end_offset": 1,
|
|
},
|
|
"prompt": {
|
|
"provider": "groq",
|
|
"model": "llama-3-70b",
|
|
"timeout_ms": 10000,
|
|
"system_prompt_dir": "/opt/prompts",
|
|
},
|
|
"safety": {
|
|
"enabled": True,
|
|
"classifier_path": "/opt/safety.pt",
|
|
},
|
|
},
|
|
}
|
|
|
|
loaded = parse_config(ServeConfig, raw)
|
|
|
|
assert isinstance(loaded.streaming, StreamingConfig)
|
|
assert loaded.streaming.session_timeout_seconds == 120
|
|
assert loaded.streaming.generation_segment_cap == 4
|
|
assert loaded.streaming.stream_mode == "legacy_jpeg"
|
|
assert loaded.streaming.warmup == WarmupConfig(enabled=False, prompt="warmup prompt", timeout_seconds=600)
|
|
assert loaded.streaming.pool == GpuPoolConfig(
|
|
num_workers=2,
|
|
enable_audio_reencode=False,
|
|
conditioning_num_frames=5,
|
|
conditioning_end_offset=1,
|
|
)
|
|
assert loaded.streaming.prompt == PromptEnhancerConfig(
|
|
provider="groq",
|
|
model="llama-3-70b",
|
|
timeout_ms=10000,
|
|
system_prompt_dir="/opt/prompts",
|
|
)
|
|
assert loaded.streaming.safety == PromptSafetyConfig(enabled=True, classifier_path="/opt/safety.pt")
|
|
|
|
|
|
def test_serve_config_streaming_round_trip_through_config_to_dict() -> None:
|
|
raw = {
|
|
"generator": {
|
|
"model_path": "/models/server"
|
|
},
|
|
"streaming": {
|
|
"session_timeout_seconds": 600
|
|
},
|
|
}
|
|
loaded = parse_config(ServeConfig, raw)
|
|
dumped = config_to_dict(loaded)
|
|
assert dumped["streaming"]["session_timeout_seconds"] == 600
|
|
assert dumped["streaming"]["warmup"]["enabled"] is True
|
|
assert dumped["streaming"]["prompt"]["enabled"] is False
|
|
assert dumped["streaming"]["prompt"]["provider"] == "cerebras"
|
|
assert dumped["streaming"]["safety"]["enabled"] is False
|
|
|
|
|
|
def test_load_serve_config_with_streaming_from_yaml(tmp_path) -> None:
|
|
raw = {
|
|
"generator": {
|
|
"model_path": "/models/server"
|
|
},
|
|
"streaming": {
|
|
"stream_mode": "av_fmp4",
|
|
"pool": {
|
|
"num_workers": 4
|
|
},
|
|
},
|
|
}
|
|
path = tmp_path / "serve.yaml"
|
|
path.write_text(yaml.safe_dump(raw), encoding="utf-8")
|
|
|
|
loaded = load_serve_config(path)
|
|
|
|
assert loaded.streaming is not None
|
|
assert loaded.streaming.stream_mode == "av_fmp4"
|
|
assert loaded.streaming.pool.num_workers == 4
|