23 KiB
Inference Architecture
This section documents the FastVideo inference pipeline: how models are discovered, configs resolved, components loaded, and stages composed to generate video. Training-specific code paths (FSDP, gradient checkpointing) are out of scope.
Registries
FastVideo uses three registries that work together to resolve a
user-provided model_path into a runnable pipeline.
Model Registry (fastvideo/models/registry.py)
Maps HuggingFace architecture class names (e.g. "WanTransformer3DModel")
to FastVideo model classes. Two discovery mechanisms:
- Hardcoded dicts —
_TEXT_TO_VIDEO_DIT_MODELS,_VAE_MODELS,_SCHEDULERS,_TEXT_ENCODER_MODELS,_IMAGE_ENCODER_MODELS,_UPSAMPLERS,_AUDIO_MODELS. Each entry is{hf_class_name: (component_name, module_name, class_name)}. - AST-based discovery —
_discover_and_register_models()walksfastvideo/models/and parses each.pyfile's AST looking for anEntryClassvariable assignment. Discovered models take priority over hardcoded entries. For example,fastvideo/models/wan/transformer.pyexportsEntryClass = WanTransformer3DModel.
Both feed into a unified _FAST_VIDEO_MODELS dict, which populates the
singleton ModelRegistry — an instance of _ModelRegistry. Components are
wrapped in _LazyRegisteredModel for deferred import.
Key API: ModelRegistry.resolve_model_cls(architectures) iterates
candidate architecture strings and returns the first matching
(model_cls, arch) tuple. Called by TransformerLoader, VAELoader, etc.
Config Registry (fastvideo/registry.py)
Maps model paths and names to (PipelineConfig, SamplingParam) class
pairs. Registration happens at module load via _register_configs(), which
calls register_configs() for each model family:
register_configs(
sampling_param_cls=WanT2V_1_3B_SamplingParam,
pipeline_config_cls=WanT2V480PConfig,
hf_model_paths=["Wan-AI/Wan2.1-T2V-1.3B-Diffusers"],
model_detectors=[lambda path: "wanpipeline" in path.lower()],
)
Each call populates three data structures:
_CONFIG_REGISTRY: dict[str, ConfigInfo]— auto-incrementing ID toConfigInfo(sampling_param_cls, pipeline_config_cls)._MODEL_HF_PATH_TO_NAME: dict[str, str]— HF path to registry ID._MODEL_NAME_DETECTORS: list[tuple[str, Callable]]— lambda detectors.
Resolution priority (_get_config_info()):
- Exact HF path match in
_MODEL_HF_PATH_TO_NAME. - Partial match on short model name (last path segment, case-insensitive).
- Detector-based match — runs each detector against the lowercased path
and the
_class_namefrommodel_index.json. RuntimeErrorif no match.
Top-level resolver: get_model_info(model_path, pipeline_type, workload_type) combines config resolution with pipeline resolution to
return a ModelInfo(pipeline_cls, sampling_param_cls, pipeline_config_cls).
Pipeline Registry (fastvideo/pipelines/pipeline_registry.py)
Discovers pipeline classes by scanning Python packages under
fastvideo/pipelines/{basic,preprocess,training}/.
import_pipeline_classes() iterates architecture subdirectories
(e.g. wan/, hunyuan/), imports each module, and collects those
exporting an EntryClass attribute. Supports single class or list.
Returns {pipeline_type_str: {pipeline_class_name: pipeline_cls}}.
_PipelineRegistry.resolve_pipeline_cls(pipeline_name, pipeline_type, workload_type) looks up the pipeline class by the _class_name field
from model_index.json.
Config Mechanism
Config Hierarchy
PipelineConfig (fastvideo/configs/pipelines/base.py)
├── WanT2V480PConfig (fastvideo/models/wan/pipeline_config.py)
│ ├── WanT2V720PConfig
│ └── WanI2V480PConfig
├── HunyuanConfig (fastvideo/configs/pipelines/hunyuan.py)
├── LTX2T2VConfig (fastvideo/configs/pipelines/ltx2.py)
├── CosmosConfig (fastvideo/configs/pipelines/cosmos.py)
└── ... (15+ model families)
PipelineConfig holds:
- Video generation params:
embedded_cfg_scale,flow_shift,disable_autocast,is_causal. - Nested model configs:
dit_config: DiTConfig,vae_config: VAEConfig,text_encoder_configs: tuple[EncoderConfig, ...]. - Precision settings:
dit_precision,vae_precision,text_encoder_precisions.
Model-specific subclasses override defaults. For example,
WanT2V480PConfig sets flow_shift=3.0 and uses WanVideoConfig as
its DiT config.
Wan's models/wan/definition.py links each registered variant to its pipeline
config and sampling preset. The shared registry consumes these definitions
without changing detector precedence or checkpoint/override-based pipeline
selection. configs/pipelines/wan.py remains a compatibility import.
ModelConfig / ArchConfig (fastvideo/configs/models/base.py)
ModelConfig wraps an ArchConfig using __getattr__ proxy — attribute
access falls through to arch_config transparently. ArchConfig holds
architecture fields from config.json (hidden_size, num_attention_heads,
etc.) and is immutable after initialization. update_model_arch() writes
to ArchConfig; update_model_config() writes to ModelConfig fields.
Concrete hierarchy: DiTConfig → DiTArchConfig, VAEConfig →
VAEArchConfig, EncoderConfig → EncoderArchConfig.
Config Construction
PipelineConfig.from_pretrained(model_path)— resolves config class viaget_pipeline_config_cls_from_name(), instantiates with defaults.PipelineConfig.from_kwargs(kwargs)— resolves class, optionally loads JSON viaload_from_json(), then applies CLI overrides viaupdate_config_from_dict().dump_to_json()/load_from_json()— JSON persistence. Callable fields andarch_configare excluded from dumps.
SamplingParam (fastvideo/api/sampling_param.py)
Generation parameters separate from pipeline config. Each model family
provides defaults via a profile (see fastvideo/pipelines/basic/<family>/profiles.py):
sp = SamplingParam.from_pretrained("Wan-AI/Wan2.1-T2V-1.3B-Diffusers")
# sp.height == 480, sp.width == 832, sp.num_frames == 81, etc.
Component Loading
ComponentLoader (fastvideo/models/loader/component_loader.py)
Abstract base with a load(model_path, fastvideo_args) method.
ComponentLoader.for_module_type(module_type, library) is a factory
that dispatches to specialized loaders via a module_loaders dict:
| Module type | Loader class | Library |
|---|---|---|
scheduler |
SchedulerLoader |
diffusers |
transformer, transformer_2, transformer_3 |
TransformerLoader |
diffusers |
vae |
VAELoader |
diffusers |
text_encoder, text_encoder_2, text_encoder_3 |
TextEncoderLoader |
transformers |
tokenizer, tokenizer_2, tokenizer_3 |
TokenizerLoader |
transformers |
image_encoder |
ImageEncoderLoader |
transformers |
image_processor, feature_extractor |
ImageProcessorLoader |
transformers |
audio_vae, audio_decoder |
AudioDecoderLoader |
diffusers |
vocoder |
VocoderLoader |
diffusers |
upsampler, upsampler_2 |
UpsamplerLoader |
diffusers |
TransformerLoader reads config.json from the component directory,
resolves the class via ModelRegistry.resolve_model_cls(), instantiates
the model, and loads safetensors weights. CPU offload and layerwise
offload are applied based on FastVideoArgs.
Unknown module types fall back to GenericComponentLoader.
model_index.json
Diffusers-format JSON at the model root. Keys are module names; values
are [library, class_name] tuples:
{
"_class_name": "WanPipeline",
"_diffusers_version": "0.24.0",
"transformer": ["diffusers", "WanTransformer3DModel"],
"vae": ["diffusers", "AutoencoderKLWan"],
"text_encoder": ["transformers", "UMT5EncoderModel"],
"tokenizer": ["transformers", "AutoTokenizer"],
"scheduler": ["diffusers", "FlowMatchEulerDiscreteScheduler"]
}
ComposedPipelineBase.load_modules() reads this file via
_load_config(), strips metadata keys (_class_name,
_diffusers_version, _name_or_path), detects MoE pipelines via
boundary_ratio, then loads each module listed in the pipeline's
required_config_modules. Modules not in required_config_modules are
skipped. _extra_config_module_map allows aliasing (e.g. mapping
"transformer_2" to an alternate directory name).
PipelineComponentLoader.load_module() orchestrates per-component
loading by calling ComponentLoader.for_module_type() then .load().
Stage Design
PipelineStage (fastvideo/pipelines/stages/base.py)
Abstract base class using the Template Method pattern:
__call__(batch, fastvideo_args)— orchestrates verification, timing, and error handling. Not overridden by subclasses.forward(batch, fastvideo_args) -> ForwardBatch— abstract, contains the stage logic.verify_input()/verify_output()— optional hooks returningVerificationResult. Default: no checks.
When fastvideo_args.enable_stage_verification is True, __call__
runs input verification before forward() and output verification after.
When envs.FASTVIDEO_STAGE_LOGGING is set, execution time is measured
with torch.cuda.synchronize() and logged.
ForwardBatch (fastvideo/pipelines/pipeline_batch_info.py)
Dataclass carrying all pipeline state between stages. Key field groups:
- Inputs:
prompt,negative_prompt,image_path,pil_image,video_path. - Embeddings:
prompt_embeds: list[Tensor],negative_prompt_embeds,prompt_attention_mask,image_embeds. - Latents:
latents,image_latent,noise_pred,lq_latents. - Dimensions:
height,width,num_frames,height_latents,width_latents. - Scheduler:
timesteps,num_inference_steps,guidance_scale,sigmas. - Task-specific:
mouse_cond/keyboard_cond(Matrix-Game 2.0),pose(HYWorld),c2ws_plucker_emb(LingBotWorld). - Output:
output: Tensor | None. - Logging:
logging_info: PipelineLoggingInfo.
__post_init__ enables CFG when guidance_scale > 1.0 or LTX2 text
CFG scales differ from 1.0.
Stage Catalog
Standard stages (typical execution order):
| Stage | File | Purpose |
|---|---|---|
InputValidationStage |
stages/input_validation.py |
Validates input dimensions and types |
TextEncodingStage |
stages/text_encoding.py |
Encodes prompts via text encoders |
ImageEncodingStage |
stages/image_encoding.py |
Encodes input images (I2V pipelines) |
ConditioningStage |
stages/conditioning.py |
Prepares conditioning embeddings |
TimestepPreparationStage |
stages/timestep_preparation.py |
Sets up scheduler timesteps |
LatentPreparationStage |
stages/latent_preparation.py |
Initializes noise latents |
DenoisingStage |
stages/denoising.py |
Main diffusion denoising loop |
DecodingStage |
stages/decoding.py |
Decodes latents to video via VAE |
Specialized variants: CausalDenoisingStage, LTX2DenoisingStage,
HYWorldDenoisingStage, MatrixGame2CausalDenoisingStage,
SRDenoisingStage, LTX2AudioDecodingStage, SD35ConditioningStage,
LTX2TextEncodingStage, LTX2LatentPreparationStage.
Wan owns its sampling recipes under basic/wan/stages/. WanDenoisingStage
specializes input packing, expert selection, timesteps, and first-frame
restoration around the shared dense loop. WanFirstFrameEncodingStage
produces normalized ForwardBatch.first_frame_latent before sampling; the
sampler no longer executes a VAE. Dense DMD and the two causal samplers have
family-local implementations and explicit scheduler ownership. Standard and
DMD causal sampling share cache allocation, not their sampling algorithm.
Legacy imports from stages/ remain compatibility aliases. Sampling invariants
also live beside the code in fastvideo/pipelines/basic/wan/AGENTS.md.
Verification System (fastvideo/pipelines/stages/validators.py)
StageValidators (aliased as V) provides static validators:
not_none, positive_int, is_tensor, tensor_with_dims,
positive_int_divisible(divisor), etc.
VerificationResult collects check results:
result = VerificationResult()
result.add_check("height", batch.height, V.positive_int_divisible(8))
result.add_check("width", batch.width, V.positive_int_divisible(8))
is_valid() returns whether all checks passed. get_failure_summary()
provides detailed error messages. Failed verification raises
StageVerificationError.
Pipeline Architecture
ComposedPipelineBase (fastvideo/pipelines/composed_pipeline_base.py)
Abstract base for all inference pipelines. Lifecycle:
__init__(model_path, fastvideo_args)— initializes distributed environment viamaybe_init_distributed_environment_and_model_parallel (tp_size, sp_size), then callsload_modules()to populateself.modules.post_init()— callsinitialize_pipeline()(model-specific setup),create_pipeline_stages()(abstract — subclasses wire stages), optionally appliestorch.compileto transformers, and callswarmup_sequence_parallel_communication().forward(batch, fastvideo_args)— iteratesself.stagescalling each stage in order. Decorated with@torch.no_grad().
Key class attributes:
_required_config_modules: list[str]— module names to load frommodel_index.json._extra_config_module_map: dict[str, str]— aliases for module dirs.is_video_pipeline: bool— whether this produces video output.
Key methods:
add_stage(name, stage)— appends to_stageslist and_stage_name_mappingdict, also sets attribute onself.get_module(name, default)— retrieves a loaded module.from_pretrained(model_path, **kwargs)— class method constructingFastVideoArgsand callingcls(...)thenpost_init().
LoRAPipeline (fastvideo/pipelines/lora_pipeline.py)
Extends ComposedPipelineBase with LoRA adapter support. Sits in the
MRO between the concrete pipeline and ComposedPipelineBase:
class WanPipeline(LoRAPipeline, ComposedPipelineBase):
...
Key functionality:
convert_to_lora_layers()— scans transformer blocks, replaces target linear layers (default: q/k/v/o projections) with LoRA equivalents viaget_lora_layer().set_lora_adapter(path)— loads safetensors containinglora_A,lora_B,lora_alphaand maps weights to internal layers.merge_lora_weights()/unmerge_lora_weights()— activates or deactivates LoRA in the forward pass.LoRAModelLayers— groups LoRA layers by transformer block for efficient layerwise offload.
Distributed Inference
maybe_init_distributed_environment_and_model_parallel(tp_size, sp_size)
in fastvideo/distributed/ initializes torch.distributed and creates
tensor-parallel (TP) and sequence-parallel (SP) process groups.
Key APIs: get_tp_rank(), get_tp_world_size(), get_sp_rank(),
get_sp_world_size(), get_world_rank(), get_world_size().
warmup_sequence_parallel_communication() pre-warms NCCL communicators
to avoid slow first forward passes.
The default mp executor starts all workers locally. For synchronized offline
SPMD generation (including multi-node inference), select the
external_launcher executor in the typed run config and make
generator.engine.num_gpus equal the launcher's total world size. The
MiniMax-H3 external-launcher example
contains a complete request:
torchrun --standalone --nproc-per-node=4 \
-m fastvideo.entrypoints.cli.main generate \
--config examples/inference/basic/minimax_h3_external_launcher.yaml
For two nodes with four GPUs each, launch the same command on both nodes and
set NODE_RANK to 0 or 1:
torchrun --nnodes=2 --nproc-per-node=4 \
--node-rank="$NODE_RANK" \
--master-addr="$MASTER_ADDR" --master-port="$MASTER_PORT" \
-m fastvideo.entrypoints.cli.main generate \
--config examples/inference/basic/minimax_h3_external_launcher.yaml \
--generator.engine.num_gpus 8 \
--generator.engine.parallelism.sp_size 8
Native Slurm variables are also accepted: SLURM_PROCID, SLURM_NTASKS,
and SLURM_LOCALID map to global rank, world size, and local rank. Slurm does
not choose the env rendezvous endpoint, so export a reachable rank-zero host
and a job-unique port. Preserve a scheduler/CI-provided MASTER_PORT when one
already exists. This uniform 2 x 4 example exposes all four allocated GPUs
to every task so SLURM_LOCALID is a valid device index:
export MASTER_ADDR="${MASTER_ADDR:-$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n 1)}"
export MASTER_PORT="${MASTER_PORT:-$((20000 + SLURM_JOB_ID % 20000))}"
srun --nodes=2 --ntasks-per-node=4 --gpus-per-node=4 --gpu-bind=none \
--kill-on-bad-exit=1 \
python -m fastvideo.entrypoints.cli.main generate \
--config examples/inference/basic/minimax_h3_external_launcher.yaml \
--generator.engine.num_gpus 8 \
--generator.engine.parallelism.sp_size 8
Every rank must enter the same requests in the same order. The CLI config and model/media inputs therefore need to resolve identically on every node. Rank zero alone reads prompt-list files, creates output directories, saves media, and returns generated frames/audio/latents/trajectories; only that rank needs a writable output path. For MiniMax-H3 parallel VAE decode, every rank in the SP group containing global rank zero participates in the decoder collectives, but only rank zero assembles the CPU output. All ranks in every other SP group skip the decode uniformly, so multiple SP groups do not materialize duplicate outputs.
Set generator.engine.parallelism.dist_timeout to an optional positive
integer number of seconds to apply one timeout to the default process
group (when FastVideo initializes it) and every FastVideo-created device and
Gloo control group. Omitting the key preserves PyTorch's backend defaults. A
local-rank error, an out-of-range rendezvous port, or a device index outside
the process-visible accelerator set fails before model loading. The process-
group timeout bounds a missing collective participant;
--kill-on-bad-exit=1 (or torchrun's worker-group supervision) remains
necessary to terminate the rest of the worker group after a rank failure.
FASTVIDEO_EXTERNAL_LAUNCHER=1 is an equivalent offline-only opt-in for
callers that leave execution_backend: mp. External-launcher mode is rejected
by REST and streaming servers because those request loops do not provide a
rank-zero SPMD control plane.
torch.compile Integration
When fastvideo_args.enable_torch_compile is True,
_maybe_compile_pipeline_module() checks for a _compile_conditions
attribute on the module. If present, only matching submodules are
compiled. Otherwise, the entire module is compiled. FSDP-wrapped
modules are skipped.
Entry Points
Python API (fastvideo/entrypoints/video_generator.py):
generator = VideoGenerator.from_pretrained(
model_path="Wan-AI/Wan2.1-T2V-14B-Diffusers",
num_gpus=1, tp_size=1, sp_size=1,
)
result = generator.generate_video(
prompt="A cat dancing",
height=720, width=1280, num_frames=81,
)
CLI (fastvideo/entrypoints/cli/):
fastvideo generate \
--model-path "Wan-AI/Wan2.1-T2V-14B-Diffusers" \
--prompt "A cat dancing" \
--num-gpus 1
FastVideoArgs (fastvideo/fastvideo_args.py): Central args dataclass.
Key fields: model_path, mode (ExecutionMode), workload_type
(WorkloadType), pipeline_config (PipelineConfig), num_gpus,
tp_size, sp_size, lora_path, dit_cpu_offload,
dit_layerwise_offload, enable_torch_compile,
enable_stage_verification.
Constructed via FastVideoArgs.from_kwargs(**kwargs) which resolves the
PipelineConfig from the registry, applies JSON config if provided, and
merges CLI overrides.
End-to-End Inference Flow
User: VideoGenerator.from_pretrained(model_path, **kwargs)
│
├─ FastVideoArgs.from_kwargs() → PipelineConfig resolved via registry
├─ get_model_info() → ModelInfo(pipeline_cls, sampling_param_cls, ...)
│ ├─ model_index.json read → _class_name extracted
│ ├─ pipeline_registry resolves pipeline_cls from _class_name
│ └─ config_registry resolves config classes from model_path
│
├─ pipeline_cls.__init__(model_path, fastvideo_args)
│ ├─ maybe_init_distributed(tp_size, sp_size)
│ └─ load_modules() → reads model_index.json, loads each component
│ ├─ ComponentLoader.for_module_type() → specialized loader
│ └─ loader.load() → model class resolved, weights loaded
│
└─ pipeline.post_init()
├─ initialize_pipeline() → model-specific setup
├─ create_pipeline_stages() → stages wired with modules
├─ torch.compile (if enabled)
└─ warmup_sequence_parallel_communication()
User: generator.generate_video(prompt, ...)
│
├─ ForwardBatch constructed from SamplingParam + user args
└─ pipeline.forward(batch, fastvideo_args)
├─ InputValidationStage → validates dims
├─ TextEncodingStage → prompt → embeddings
├─ ConditioningStage → prepares conditioning
├─ TimestepPreparationStage → scheduler timesteps
├─ LatentPreparationStage → random noise
├─ DenoisingStage → iterative denoising loop
└─ DecodingStage → latents → video frames
Adding a New Model Family — Checklist
-
Pipeline config — Create a
PipelineConfigsubclass infastvideo/configs/pipelines/<model>.py. Set DiT/VAE/encoder configs, flow_shift, precision defaults. -
Sampling param profile — Create a profile in
fastvideo/pipelines/basic/<model>/profiles.pywith default height, width, num_frames, guidance_scale, num_inference_steps. -
Register configs — In
fastvideo/registry.py, add aregister_configs()call inside_register_configs()withhf_model_pathsand/ormodel_detectors. -
Pipeline class — Create a subclass of
ComposedPipelineBase(orLoRAPipeline+ComposedPipelineBase) infastvideo/pipelines/basic/<model>/<model>_pipeline.py.- Set
_required_config_moduleslisting needed components. - Implement
create_pipeline_stages()wiring stages viaadd_stage(). - Optionally override
initialize_pipeline()for custom setup. - Export
EntryClass = YourPipelineat module level.
- Set
-
Model classes (if custom) — Add DiT/VAE implementations in
fastvideo/models/dits/orfastvideo/models/vaes/withEntryClass = YourModel. The AST discovery will register them automatically. -
Custom stages (if needed) — Subclass
PipelineStageinfastvideo/pipelines/stages/, implementforward(), optionally implementverify_input()/verify_output(). -
Verify — Run
fastvideo generate --config <config.yaml>with a minimal nested config to confirm the pipeline loads and generates output.