Files
Davids048andClaude Fable 5.1 1ea7278c00 [refactor]: group model-family settings under pipeline.model
pipeline.model is a tagged union: a mapping with exactly one key, which
names the model family (ltx2, minimax_h3, longcat, or generic) and selects
that family's options class. The former sibling sections pipeline.ltx2,
pipeline.minimax_h3, and pipeline.longcat, and the DiT/VAE architecture
override dicts pipeline.dit and pipeline.vae, live inside the family block.
The parser implements tagged unions as a general mechanism (a dataclass
with a TAGS table); the family is readable as pipeline.model.family.

Resolution validates the tag against the family that the registry chose
for model_path (validate_model_family raises and names the registry class
and the right block) and fills the family's empty block when the input has
none (fill_model_family), so readers never see None. Family-only fills and
derivations (the MiniMax-H3 parallel-VAE environment variables, the LTX-2
tile-size derivation and refine preset copy, the LongCat BSA mirrors) run
only for their family.

Against the 3f6893a0 goldens every PipelineConfig is identical and no
other field differs beyond the allowed categories; family-only settings
are no longer decided on models of another family. LingBot-Video reads
its refiner switch from pipeline.preset_overrides.refine.enabled and loads
the refiner by default when the checkpoint has one, as at 3f6893a0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CbSuTKA9oX4YsECdcBPAsX
2026-10-05 06:04:17 +00:00

424 lines
14 KiB
Python

# SPDX-License-Identifier: Apache-2.0
import json
import pytest
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,
)
from fastvideo.api.errors import ConfigValidationError
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",
"mode": "inference",
"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,
"master_port": 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,
"moba_config": None,
"nvfp4_fa4": False,
},
"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,
"boundary_ratio": None,
"output_type": "pil",
"model": 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,
"embedded_cfg_scale": 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
def test_tagged_union_parses_the_single_family_key() -> None:
from fastvideo.api import LTX2Options, LongCatOptions
config = parse_config(
GeneratorConfig, {
"model_path": "/models/ltx2",
"pipeline": {
"model": {
"ltx2": {
"refine": {
"enabled": True
},
"dit": {
"prefix": "transformer"
}
}
}
},
})
assert isinstance(config.pipeline.model, LTX2Options)
assert config.pipeline.model.family == "ltx2"
assert config.pipeline.model.refine.enabled is True
assert config.pipeline.model.dit == {"prefix": "transformer"}
assert config_to_dict(config)["pipeline"]["model"]["ltx2"]["refine"]["enabled"] is True
assert "family" not in config_to_dict(config)["pipeline"]["model"]["ltx2"]
assert parse_config(GeneratorConfig, config_to_dict(config)) == config
longcat = parse_config(GeneratorConfig, {"model_path": "/models/longcat", "pipeline": {"model": {"longcat": {}}}})
assert isinstance(longcat.pipeline.model, LongCatOptions)
assert longcat.pipeline.model.family == "longcat"
@pytest.mark.parametrize(("model", "path", "message"), [
({}, "pipeline.model", "expected exactly one key"),
({"ltx2": {}, "longcat": {}}, "pipeline.model", "expected exactly one key"),
({"wan": {}}, "pipeline.model.wan", "unknown key"),
({"ltx2": {"family": "ltx2"}}, "pipeline.model.ltx2.family", "set by the key"),
({"ltx2": {"enable_bsa": True}}, "pipeline.model.ltx2.enable_bsa", "unknown field"),
])
def test_tagged_union_rejects_malformed_blocks(model, path, message) -> None:
with pytest.raises(ConfigValidationError) as error:
parse_config(GeneratorConfig, {"model_path": "/models/x", "pipeline": {"model": model}})
assert error.value.path == path
assert message in error.value.message