Compare commits

...
46 Commits
Author SHA1 Message Date
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
Davids048andClaude Fable 5.1 a5aa64ab6b [refactor]: decide every config value before the config freezes, and drop the from_pretrained keywords
VideoGenerator.from_pretrained(model_path, config) takes the same nested
settings as from_config; the 24 flat keywords, from_pretrained_kwargs_to_config,
FROM_PRETRAINED_KWARGS, and every flat_field in the schema are deleted.
nvfp4_fa4 is the typed field engine.attention.nvfp4_fa4, applied by a
resolution step. 65 call sites and the docs use the nested form; the three
kwargs golden cases are config cases with byte-identical results.

Every value is decided during resolution; no runtime code calls
with_override. The device offload policy (unified memory, MPS, layerwise
conflicts, lazy module load) runs as resolution steps in the main process
(fastvideo/api/device_policy.py), and the worker runs on the config it
receives. The LTX-2 refine defaults from model_index.json and the MiniMax-H3
schedule from fastvideo_inference.json are resolution steps
(fastvideo/api/checkpoint_defaults.py) that read local checkpoints or download
only those files; the H3 pipeline validates the schedule against the loaded
schedulers but no longer writes it. The preprocessing entry point passes the
downloaded local path as run state instead of a config override. Teacher and
critic models load with override_transformer_cls_name as a load argument.
The test isolation turns the device policy off, as it blocks downloads, so
that the goldens do not depend on the machine.

Against the 3f6893a0 goldens, every field still matches except boundary_ratio
and ltx2_vae_tiling (DESIGN section 9) and the fields that no longer exist.
The launcher and trainer YAML verifications report 0 unexplained differences.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 07:14:31 +00:00
Davids048andClaude Fable 5.1 0bcd15d5e2 [refactor]: delete FastVideoArgs, TrainingArgs, the flattening, and argparse
ResolvedGeneratorConfig is the only runtime config. It answers only its
typed fields, pipeline_config, mode, training_mode, and inference_mode;
the transitional flat-name fallback is gone.

Deleted: fastvideo/fastvideo_args.py (FastVideoArgs, TrainingArgs, their
argparse, prepare/set/get_current_fastvideo_args), the flat-name fallback,
generator_config_to_fastvideo_args and the other flattening helpers,
PipelineConfig/model-config/PreprocessConfig/SamplingParam add_cli_args,
configs/utils.py, and the tests of the deleted code.

Materialization builds the model's PipelineConfig directly from the typed
values through an explicit table of typed path to PipelineConfig mirror
attribute (PIPELINE_CONFIG_MIRRORS); PipelineConfig.from_source replaces
from_kwargs. flat_field metadata stays only on the fields behind the 24
from_pretrained keywords; a pipeline.experimental key that names a mirror
attribute, and pipeline.preset / preset_version / components.vae_weights,
are rejected during resolution. The training root keeps the DiT on the
device through a resolution step instead of a from_pretrained override.

The goldens snapshot the end-state objects (resolved config, PipelineConfig,
decisions, request); the cli category is gone. Against the 3f6893a0
goldens, every field matches except disable_autocast, boundary_ratio,
ltx2_vae_tiling (DESIGN section 9), and the fields that no longer exist.
The schema parity inventory classifies the typed GeneratorConfig paths.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-04 04:45:02 +00:00
Davids048andClaude Opus 5.5 0b0c692012 [refactor]: read typed config paths in the loaders
- The component loaders read engine.offload.*, engine.precision.*,
  engine.parallelism.hsdp_*, engine.use_fsdp_inference, engine.compile.*,
  engine.attention.*, pipeline.components.*, and pipeline.flow_shift (the
  scheduler shift) from the resolved config, and compile settings through
  torch_compile_kwargs(). They no longer write model_paths (the pipeline's
  ComponentState records paths) or apply the unified-memory policy to the
  config; the text-encoder loader only queries the policy for its target
  device.
- PipelineComponentLoader.load_module and TransformerLoader.load take
  loading_teacher_critic_model instead of reading an attribute that
  training code set on the config.
- Loader, encoder, transformer, VAE, and device-policy tests build resolved
  configs; the env-var docs name typed paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:46:52 +00:00
Davids048andClaude Opus 5.5 6c2a250fd0 [refactor]: read typed config paths in the pipeline base, worker, and entry points
- ComposedPipelineBase owns a ComponentState: it records each component's
  path after loading and attaches the state to every stage it registers.
  from_pretrained(model_path, *, resolved_config, ...) takes a resolved
  config instead of building FastVideoArgs from keywords or argparse; the
  training_args alias and TrainingArgs checks are gone, and the training
  DiT offload is a recorded override. Compile settings come from
  engine.compile and torch_compile_kwargs(); LoRA settings from
  pipeline.components.lora_* and training.lora.*.
- The worker and executors read engine.num_gpus, engine.parallelism.*,
  engine.execution_backend, and pipeline.output_type; the Ray executor owns
  its placement group and runtime env.
- VideoGenerator builds from a resolved config only (_from_resolved_config);
  the prompt_txt fallback is gone (requests carry inputs.prompt_path). The
  OpenAI server state holds the resolved config (get_resolved_config()).
- Tests use resolved configs; the SSIM and performance helpers map their
  settings to dotted typed paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:35:19 +00:00
Davids048andClaude Opus 5.5 0607bd78c7 [refactor]: run the legacy training stack on a TrainingRunConfig
- The 12 fastvideo/training/*_pipeline.py entry points load a
  TrainingRunConfig from --config <yaml> plus dotted overrides (mode
  finetuning or distillation) instead of TrainingArgs argparse flags.
- The training pipelines read self.resolved_config.training.<section>.<field>
  and typed paths; the training_args alias, the inference_mode toggles,
  and the writes to the training config are gone. Teacher and critic
  models load with a with_override config and the loading_teacher_critic_model
  argument; validation pipelines are built from their own resolved config
  (resolve_validation_config), and the tracker receives to_dict().
- Each of the 37 training launchers has a YAML file next to it with the
  values its flags held; shell variables stay as dotted overrides. Against
  the 3f6893a0 TrainingArgs snapshots every value and PipelineConfig
  matches, except mode/inference_mode (the stage readers saw training mode
  before and after), the dropped flags without a reader, lora_alpha (now
  derived from the rank by a step), and the vae_tiling and compile-kwargs
  defaults.
- Validation stages read the validation pipeline's config: modules that
  validation loads itself use the inference offload defaults, and the
  bf16-autocast VAE decode of two distillation launchers becomes fp32.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:28:44 +00:00
Davids048andClaude Opus 5.5 c4bb13a650 [refactor]: decide the v1 preprocessing VAE precision during resolution
The fp32 VAE that every v1 preprocessing task except text_only needs is a
resolution step (derive_video_preprocess_vae_precision) instead of an
override that the entry point applied after materialization, so the
PipelineConfig carries it too. Sections of a resolved config pickle on
their own, so a DataLoader worker can receive one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:25:18 +00:00
Davids048andClaude Opus 5.5 d051603677 [refactor]: run preprocessing on a PreprocessRunConfig
- v1_preprocess.py and v1_preprocessing_new.py load a PreprocessRunConfig
  from --config <yaml> plus dotted overrides; their argparse flags are
  gone. The preprocessing pipelines, workflows, datasets, and stages read
  resolved_config.preprocess.* and typed paths instead of
  preprocess_config and an argparse namespace.
- The overfit preprocessors build resolved configs instead of FastVideoArgs.
- Each preprocessing launcher has a YAML file next to it with the values
  its flags held; shell variables stay as dotted overrides. Against the
  3f6893a0 snapshots of the 17 launchers, every value and PipelineConfig
  matches, except mode (preprocess instead of inference), ltx2_vae_tiling,
  and text_only's vae_config.load_encoder. Five launchers that the old
  parser rejected now carry their intended values; the never-defined
  --model_type and --validation_dataset_file flags are dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:24:03 +00:00
Davids048andClaude Opus 5.5 00a8e5bcb6 [refactor]: build resolved configs in the modular trainer
- moduleloader builds the component-loading config and the inference
  config as resolved configs (_build_training_resolved_config and
  build_inference_resolved_config) instead of TrainingArgs objects mutated
  after construction; the transformer override, weights path, and
  teacher/critic flag are config values or load_module arguments.
  keep_checkpoint_component_config keeps the trainer's PipelineConfig view
  of the checkpoint-filled VAE and text-encoder configs.
- The validation callback builds its pipeline with
  from_pretrained(path, resolved_config=...) and forwards with a resolved
  config built per run; it no longer replaces or mutates the trainer's
  pipeline_config, and generator_overrides takes nested GeneratorConfig
  fields instead of flat names.
- Against the 3f6893a0 snapshots of the 67 trainer YAML files, every
  PipelineConfig and value matches except ltx2_vae_tiling (the model's
  vae_tiling) and the unread pretrained_model_name_or_path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:19:13 +00:00
Davids048andClaude Opus 5.5 4117696a4f [refactor]: describe and use the resolved config in docs, examples, and local tests
- Docs, AGENTS.md files, and skills describe ResolvedGeneratorConfig,
  resolve_inference_config, typed YAML, and --config plus dotted
  overrides for training and preprocessing instead of FastVideoArgs and
  flat flags.
- The local tests build resolved configs (or small fakes that expose typed
  paths) instead of FastVideoArgs; the LoRA extraction scripts build
  pipelines with from_pretrained(model_path, resolved_config=...).
- The mixkit README's NVFP4 snippet uses from_config with
  engine.quantization.transformer_quant (the from_pretrained keyword it
  used is rejected).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:16:10 +00:00
Davids048andClaude Opus 5.5 23cb8c0c2b [refactor]: read typed config paths in the shared stages
- PipelineStage gains component_state; the denoising, SR denoising, and
  decoding stages reload released components through it instead of the
  model_paths and model_loaded fields on the config.
- The shared stages read engine.disable_autocast, engine.offload.*,
  engine.precision.*, engine.attention.{vsa_sparsity, moba_config},
  pipeline.{vae_tiling, flow_shift, output_type}, and
  engine.enable_stage_verification; embedded_cfg_scale_for_batch falls
  back to pipeline.embedded_cfg_scale.
- The stage tests build resolved configs (make_resolved_config) instead of
  FastVideoArgs fakes; the sequential-load CLI test uses dotted overrides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:12:37 +00:00
Davids048andClaude Opus 5.5 ca7965ed28 [refactor]: read typed config paths in the remaining model pipelines
The Cosmos, DreamX-World, Flux, GLM-Image, HunyuanVideo, HyWorld,
Kandinsky5, LingBot, LongCat, Matrix-Game, SD3.5, TurboDiffusion, and
Z-Image pipelines read flow_shift, precisions, vae_tiling,
dmd_denoising_steps, output_type, offload, autocast, and the LongCat BSA
settings from typed paths. LingBot's refine switch reads
pipeline.ltx2.refine.enabled, so the typed field also turns its refiner off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:09:11 +00:00
Davids048andClaude Opus 5.5 61dffbe904 [refactor]: read typed config paths in the LTX-2, MiniMax-H3, Wan, MMAudio, MagiHuman, and Stable Audio pipelines
- LTX-2: the refine settings are read from pipeline.ltx2.refine.* and
  pipeline.components.upsampler_weights. The checkpoint's model_index.json
  defaults fill the fields that are still None, and the remaining switches
  get their defaults, in one recorded override that rebinds the pipeline's
  config. An explicit input value now always wins over the checkpoint
  default; the flat sentinels (the generic refine_* names and the class
  default of the step count) are gone.
- MiniMax-H3: the sequential load, decode backend, TAEH3, parallel VAE,
  offload, and checkpoint schedule settings are read from typed paths.
- Wan, MMAudio, MagiHuman, Stable Audio: flow_shift, dmd_denoising_steps,
  output_type, workload_type, offload, and autocast settings are read from
  typed paths. MagiHuman no longer reads the never-set log_level_progress
  name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:06:25 +00:00
Davids048andClaude Opus 5.5 7ec6251133 [refactor]: read typed config paths in the model-specific stages
The Flux, GameCraft, Gen3C, HyWorld, Kandinsky5, LongCat, Matrix-Game,
and SD3.5 stages read engine.precision.*, engine.disable_autocast,
engine.offload.vae, pipeline.vae_tiling, pipeline.dmd_denoising_steps,
pipeline.flow_shift, and engine.attention.vsa_sparsity from the resolved
config, and component residency from the pipeline's ComponentState.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 06:01:11 +00:00
Davids048andClaude Opus 5.5 b323cb787c [refactor]: add the shared runtime helpers for reader migration
- ComponentState (fastvideo/pipelines/component_state.py) holds each
  pipeline's component paths and residency, which runtime code kept on
  FastVideoArgs as model_paths and model_loaded.
- torch_compile_kwargs(resolved_config) derives the torch.compile keyword
  arguments from engine.compile.
- thaw(value) returns a mutable copy of a value read from a resolved
  config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 05:48:05 +00:00
Davids048andClaude Opus 5.5 e263249175 [refactor]: name the runtime config parameter resolved_config
Every parameter, attribute, and keyword argument that carries the runtime
config is named resolved_config instead of fastvideo_args, including the
RPC kwargs keys between the executors and the workers. The module path
fastvideo.fastvideo_args is unchanged. The rename is mechanical (a
token-level rewrite) plus yapf line wrapping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 05:45:37 +00:00
Davids048andClaude Opus 5.5 6c776a219b [refactor]: run the inference path on the resolved config
ResolvedGeneratorConfig becomes the runtime config of every mode. Resolution
builds the model's PipelineConfig once, freezes it, and attaches it as
resolved_config.pipeline_config; it also exposes mode, training_mode, and
inference_mode. VideoGenerator, fastvideo serve, the OpenAI server state,
the executors, the worker, and ComposedPipelineBase receive the resolved
config instead of a FastVideoArgs, and generator_config_to_fastvideo_args
is no longer on the inference path.

- Schema: ExecutionMode and WorkloadType move to fastvideo/api/schema.py;
  typed homes for mode, output_type, boundary_ratio, master_port,
  moba_config, and the LTX-2 refine settings; Enum parsing.
- Resolution: steps for the refine preset overrides, flat experimental
  keys, the V-MoBA config, the validation that check_fastvideo_args did,
  the num_gpus bump, the model's vae_tiling default, and a final
  fill_runtime_defaults step for typed fields no earlier step decides.
- fastvideo/api/training_schema.py: TrainingRunConfig and
  PreprocessRunConfig roots, their resolvers, and a --config plus dotted
  override loader.
- fastvideo/api/device_policy.py: the device offload policy as functions
  that return an overridden config; the worker keeps the result. The LTX-2
  and MiniMax-H3 checkpoint defaults rebind the pipeline's config.
- fastvideo/api/flat_name_fallback.py answers the flat FastVideoArgs names
  on the resolved config until the readers move to typed paths.

Golden changes: the decisions of the steps above are recorded, and
FastVideoArgs.ltx2_vae_tiling holds the model's vae_tiling (it held None;
no runtime code reads it). Against the 3f6893a0 goldens every
FastVideoArgs field, PipelineConfig, and SamplingParam matches, except
disable_autocast and boundary_ratio, which FastVideoArgs held at their
defaults because flattening routed them only to PipelineConfig.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 05:35:14 +00:00
Davids048andClaude Opus 5.5 3f6893a098 [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
2026-10-03 02:23:41 +00:00
Davids048andClaude Opus 5.5 d728a0dc5f [refactor]: remove VideoGenerator.generate_video
VideoGenerator.generate(request) is the one inference entry point. The
generate_video keyword call, its mapping from flat keywords to a request
(legacy_generate_call_to_request and its helpers), and the tests of that
mapping are removed. Comments and docs that named generate_video describe
generate and request extensions instead.

scripts/ltx2_sr_alignment.py keeps its generate_video call, because its
reference run imports the separate FastVideo-internal package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 02:17:49 +00:00
Davids048andClaude Opus 5.5 d6ba5ced94 [refactor]: move apps and docs to generate(request) and from_config
DreamVerse, FastVideo Studio, the ComfyUI node, the README, and the
inference docs call VideoGenerator.generate with nested requests and
VideoGenerator.from_config with typed configs, and read GenerationResult
attributes instead of result dict keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 02:15:04 +00:00
Davids048andClaude Opus 5.5 9139c0411b [refactor]: move tests and scripts to generate(request) and from_config
The SSIM, LoRA, performance, nightly, and local tests, two scripts, and the
pipeline parity skill template call VideoGenerator.generate with nested
requests and VideoGenerator.from_config with typed configs. The SSIM and
performance helpers convert their flat keyword tables into a typed config
and a request after the overrides merge. Text encoder precisions are passed
as lists, which the typed config parser requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 02:14:42 +00:00
Davids048andClaude Opus 5.5 f571621ae7 [refactor]: move examples to generate(request) and from_config
Every example calls VideoGenerator.generate with a nested request instead
of generate_video keywords, and passes settings outside the from_pretrained
convenience keywords through VideoGenerator.from_config at their typed
paths. Each request and generator config equals what the keyword call built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 02:14:36 +00:00
Davids048andClaude Opus 5.5 96b7c0d223 [refactor]: build OpenAI image requests as typed requests
The image routes call VideoGenerator.generate with a GenerationRequest
instead of passing flat generate_video keywords. The flat projection
_build_generation_kwargs in video_api.py had no runtime caller, so it is
removed, and its tests exercise build_generation_request instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 02:03:51 +00:00
Davids048andClaude Opus 5.5 74a52c027b [refactor]: remove the flat-flag entry point of the OpenAI API server
python -m fastvideo.entrypoints.openai.api_server parsed the hand-written
FastVideoArgs argparse flags, an inference input path next to the typed config
that fastvideo serve reads. It is removed; start the server with
fastvideo serve --config SERVE_CONFIG and dotted overrides such as
--server.port 8000. The GPU integration test starts the server that way, with
the same model, GPU count, and DiT offload.

FastVideoArgs.add_cli_args stays for the legacy training and preprocessing
scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 01:54:34 +00:00
Davids048andClaude Opus 5.5 2cd2018137 [refactor]: build OpenAI requests as nested typed requests
build_generation_request flattened the serve config's default_request with
explicit_request_updates, merged the request body into the flat dict, and then
rebuilt a nested GenerationRequest through legacy_generate_call_to_request,
the generate_video keyword mapping. It now builds the nested request directly:

- default_request contributes its explicit fields with their sections, through
  the new compat.explicit_request_raw;
- each value collected from the OpenAI request goes to a fixed typed path from
  _REQUEST_PATHS, and any other collected value goes to extensions.

The resulting SamplingParam is unchanged. A request-body field still wins over
the same-named field in default_request.stage_overrides, because
request_to_sampling_param applies stage overrides to sampling fields of the same
name. The only difference in the typed request: an operator stage override
that the body does not set now stays under stage_overrides instead of moving
into sampling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 01:52:27 +00:00
Davids048andClaude Opus 5.5 6627366805 [docs]: show how to look up where a config value came from
docs/inference/configuration.md describes resolved_config.provenance(path)
on a VideoGenerator and resolved_request.provenance(path) on a
GenerationResult, with examples checked against the Wan2.1 T2V 1.3B preset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 20:27:14 +00:00
Davids048andClaude Opus 5.5 7aa1f78e10 [refactor]: carry the resolved config through VideoGenerator and deprecate raw FastVideoArgs
VideoGenerator.__init__ takes resolved_config from the FastVideoArgs it
receives, so a generator that fastvideo serve builds through
from_fastvideo_args exposes the resolved config too, not only one built
through from_config.

from_fastvideo_args(...) with a FastVideoArgs that was not built from a typed
config now emits a DeprecationWarning that points to from_config. This is the
only public path that hands VideoGenerator a hand-built FastVideoArgs, and it
has to go before FastVideoArgs can be removed. The deprecated
fastvideo.entrypoints.openai.api_server flag entry point reaches it, as
before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 20:25:33 +00:00
Davids048andClaude Opus 5.5 38096b94a3 [refactor]: resolve request settings against the model's preset defaults
resolve_generation_request in fastvideo/api/resolution.py runs resolution
steps over the settings sections of a GenerationRequest (sampling, runtime,
output, stage_overrides) and freezes them as a ResolvedRequest. The explicit
paths are the ones the request tracked while it was parsed or built. Prompts,
inputs, state, plan, and extensions are request data and are left out, so
large tensors are not copied.

fastvideo/api/request_resolution.py adds resolve_request(request, model_path).
Its step, fill_sampling_defaults[preset <name>], sets each setting that the
request did not write to the model's preset default
(SamplingParam.from_pretrained). This is the same source that
request_to_sampling_param starts from, and a test checks that the two agree
field by field.

VideoGenerator attaches the ResolvedRequest to each GenerationResult as
resolved_request, so the source of every setting can be looked up. The golden
request snapshots record request_resolution_decisions; nothing else changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 20:23:05 +00:00
Davids048andClaude Opus 5.5 4d05e9c020 [bugfix]: apply per-request embedded_cfg_scale in the worker
A request's embedded_cfg_scale (request extension or legacy generate_video
keyword) was applied to a deep copy of FastVideoArgs.pipeline_config in
VideoGenerator. The worker runs the pipeline with its own FastVideoArgs
(GpuWorker.execute_forward passes self.fastvideo_args), so the copy never
reached the denoising stages and the request value was ignored.

embedded_cfg_scale is now a per-request sampling value:

- the request field request.sampling.embedded_cfg_scale (the extension key
  still works);
- SamplingParam.embedded_cfg_scale and ForwardBatch.embedded_cfg_scale;
- embedded_cfg_scale_for_batch(batch, fastvideo_args), which the denoising,
  SR denoising, Wan DMD, and Flux2 text-encoding stages read. It returns the
  batch value, else pipeline_config.embedded_cfg_scale, so training and
  requests without the field are unchanged.

VideoGenerator no longer copies or changes FastVideoArgs per request, and
request_to_pipeline_overrides is removed. The golden snapshots record the new
SamplingParam field, and the request with extensions.embedded_cfg_scale now
carries 3.0 on SamplingParam instead of on a pipeline override.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 20:17:03 +00:00
Davids048andClaude Opus 5.5 62780cf53b [refactor]: freeze FastVideoArgs built from a resolved config
generator_config_to_fastvideo_args passes the ResolvedGeneratorConfig to
FastVideoArgs through the resolved_config init-only argument. Such an
object keeps the config as resolved_config, skips the environment folds that
resolution has already decided, and becomes read-only after construction.
Its PipelineConfig becomes read-only too:

- Assigning a configuration field raises AttributeError and names
  FastVideoArgs.override.
- Runtime state stays writable: model_paths, model_loaded, and the Ray
  placement handles. So do nested component configs, which loaders fill
  with checkpoint data.
- A FastVideoArgs built directly, as the training and argparse entry points
  do, is unchanged and stays writable.

FastVideoArgs.override(source, values) is the one way to change fields after
construction. It records (source, values) in override_log and records the
change on resolved_config for fields that have a typed path. The writers that
changed configuration in place now go through it:

- device_policy:* for the unified-memory, lazy-load, MPS, and layerwise
  offload decisions that each worker makes after binding its device;
- checkpoint:model_index.json for the LTX-2 refine defaults;
- checkpoint:fastvideo_inference.json for the FastH3 DMD schedule;
- request for the per-request embedded_cfg_scale copy.

Behavior change: an explicit false for engine.compile.regional or
pipeline.minimax_h3.vae_parallel_decode/encode now wins over
FASTVIDEO_INFERENCE_TORCH_COMPILE and FASTVIDEO_VAE_PARALLEL_*. The resolution
steps fill these fields only while they are unset, and FastVideoArgs no
longer folds the environment a second time.

A test run that froze every FastVideoArgs logged no other non-test writer in
the stages, pipelines, loader, worker, hooks, entrypoints, and platforms
suites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 20:08:29 +00:00
Davids048andClaude Opus 5.5 40ff9af3fb [refactor]: derive VAE tiling from LTX-2 tile sizes during resolution
FastVideoArgs._apply_ltx2_vae_overrides turns on PipelineConfig.vae_tiling
when an LTX-2 VAE tile size is set and ltx2_vae_tiling is unset. The
resolution step derive_vae_tiling_from_ltx2_tile_sizes makes the same
decision on pipeline.vae_tiling, so the resolved config holds it with its
source.

PipelineConfig is unchanged. In the golden case kwargs/ltx2_vae_tile_sizes,
the carrier field FastVideoArgs.ltx2_vae_tiling is now True instead of
None, because the decision now reaches FastVideoArgs as input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 19:24:08 +00:00
Davids048andClaude Opus 5.5 93d921fe33 [misc]: add a golden case for the LTX-2 VAE tile-size keywords
kwargs/ltx2_vae_tile_sizes records that setting an LTX-2 VAE tile size turns
on VAE tiling and reaches the VAE config, before that rule moves into
resolution.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 19:22:23 +00:00
Davids048andClaude Opus 5.5 d26aa6b2d7 [refactor]: fill unset typed fields from the model's PipelineConfig during resolution
The resolution step fill_pipeline_config_defaults[<PipelineConfig class>]
fills each typed field that is still None with the value that the model's
PipelineConfig holds for the field's flat name. That PipelineConfig is the
one flattening starts from: the registry class for model_path, plus a
pipeline config file or object when one is given. The covered fields are the
engine.precision fields, pipeline.vae_sp, flow_shift, embedded_cfg_scale,
dmd_denoising_steps, and the pipeline.longcat BSA settings.

The resolved config therefore holds the effective value of these fields,
and provenance tells a model default apart from user input. The step runs
after the environment steps and before the derived values, so the precedence
is user input, then environment variables, then model defaults.
inference_resolution_steps(config) builds the ordered step list.

The values that the step fills are the values that PipelineConfig already
had, so FastVideoArgs and PipelineConfig are unchanged; the golden snapshots
add the decisions. Tests that flatten unregistered model paths skip this
step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 19:21:25 +00:00
Davids048andClaude Opus 5.5 b9c397dd1a [bugfix]: reject unsupported FASTVIDEO_ATTENTION_BACKEND names
An unsupported FASTVIDEO_ATTENTION_BACKEND name, such as a typo, was ignored
and the run fell back to automatic backend selection without telling the
user. An explicit attention_backend argument with the same name raised.

get_env_variable_attn_backend now raises ValueError for an unsupported name,
using the same parse as an explicit request (coerce_attn_backend). Every
reader goes through it: the fill_attention_backend_from_env resolution
step, FastVideoArgs.__post_init__, and the attention selector's
environment fallbacks.

The golden case environment/attention_backend_unknown_name now records the
error, and the registry description and env_vars.md table say that an
unsupported name raises.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 19:09:50 +00:00
Davids048andClaude Opus 5.5 867f960bf0 [bugfix]: commit the environment-variable golden snapshots
The golden snapshots of the environment-variable cases lived under
config_snapshot_goldens/env/, which the repository .gitignore entry "env"
ignores, so they were never committed and the five cases would fail in CI
with a missing golden file. The case category is renamed environment, and its
golden files, generated from the previous commit, are added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 19:04:40 +00:00
Davids048andClaude Opus 5.5 1d7e7e2e35 [refactor]: fold inference env vars and parallel placeholders during resolution
The first resolution steps take over startup rewrites that
FastVideoArgs.__post_init__ and check_fastvideo_args make in place:

- fill_attention_backend_from_env: FASTVIDEO_ATTENTION_BACKEND fills
  engine.attention.backend while it is unset.
- fill_regional_compile_from_env: FASTVIDEO_INFERENCE_TORCH_COMPILE turns
  engine.compile.regional on.
- fill_vae_parallel_from_env: the FASTVIDEO_VAE_PARALLEL_* variables set
  pipeline.minimax_h3, and the decode strategy defaults to gather.
- derive_parallel_sizes: the -1 placeholders of tp_size, sp_size, and
  hsdp_shard_dim become 1, num_gpus, and num_gpus.

Each step repeats the current FastVideoArgs rule exactly, so FastVideoArgs is
unchanged: the golden snapshots differ only in their resolution decisions.
The FastVideoArgs code stays for FastVideoArgs built directly by the
training and argparse entry points; for a resolved config it finds the
values already decided.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 19:01:09 +00:00
Davids048andClaude Opus 5.5 0d93b16fdd [refactor]: resolve inference configs before flattening them
generator_config_to_fastvideo_args now runs resolve_inference_config, in
the new fastvideo/api/inference_resolution.py, before it flattens the
config. The resolution goes through the ordered, provenance-recording driver
in fastvideo/api/resolution.py. VideoGenerator.from_config keeps the frozen
result as resolved_config, so the source of every startup value can be
inspected.

A mapping input is the raw user input, and every leaf in it counts as
explicit. A GeneratorConfig object does not record which fields the caller
wrote. written_fields therefore treats the fields that differ from the
schema defaults as explicit, and parsing its output gives back an equal
config.

INFERENCE_RESOLUTION_STEPS is empty in this commit, so FastVideoArgs is
unchanged. The golden snapshots of the YAML and from_pretrained cases add
the list of resolution decisions, which is empty here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 18:58:12 +00:00
Davids048andClaude Opus 5.5 7ba7dbbbd5 [misc]: deprecate the flat-flag entry point of the OpenAI API server
python -m fastvideo.entrypoints.openai.api_server parses the hand-written
FastVideoArgs argparse flags, a second input system next to the typed config
that fastvideo serve reads. It keeps working unchanged during the transition
and logs a warning that points to fastvideo serve --config with dotted
overrides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 18:30:31 +00:00
Davids048andClaude Opus 5.5 c7c2d77fae [bugfix]: apply the LTX-2 refine LoRA to the refine transformer
The from_pretrained keyword ltx2_refine_lora_path set
pipeline.components.lora_path, which reaches FastVideoArgs.lora_path and loads
as the main LoRA on the stage-1 transformer. The refine stage reads
FastVideoArgs.ltx2_refine_lora_path, which stayed unset. The conversion also
turned an empty string into None. An empty string is how callers such as the
Dreamverse GPU pool disable the refine LoRA, and None makes LTX2Pipeline load
the checkpoint default instead: fastvideo_refine_lora_path in model_index.json
is FastVideo/LTX2-Distilled-LoRA for the LTX-2 Diffusers checkpoints.

The refine LoRA gets its own field, pipeline.ltx2.refine.lora_path, whose
flat name is ltx2_refine_lora_path. None keeps the checkpoint default, and an
empty string disables the refine LoRA. The Dreamverse contract test, the
Dynamo contract example, and the parity inventory point at the field.

Golden changes: kwargs/ltx2_refine_lora_path now records the path on
ltx2_refine_lora_path instead of lora_path, and the added case
kwargs/ltx2_refine_lora_disabled pins the empty string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 18:29:37 +00:00
Davids048andClaude Opus 5.5 991fa391ff [refactor]: name the model-family config partitions *Options
The schema class MiniMaxH3Config had the same name as the MiniMax-H3 DiT
config class in fastvideo/configs/models/dits/minimax_h3.py. The family
partitions of PipelineSelection are renamed LTX2Options, LTX2RefineOptions,
MiniMaxH3Options, and LongCatOptions, so their names cannot be confused with
the model and pipeline config classes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 17:49:25 +00:00
Davids048andClaude Opus 5.5 ecb15bf9e0 [refactor]: move repository configs and examples off pipeline.experimental
Every key that repository YAML configs, Python examples, the Dreamverse
backend, and tests passed through pipeline.experimental has a typed
GeneratorConfig field, so they use those fields instead:
engine.attention, engine.compile.regional, pipeline.flow_shift,
pipeline.embedded_cfg_scale, pipeline.dmd_denoising_steps,
pipeline.components.lora_nickname, pipeline.minimax_h3, and
pipeline.longcat. No repository config outside the tests uses
pipeline.experimental any more.

The flat FastVideoArgs keywords that each migrated YAML file and each
basic_fasth3.py option set produces match the previous ones. The golden
snapshots of the two Hunyuan scripts now record embedded_cfg_scale and
flow_shift as floats (6.0 instead of 6), because the typed fields are
floats. The guidance tensor is built as float32 either way. The golden case
untyped_experimental_keys is renamed attention_precision_and_flow_shift with
identical content, and keys_without_typed_fields covers keywords that still
go to pipeline.experimental.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 17:37:22 +00:00
Davids048andClaude Opus 5.5 b24396561a [refactor]: add component overrides and model-family config partitions
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
2026-10-02 17:28:11 +00:00
Davids048andClaude Opus 5.5 0b80dd52db [refactor]: add typed fields for attention, precision, and pipeline knobs
These inference parameters could be set only through argparse flags or the
untyped pipeline.experimental dict. Each one gains a typed GeneratorConfig
field whose flat name is its FastVideoArgs or PipelineConfig field, so YAML,
dotted CLI overrides, and from_pretrained keywords all reach it:

- engine.attention: backend, vsa_sparsity, vsa_tile_size, moba_config_path
- engine.precision: dit, vae, vae_decode, image_encoder, text_encoders
- engine.compile.regional (inference_torch_compile)
- pipeline: vae_sp, flow_shift, embedded_cfg_scale, dmd_denoising_steps
- pipeline.components.lora_target_modules

Every field defaults to None, which keeps the runtime or model default; a
None field is not passed to FastVideoArgs. pipeline.experimental still
accepts the flat names and still wins over the typed fields. The parity
inventory lists the typed paths, and the docs show the typed forms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 17:24:19 +00:00
Davids048andClaude Opus 5.5 04878f0584 [refactor]: declare flat keyword names on GeneratorConfig fields
Each GeneratorConfig field that has a name in the flat keyword API
(VideoGenerator.from_pretrained kwargs and FastVideoArgs fields) declares it
through flat_field(...) metadata. legacy_from_pretrained_to_config and
generator_config_to_fastvideo_args read those names instead of keeping two
hand-written mappings, so adding a typed field with a flat name needs no
compat change.

The keywords that need a conversion keep explicit branches: the
torch_compile_kwargs split, the transformer_quant instance, the LTX-2 refine
preset keys, empty-string refine paths, and a non-string pipeline_config.
_SPECIALLY_MAPPED_PATHS lists every field without a flat name, and
test_generator_config_flat_names.py fails when a field has neither.

Behavior is unchanged: the 150 golden config snapshots match. The flat
keyword dict no longer passes None for revision, dist_timeout, and
lazy_module_load, and always passes the default LoRA nickname and strength and
empty per-component compile dicts; each equals the FastVideoArgs default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 17:17:45 +00:00
Davids048andClaude Opus 5.5 cd1ffc09c9 [refactor]: add golden snapshots of resolved inference configuration
Record the final FastVideoArgs, PipelineConfig, and SamplingParam for every
registered model path, every repository YAML config with a generator section,
and a set of from_pretrained kwargs, argparse, environment-variable, and
GenerationRequest cases. Each case runs with registered FastVideo environment
variables unset and model-index downloads blocked, so the snapshot depends only
on the source tree. Later configuration refactors compare against these files
to show that behavior is unchanged, or to make an intended change visible as a
JSON diff.

Regenerate with: python -m fastvideo.tests.api.config_snapshot

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 10:03:56 +00:00
Davids048andClaude Opus 5.5 db4a60c5db [refactor]: add ordered config resolution with provenance
Add fastvideo/api/resolution.py. resolve_generator_config parses a raw
GeneratorConfig mapping and runs an ordered list of steps over it. Each
step receives a read-only ResolutionView and returns {dotted_path: value};
it never assigns fields. The driver records every decision as
(source, values), where source is the step's qualified name, and the last
decision for a path wins. Explicit paths are taken from the keys of the
raw mapping, so "the user set this" is never inferred from defaults.

The result is a frozen ResolvedGeneratorConfig with the same nesting as
GeneratorConfig, per-path provenance, and with_override(source, values)
as the only way to change it after resolution; every override is logged.

The module is not wired into FastVideoArgs, compat.py, or any entry
point yet, so runtime behavior is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-02 09:55:15 +00:00
777 changed files with 96572 additions and 15701 deletions
@@ -80,26 +80,36 @@ def _run_fastvideo_pipeline(model_path: Path, params: dict[str, Any]) -> Any:
generator = VideoGenerator.from_pretrained(
str(model_path),
num_gpus=1,
use_fsdp_inference=False,
dit_cpu_offload=False,
vae_cpu_offload=False,
text_encoder_cpu_offload=False,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False,
"offload": {
"dit": False,
"vae": False,
"text_encoder": False,
},
},
},
)
try:
return generator.generate_video(
prompt=params["prompt"],
negative_prompt=params.get("negative_prompt"),
output_path=f"outputs_{_MODEL_FAMILY}/pipeline_parity",
save_video=False,
height=params.get("height"),
width=params.get("width"),
num_frames=params.get("num_frames"),
fps=params.get("fps"),
num_inference_steps=params["num_inference_steps"],
guidance_scale=params.get("guidance_scale"),
seed=params["seed"],
)
return generator.generate({
"prompt": params["prompt"],
"negative_prompt": params.get("negative_prompt"),
"sampling": {
"height": params.get("height"),
"width": params.get("width"),
"num_frames": params.get("num_frames"),
"fps": params.get("fps"),
"num_inference_steps": params["num_inference_steps"],
"guidance_scale": params.get("guidance_scale"),
"seed": params["seed"],
},
"output": {
"output_path": f"outputs_{_MODEL_FAMILY}/pipeline_parity",
"save_video": False,
},
})
finally:
generator.shutdown()
@@ -84,7 +84,9 @@ fastvideo/configs/models/dits/__init__.py
fastvideo/configs/models/encoders/__init__.py
fastvideo/configs/models/vaes/__init__.py
fastvideo/envs.py
fastvideo/fastvideo_args.py
fastvideo/api/schema.py
fastvideo/api/resolution.py
fastvideo/api/inference_resolution.py
fastvideo/distributed/**
fastvideo/layers/**
fastvideo/attention/**
+2 -1
View File
@@ -20,7 +20,8 @@ on a summary here.
- Read `docs/contributing/env_vars.md` in full.
- Decide whether the setting belongs in an environment variable or an argument
(rule 5 in the policy doc). Settings that users change per deployment are
arguments; add them through `fastvideo/fastvideo_args.py` instead.
arguments; add them as typed config fields in `fastvideo/api/schema.py`
instead.
## Inputs
-1
View File
@@ -489,7 +489,6 @@ def classify_paths(paths: list[str]) -> MergePlan:
_select_output_coverage(plan, path)
continue
if path in {
"fastvideo/fastvideo_args.py",
"fastvideo/forward_context.py",
"fastvideo/image_processor.py",
"fastvideo/registry.py",
+8 -6
View File
@@ -135,18 +135,20 @@ def main():
# Create a video generator with a pre-trained model
generator = VideoGenerator.from_pretrained(
"FastVideo/FastWan2.1-T2V-1.3B-Diffusers",
num_gpus=1, # Adjust based on your hardware
{"engine": {"num_gpus": 1}}, # Adjust based on your hardware
)
# Define a prompt for your video
prompt = "A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes wide with interest."
# Generate the video
video = generator.generate_video(
prompt,
output_path="my_videos/", # Controls where videos are saved
save_video=True
)
video = generator.generate({
"prompt": prompt,
"output": {
"output_path": "my_videos/", # Controls where videos are saved
"save_video": True,
},
})
if __name__ == '__main__':
main()
+1 -1
View File
@@ -42,7 +42,7 @@ Near-term OSS note:
- `apps/dreamverse/dreamverse/main.py`: websocket endpoint, request handling,
session state machine, rewrite orchestration, REST routes, and stream relay
- `apps/dreamverse/dreamverse/gpu_pool.py`: GPU worker processes, warmup, model
loading, and `generate_video()` calls through FastVideo
loading, and `generate()` calls through FastVideo
- `apps/dreamverse/dreamverse/prompt_enhancer.py`: prompt enhancement, rollout
rewrite execution, provider selection, and timeout/fallback behavior
- `apps/dreamverse/dreamverse/rewrite_prompt_payload.py`: canonical rewrite request payload
@@ -52,7 +52,8 @@ import torch # noqa: E402
from fastvideo import VideoGenerator # noqa: E402
from fastvideo.api import ( # noqa: E402
ComponentConfig, CompileConfig, EngineConfig, GeneratorConfig, OffloadConfig, PipelineSelection, QuantizationConfig,
ComponentConfig, CompileConfig, EngineConfig, GenerationResult, GeneratorConfig, OffloadConfig, PipelineSelection,
QuantizationConfig,
)
DEFAULT_PROMPT = ("A cinematic drone shot over coastal cliffs at sunrise, golden "
@@ -128,9 +129,9 @@ def _build_generator_config(model_path: str, enable_compile: bool, num_gpus: int
)
def _extract_stage_times(result: dict) -> OrderedDict[str, float]:
def _extract_stage_times(result: GenerationResult) -> OrderedDict[str, float]:
out: OrderedDict[str, float] = OrderedDict()
info = result.get("logging_info") if isinstance(result, dict) else None
info = result.logging_info if isinstance(result, GenerationResult) else None
if info is None:
return out
stages = getattr(info, "stages", None)
@@ -162,19 +163,25 @@ def _do_one_run(generator: VideoGenerator, prompt: str, *, height: int, width: i
_reset_peak_gpu()
t0 = time.perf_counter()
try:
result = generator.generate_video(
prompt=prompt,
negative_prompt="",
save_video=False,
height=height,
width=width,
num_frames=num_frames,
fps=24,
num_inference_steps=num_inference_steps,
guidance_scale=1.0,
seed=seed,
ltx2_image_crf=0.0,
)
result = generator.generate({
"prompt": prompt,
"negative_prompt": "",
"sampling": {
"height": height,
"width": width,
"num_frames": num_frames,
"fps": 24,
"num_inference_steps": num_inference_steps,
"guidance_scale": 1.0,
"seed": seed,
},
"output": {
"save_video": False
},
"extensions": {
"ltx2_image_crf": 0.0
},
})
if torch.cuda.is_available():
torch.cuda.synchronize()
except Exception as exc:
+52 -40
View File
@@ -14,6 +14,7 @@ import gc
import os
import re
import time
from typing import TYPE_CHECKING
import numpy as np
import torch
@@ -34,6 +35,9 @@ from dreamverse.config import (
)
from dreamverse.generation_contracts import StepResult
if TYPE_CHECKING:
from fastvideo.api import GenerationResult
# Multi-frame decoded continuation defaults from
# examples/inference/basic/basic_ltx2_distilled_video_continuation.py.
# Overridable via environment variables.
@@ -95,8 +99,8 @@ class ContinuationState:
self.video_images = None
self.audio_latents = None
def apply_video(self, request_kwargs: dict, segment_idx: int) -> None:
"""Seed next-segment kwargs with the cached tail frames."""
def apply_video(self, request: dict, segment_idx: int) -> None:
"""Seed the next-segment request with the cached tail frames."""
if segment_idx <= 1 or not self.video_images:
return
from PIL import Image
@@ -110,21 +114,21 @@ class ContinuationState:
arr = np.clip(arr, 0, 255).astype(np.uint8)
noisy.append(Image.fromarray(arr))
cond_images = noisy
request_kwargs["ltx2_video_conditions"] = [(
request["extensions"]["ltx2_video_conditions"] = [(
cond_images,
LTX2_VIDEO_CONDITIONING_FRAME_IDX,
LTX2_VIDEO_CONDITIONING_STRENGTH,
)]
request_kwargs["ltx2_images"] = None
request_kwargs["image_path"] = None
request["extensions"]["ltx2_images"] = None
request["inputs"]["image_path"] = None
def apply_audio(
self,
request_kwargs: dict,
request: dict,
segment_idx: int,
audio_lps: float,
) -> None:
"""Seed next-segment kwargs with clean audio latents + denoise mask.
"""Seed the next-segment request with clean audio latents + denoise mask.
When audio conditioning is longer than video, extend audio
generation and shift video RoPE forward so the audio prefix
@@ -141,9 +145,9 @@ class ContinuationState:
audio_extra = max(0, AUDIO_CONDITIONING_NUM_FRAMES - LTX2_VIDEO_CONDITIONING_NUM_FRAMES)
if audio_extra > 0:
audio_num_frames = NUM_FRAMES + audio_extra
request_kwargs["audio_num_frames"] = (audio_num_frames)
request["extensions"]["audio_num_frames"] = (audio_num_frames)
prefix_sec = float(audio_extra) / 24.0
request_kwargs["video_position_offset_sec"] = prefix_sec
request["extensions"]["video_position_offset_sec"] = prefix_sec
new_duration = float(NUM_FRAMES + audio_extra) / 24.0
total_T = max(
@@ -161,8 +165,8 @@ class ContinuationState:
mask = torch.ones((B, 1, total_T, 1), dtype=torch.float32)
mask[:, :, :audio_cond_T, :] = (1.0 - AUDIO_CONDITIONING_STRENGTH)
request_kwargs["ltx2_audio_clean_latent"] = clean
request_kwargs["ltx2_audio_denoise_mask"] = mask
request["extensions"]["ltx2_audio_clean_latent"] = clean
request["extensions"]["ltx2_audio_denoise_mask"] = mask
def save_video(self, frames: list) -> None:
"""Snapshot trailing N frames as PIL images for next-segment conditioning."""
@@ -306,7 +310,7 @@ class LTX2GenerationBackend:
),
)
self.generator = VideoGenerator.from_pretrained(config=generator_config)
self.generator = VideoGenerator.from_config(generator_config)
print(f"[GPU {self.gpu_id}] After model load: {self._gpu_mem()}")
lora_stack = DREAMVERSE_LORA_STACK or ([(DREAMVERSE_LORA_PATH,
@@ -403,7 +407,7 @@ class LTX2GenerationBackend:
return
loader = ComponentLoader.for_module_type("audio_encoder", "diffusers")
enc = loader.load(audio_vae_path, self.generator.fastvideo_args)
enc = loader.load(audio_vae_path, self.generator.resolved_config)
target = getattr(enc, "model", enc)
proc = AudioProcessor(
@@ -460,51 +464,59 @@ class LTX2GenerationBackend:
prompt = self._inject_style_trigger(prompt)
request_kwargs = dict(
prompt=prompt,
negative_prompt="",
save_video=False,
height=FRAME_HEIGHT,
width=FRAME_WIDTH,
num_frames=NUM_FRAMES,
fps=24,
num_inference_steps=NUM_INFERENCE_STEPS,
guidance_scale=1.0,
seed=10,
ltx2_image_crf=0.0,
image_path=image_path if segment_idx == 1 else None,
return_continuation_state=False,
)
request = {
"prompt": prompt,
"negative_prompt": "",
"inputs": {
"image_path": image_path if segment_idx == 1 else None
},
"sampling": {
"height": FRAME_HEIGHT,
"width": FRAME_WIDTH,
"num_frames": NUM_FRAMES,
"fps": 24,
"num_inference_steps": NUM_INFERENCE_STEPS,
"guidance_scale": 1.0,
"seed": 10,
},
"output": {
"save_video": False
},
"extensions": {
"ltx2_image_crf": 0.0,
"return_continuation_state": False,
},
}
if reset_conditioning:
self.continuation.clear()
audio_lps = (DEFAULT_LTX2_AUDIO_SAMPLE_RATE / DEFAULT_LTX2_AUDIO_HOP_LENGTH / DEFAULT_LTX2_AUDIO_DOWNSAMPLE)
# Phase 1: seed kwargs with prior-segment conditioning.
self.continuation.apply_video(request_kwargs, segment_idx)
self.continuation.apply_audio(request_kwargs, segment_idx, audio_lps)
# Phase 1: seed the request with prior-segment conditioning.
self.continuation.apply_video(request, segment_idx)
self.continuation.apply_audio(request, segment_idx, audio_lps)
# Phase 2: generate.
t0 = time.perf_counter()
result = self.generator.generate_video(**request_kwargs)
result = self.generator.generate(request)
torch.cuda.synchronize()
timings["generation_ms"] = (time.perf_counter() - t0) * 1000
if not isinstance(result, dict):
raise RuntimeError("Expected dictionary output from generate_video.")
frames = result.get("frames")
if isinstance(result, list):
raise RuntimeError("Expected a single GenerationResult from generate.")
frames = result.frames
if not isinstance(frames, list) or len(frames) == 0:
raise RuntimeError("Generation did not return frames.")
audio = result.get("audio")
audio_sample_rate = result.get("audio_sample_rate")
audio = result.audio
audio_sample_rate = result.audio_sample_rate
if audio is not None and audio_sample_rate is None:
# LTX2 audio decoding stage uses 24kHz output by default.
audio_sample_rate = 24000
print(f"[GPU {self.gpu_id}] audio_sample_rate missing from result; "
f"defaulting to {audio_sample_rate}Hz")
timings["generation_time_ms"] = result.get("generation_time", 0.0) * 1000
timings["generation_time_ms"] = (result.generation_time or 0.0) * 1000
# Phase 3: snapshot continuation state for the next segment.
t_save_start = time.perf_counter()
@@ -542,7 +554,7 @@ class LTX2GenerationBackend:
self,
audio: object,
audio_sample_rate: int | None,
result: dict,
result: "GenerationResult",
segment_idx: int,
) -> torch.Tensor | None:
"""Pick which tensor to cache for next-segment audio conditioning."""
@@ -557,7 +569,7 @@ class LTX2GenerationBackend:
f"for segment {segment_idx + 1}")
return re_encoded
return None
audio_latents = result.get("ltx2_audio_latents")
audio_latents = result.extra.get("ltx2_audio_latents")
if audio_latents is not None:
print(f"[GPU {self.gpu_id}] Cached audio latents "
f"shape={tuple(audio_latents.shape)} "
@@ -79,32 +79,24 @@ class MiniMaxH3GenerationBackend:
from fastvideo import VideoGenerator
from fastvideo.api import (
AttentionConfig,
CompileConfig,
ComponentConfig,
EngineConfig,
GeneratorConfig,
MiniMaxH3Options,
OffloadConfig,
ParallelismConfig,
PipelineSelection,
)
adapter_path = hf_hub_download(repo_id=adapter_repo, filename=adapter_filename)
experimental = {
"attention_backend": attention_backend,
"inference_torch_compile": attention_backend == "FLASH_ATTN",
"vae_parallel_decode": True,
"vae_parallel_decode_strategy": "gather",
}
if attention_backend == "VIDEO_SPARSE_ATTN_H3":
experimental.update({
"VSA_sparsity": 0.9,
"VSA_tile_size": 64,
})
use_vsa = attention_backend == "VIDEO_SPARSE_ATTN_H3"
generator_config = GeneratorConfig(
model_path=model_path,
pipeline=PipelineSelection(
components=ComponentConfig(lora_path=adapter_path, lora_strength=1.0),
experimental=experimental,
model=MiniMaxH3Options(vae_parallel_decode=True, vae_parallel_decode_strategy="gather"),
),
engine=EngineConfig(
num_gpus=DREAMVERSE_SP_SIZE,
@@ -117,7 +109,12 @@ class MiniMaxH3GenerationBackend:
vae=True,
pin_cpu_memory=True,
),
compile=CompileConfig(enabled=False, vae_enabled=True),
compile=CompileConfig(enabled=False, vae_enabled=True, regional=attention_backend == "FLASH_ATTN"),
attention=AttentionConfig(
backend=attention_backend,
vsa_sparsity=0.9 if use_vsa else None,
vsa_tile_size=64 if use_vsa else None,
),
use_fsdp_inference=False,
),
)
@@ -13,7 +13,6 @@ FORBIDDEN_PREFIXES = (
"fastvideo.models",
"fastvideo.layers",
"fastvideo.worker",
"fastvideo.fastvideo_args",
)
ALLOWED_INTERNAL_IMPORTS = {
(
@@ -88,14 +88,13 @@ def test_initialize_builds_vsa_datafree_fasth3_generator(monkeypatch):
assert config.model_path == "MiniMaxAI/MiniMax-H3"
assert config.pipeline.components.lora_path.endswith("vsa-datafree/adapter_model.safetensors")
assert config.pipeline.components.lora_strength == 1.0
assert config.pipeline.experimental == {
"attention_backend": "VIDEO_SPARSE_ATTN_H3",
"inference_torch_compile": False,
"vae_parallel_decode": True,
"vae_parallel_decode_strategy": "gather",
"VSA_sparsity": 0.9,
"VSA_tile_size": 64,
}
assert config.pipeline.experimental == {}
assert config.engine.attention.backend == "VIDEO_SPARSE_ATTN_H3"
assert config.engine.attention.vsa_sparsity == 0.9
assert config.engine.attention.vsa_tile_size == 64
assert config.engine.compile.regional is False
assert config.pipeline.model.vae_parallel_decode is True
assert config.pipeline.model.vae_parallel_decode_strategy == "gather"
assert config.engine.num_gpus == 4
assert config.engine.parallelism.tp_size == 1
assert config.engine.parallelism.sp_size == 4
+2 -2
View File
@@ -207,7 +207,7 @@
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e_dsg" value="generator.generate_video()" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#9673a6;endArrow=classic;fontSize=10;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="do_step" target="generator" edge="1">
<mxCell id="e_dsg" value="generator.generate()" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#9673a6;endArrow=classic;fontSize=10;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="do_step" target="generator" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_dscache" value="read / write" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#d6b656;endArrow=classic;startArrow=classic;fontSize=10;exitX=0;exitY=0.8;exitDx=0;exitDy=0;entryX=1;entryY=0.2;entryDx=0;entryDy=0;" parent="1" source="do_step" target="caches" edge="1">
@@ -534,7 +534,7 @@
<mxPoint x="1040" y="1610" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm11a" value="10a. worker runs:&#xa;VideoGenerationWorker.generate_step()&#xa; (ltx2_generation.py:380)&#xa; → generator.generate_video()&#xa; → updates ContinuationState&#xa;then stream_fmp4() (av_streaming.py:121)&#xa; → ffmpeg (rawvideo+wav → fmp4)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe0b2;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxCell id="dm11a" value="10a. worker runs:&#xa;VideoGenerationWorker.generate_step()&#xa; (ltx2_generation.py:380)&#xa; → generator.generate()&#xa; → updates ContinuationState&#xa;then stream_fmp4() (av_streaming.py:121)&#xa; → ffmpeg (rawvideo+wav → fmp4)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe0b2;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="955" y="1640" width="180" height="70" as="geometry"/>
</mxCell>
<mxCell id="dm11" value="10b. resp_q.put(MediaInit / MediaChunk / MediaComplete / StepComplete)" style="endArrow=classic;html=1;strokeColor=#b85450;fontSize=10;labelBackgroundColor=#ffffff;" parent="1" edge="1">
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

@@ -64,8 +64,8 @@ generator:
# internal: pipeline_config.dit_config.quant_config = FP4Config()
# set in gpu_pool.py:280 (via the legacy in-place mutation). The
# public typed surface resolves "NVFP4" to NVFP4Config() and pins
# it on dit_config in FastVideoArgs.__post_init__. Comment this
# block out on hosts without flashinfer / NVFP4 hardware.
# it on dit_config when resolution materializes the PipelineConfig.
# Comment this block out on hosts without flashinfer / NVFP4 hardware.
quantization:
transformer_quant: NVFP4
@@ -67,7 +67,7 @@ test.describe('preset prompt generation', () => {
// on a B200 plus encode/transfer time. The "Continuation flipped
// to Generating + Leave button rendered" pair above is the proof
// the integration works: FE → /readyz → /curated-presets → WS
// /ws → BE → GPU pool → VideoGenerator.generate_video, all green.
// /ws → BE → GPU pool → VideoGenerator.generate, all green.
const video = page.locator('video').first();
await expect(video).toHaveCount(1);
});
+53 -33
View File
@@ -862,23 +862,40 @@ class JobRunner:
sp_size,
)
gen = VideoGenerator.from_pretrained(
model_id,
workload_type=workload_type,
num_gpus=num_gpus,
dit_layerwise_offload=dit_layerwise_offload,
**({
"override_pipeline_cls_name": override_pipeline_cls_name
} if override_pipeline_cls_name else {}),
dit_cpu_offload=dit_cpu_offload,
text_encoder_cpu_offload=text_encoder_cpu_offload,
vae_cpu_offload=vae_cpu_offload,
image_encoder_cpu_offload=image_encoder_cpu_offload,
use_fsdp_inference=use_fsdp_inference,
enable_torch_compile=enable_torch_compile,
VSA_sparsity=vsa_sparsity,
tp_size=tp_size,
sp_size=sp_size,
gen = VideoGenerator.from_config(
{
"model_path": model_id,
"engine": {
"num_gpus": num_gpus,
"parallelism": {
"tp_size": tp_size,
"sp_size": sp_size,
},
"offload": {
"dit": dit_cpu_offload,
"dit_layerwise": dit_layerwise_offload,
"text_encoder": text_encoder_cpu_offload,
"image_encoder": image_encoder_cpu_offload,
"vae": vae_cpu_offload,
},
"compile": {
"enabled": enable_torch_compile
},
"attention": {
"vsa_sparsity": vsa_sparsity
},
"use_fsdp_inference": use_fsdp_inference,
},
"pipeline": {
"workload_type":
workload_type,
**({
"components": {
"override_pipeline_cls_name": override_pipeline_cls_name
}
} if override_pipeline_cls_name else {}),
},
},
log_queue=log_queue,
)
@@ -1103,30 +1120,33 @@ class JobRunner:
# Without a name FastVideo derives the filename from the prompt.
safe_name = re.sub(r'[\\/:*?"<>|]+', "", job.name).strip().strip(".")
output_target = (os.path.join(job_output_dir, f"{safe_name[:80]}.mp4") if safe_name else job_output_dir)
gen_kwargs: dict[str, Any] = {
request: dict[str, Any] = {
"prompt": job.prompt,
"output_path": output_target,
"save_video": True,
"num_inference_steps": job.num_inference_steps,
"num_frames": job.num_frames,
"height": job.height,
"width": job.width,
"guidance_scale": job.guidance_scale,
"guidance_rescale": job.guidance_rescale,
"fps": job.fps,
"seed": job.seed,
"negative_prompt": job.negative_prompt or "",
"log_queue": log_queue,
"sampling": {
"num_inference_steps": job.num_inference_steps,
"num_frames": job.num_frames,
"height": job.height,
"width": job.width,
"guidance_scale": job.guidance_scale,
"guidance_rescale": job.guidance_rescale,
"fps": job.fps,
"seed": job.seed,
},
"output": {
"output_path": output_target,
"save_video": True,
},
}
if job.image_path:
gen_kwargs["image_path"] = job.image_path
request.setdefault("inputs", {})["image_path"] = job.image_path
if job.references:
gen_kwargs["references"] = _build_h3_references(job.references)
request.setdefault("inputs", {})["references"] = _build_h3_references(job.references)
if job.last_image_path:
# _prepare_fl2va requires a PIL image, not a path.
from PIL import Image as _PILImage
gen_kwargs["last_image"] = _PILImage.open(job.last_image_path)
generator.generate_video(**gen_kwargs)
request.setdefault("inputs", {})["last_image"] = _PILImage.open(job.last_image_path)
generator.generate(request, log_queue=log_queue)
buf.phase = "saving"
logger.info("Generation completed, searching for output file...")
@@ -131,8 +131,9 @@ export default function CreateJobModal({
const editingJobId = editingJob?.id ?? null;
const editingJobModelId = editingJob?.model_id ?? null;
// Layerwise offload and FSDP compete for the DiT weights and FastVideoArgs
// silently picks a winner (fastvideo_args.py:859); resolve it visibly here.
// Layerwise offload and FSDP compete for the DiT weights and the device offload
// policy (resolve_device_offload_conflicts in fastvideo/api/device_policy.py)
// silently picks a winner; resolve it visibly here.
// dit_cpu_offload is deliberately not interlocked -- it is a modifier, not a
// competing strategy.
const handleDitLayerwiseOffloadChange = React.useCallback((next: boolean) => {
+32 -4
View File
@@ -15,6 +15,9 @@ from fastvideo import VideoGenerator as FastVideoGenerator
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
# InferenceArgs keys that are SamplingConfig fields of a GenerationRequest.
_SAMPLING_INFERENCE_ARGS = ("height", "width", "num_frames", "num_inference_steps", "guidance_scale", "seed", "fps")
# Custom exception for interruption
class GenerationInterruptedException(Exception):
@@ -154,7 +157,17 @@ class VideoGenerator:
"""Thread function to run the generation"""
try:
if self.generator is not None:
self.generator.generate_video(prompt=prompt, output_path=output_path, **inference_args)
# Place each InferenceArgs value in the GenerationRequest section that owns it.
request: dict[str, Any] = {"prompt": prompt, "output": {"output_path": output_path}}
for key, value in inference_args.items():
if key == "image_path":
section = "inputs"
elif key in _SAMPLING_INFERENCE_ARGS:
section = "sampling"
else:
section = "extensions"
request.setdefault(section, {})[key] = value
self.generator.generate(request)
self._generation_result = os.path.join(output_path, f"{prompt[:100]}.mp4")
else:
raise RuntimeError("Generator is not initialized")
@@ -253,9 +266,24 @@ class VideoGenerator:
if self.generator is None:
print('generation_args', generation_args)
print('pipeline_config', pipeline_config)
self.generator = FastVideoGenerator.from_pretrained(model_path=model_path,
**generation_args,
pipeline_config=pipeline_config)
# Place each generation argument at its GeneratorConfig engine path.
engine_config: dict[str, Any] = {}
if "num_gpus" in generation_args:
engine_config["num_gpus"] = generation_args["num_gpus"]
for parallelism_key in ("tp_size", "sp_size"):
if parallelism_key in generation_args:
engine_config.setdefault("parallelism", {})[parallelism_key] = generation_args[parallelism_key]
if "dit_cpu_offload" in generation_args:
engine_config["offload"] = {"dit": generation_args["dit_cpu_offload"]}
self.generator = FastVideoGenerator.from_config({
"model_path": model_path,
"engine": engine_config,
"pipeline": {
"experimental": {
"pipeline_config": pipeline_config
}
},
})
print('inference_args', inference_args)
+1 -1
View File
@@ -544,7 +544,7 @@
"limitations": [
"Install from the DGX Spark guide, not the generic CUDA extra. GB10 has no FA4 / sm_100a VSA kernel; keep FASTVIDEO_FA4=0 and FASTVIDEO_VSA_SM100A=0.",
"Legal num_frames values are 17n+5, capped at 345 (15 s). Native 16:9 sizes include 832x480 and 1344x768.",
"Lazy module load reloads Qwen3-VL and the DiT between phases of each request. Do not pass --no-lazy-module-load on this box.",
"Lazy module load reloads Qwen3-VL and the DiT between phases of each request. Do not set engine.offload.lazy_module_load to false on this box.",
"A 345-frame request on one Spark can OOM. Prefer 124 or 243 frames, TAEH3 decode, or two Sparks over QSFP."
]
},
+6 -10
View File
@@ -57,19 +57,15 @@ Minimal usage example (based on `examples/inference/basic/basic.py`):
```python
from fastvideo import VideoGenerator
from fastvideo.api.sampling_param import SamplingParam
model_id = "Wan-AI/Wan2.1-T2V-1.3B-Diffusers" # or official_weights/<model_name>/
generator = VideoGenerator.from_pretrained(model_id, num_gpus=1)
generator = VideoGenerator.from_pretrained(model_id, {"engine": {"num_gpus": 1}})
sampling = SamplingParam.from_pretrained(model_id)
sampling.num_frames = 45
video = generator.generate_video(
"A vibrant city street at sunset.",
sampling_param=sampling,
output_path="video_samples",
save_video=True,
)
video = generator.generate({
"prompt": "A vibrant city street at sunset.",
"sampling": {"num_frames": 45},
"output": {"output_path": "video_samples", "save_video": True},
})
```
## Some questions to ask yourself before starting
+8 -7
View File
@@ -122,8 +122,9 @@ in `examples/`, `scripts/`, `docs/`, `apps/`, and the tests in the same pull req
minor release.
To remove a variable that no code reads, delete its entry and add the name to `DEPRECATED_VARIABLES` in
`fastvideo/envs.py` with a reason. `FastVideoArgs` calls `envs.warn_deprecated_variables()`, which logs a warning for
each listed variable that is set. Delete the entry in the next minor release.
`fastvideo/envs.py` with a reason. The config resolution step `warn_deprecated_environment_variables` in
`fastvideo/api/inference_resolution.py` calls `envs.warn_deprecated_variables()`, which logs a warning for each listed
variable that is set. Delete the entry in the next minor release.
## What the contract test checks
@@ -188,7 +189,7 @@ longer exists also fails the test, so the fixing pull request deletes its entry.
| `FASTVIDEO_LOGGING_LEVEL` | str | `INFO` | logging | Default logging level. |
| `FASTVIDEO_LOGGING_PREFIX` | str | `""` | logging | Prefix prepended to every log message. |
| `FASTVIDEO_STAGE_LOGGING` | bool | `0` | logging | Log the time that each pipeline stage takes. |
| `FASTVIDEO_ATTENTION_BACKEND` | str | unset | attention | Attention backend, as an AttentionBackendEnum name such as TORCH_SDPA, FLASH_ATTN, VIDEO_SPARSE_ATTN, SAGE_ATTN, or SAGE_ATTN_THREE. FastVideoArgs uses it when FastVideoArgs.attention_backend is unset. |
| `FASTVIDEO_ATTENTION_BACKEND` | str | unset | attention | Attention backend, as an AttentionBackendEnum name such as TORCH_SDPA, FLASH_ATTN, VIDEO_SPARSE_ATTN, SAGE_ATTN, or SAGE_ATTN_THREE. Config resolution uses it when engine.attention.backend is unset. An unsupported name raises an error. |
| `FASTVIDEO_FA4` | bool | `0` | attention | The FLASH_ATTN backend uses FlashAttention-4 (flash_attn.cute) instead of FA3 or FA2. |
| `FASTVIDEO_MINIMAX_H3_FA4_PACKED_VARLEN` | bool | `0` | attention | MiniMax-H3 dense DiT self-attention uses the FlashAttention-4 packed-varlen entry point. This changes the floating-point reduction order, so it is an inference-only opt-in. |
| `FASTVIDEO_VSA_SM100A` | bool | `0` | attention | VIDEO_SPARSE_ATTN_H3 sends no-grad tile-64 forwards to the data-center Blackwell (sm_100a) kernel. fastvideo-kernel reads the same variable with the same rule. |
@@ -196,9 +197,9 @@ longer exists also fails the test, so the fixing pull request deletes its entry.
| `FASTVIDEO_DISABLE_ATTENTION_COMPILE` | bool | `1` | attention | Keep attention forward out of torch.compile graphs (torch.compiler.disable). Set it to 0 to let attention constructed under that setting be traced. Setting it explicitly to true also blocks regional compile. |
| `FASTVIDEO_MLX_WINDOW` | int | `0` | attention | MLX FastWan windowed attention size in tokens. 0 uses full attention. |
| `FASTVIDEO_MLX_WINDOW_SINK` | int | `0` | attention | Number of sink tokens that MLX windowed attention always attends to. |
| `FASTVIDEO_INFERENCE_TORCH_COMPILE` | bool | `0` | performance | Compile each DiT transformer block with fullgraph torch.compile at inference. Same as FastVideoArgs.inference_torch_compile=True. |
| `FASTVIDEO_VAE_PARALLEL_DECODE` | bool | `0` | performance | MiniMax-H3 VAE decode splits its temporal chunks across the sequence-parallel ranks instead of running serially on the output rank. Same as FastVideoArgs.vae_parallel_decode=True. |
| `FASTVIDEO_VAE_PARALLEL_ENCODE` | bool | `0` | performance | MiniMax-H3 reference-video VAE encode splits its temporal chunks across the sequence-parallel ranks. Same as FastVideoArgs.vae_parallel_encode=True. |
| `FASTVIDEO_INFERENCE_TORCH_COMPILE` | bool | `0` | performance | Compile each DiT transformer block with fullgraph torch.compile at inference. Same as engine.compile.regional=True. |
| `FASTVIDEO_VAE_PARALLEL_DECODE` | bool | `0` | performance | MiniMax-H3 VAE decode splits its temporal chunks across the sequence-parallel ranks instead of running serially on the output rank. Same as pipeline.model.minimax_h3.vae_parallel_decode=True. |
| `FASTVIDEO_VAE_PARALLEL_ENCODE` | bool | `0` | performance | MiniMax-H3 reference-video VAE encode splits its temporal chunks across the sequence-parallel ranks. Same as pipeline.model.minimax_h3.vae_parallel_encode=True. |
| `FASTVIDEO_VAE_PARALLEL_DECODE_STRATEGY` | str | unset | performance | Collective that moves chunks in parallel VAE decode: gather (used when unset) or all_gather. |
| `FASTVIDEO_MINIMAX_H3_FUSIONS` | str | `""` | performance | MiniMax-H3 inference-only Triton fusions: all, 1, or a comma-separated subset of modulate,qknorm_rope,swiglu. Empty, 0, or none keeps the eager implementation. |
| `FASTVIDEO_FSDP2_AUTOWRAP` | bool | `0` | performance | FSDP2 shards modules by parameter count instead of the model's shard conditions. Not supported by self-forcing distillation. |
@@ -234,7 +235,7 @@ longer exists also fails the test, so the fixing pull request deletes its entry.
| `FASTVIDEO_LTX2_GEMMA_LOG` | str | `""` | debug | Log file for LTX-2 Gemma text-encoder hidden states, used by parity tests. Deprecated names: `LTX2_FASTVIDEO_GEMMA_LOG`. |
| `FASTVIDEO_COSMOS25_LOG_KNOBS` | bool | `0` | debug | Log the Cosmos 2.5 latent-preparation conditioning inputs. |
| `FASTVIDEO_CFG_GATE_STEP` | float | `1.0` | sampling | CFG gating fraction in [0, 1]. Steps before len(timesteps) \* X run the conditional and unconditional forwards; later steps reuse the cached difference. 1.0 disables gating. |
| `FASTVIDEO_LTX2_USE_DISTILLED_SIGMAS` | bool | `1` | sampling | LTX-2 uses the distilled sigma schedule when FastVideoArgs.ltx2_use_distilled_sigmas is also true. Deprecated names: `LTX2_USE_DISTILLED_SIGMAS`. |
| `FASTVIDEO_LTX2_USE_DISTILLED_SIGMAS` | bool | `1` | sampling | LTX-2 uses the distilled sigma schedule when pipeline.model.ltx2.use_distilled_sigmas is also true. Deprecated names: `LTX2_USE_DISTILLED_SIGMAS`. |
| `FASTVIDEO_EVAL_CACHE` | path | computed | eval | Cache directory for evaluation models and datasets. Defaults to $FASTVIDEO_CACHE_ROOT/eval. |
| `FASTVIDEO_PHYSICS_IQ_BUCKET_URL` | str | `https://storage.googleapis.com/physics-iq-benchmark` | eval | Base URL of the Physics-IQ benchmark bucket. |
| `FASTVIDEO_VBENCH_FULL_INFO_JSON` | str | unset | eval | Path to VBench_full_info.json, used instead of the vendored copy. Deprecated names: `VBENCH_FULL_INFO_JSON`. |
+1 -1
View File
@@ -112,7 +112,7 @@ per-metric policy with direction, percent threshold, absolute threshold, and a
`test_inference_performance.py` temporarily sets `FASTVIDEO_STAGE_LOGGING=1`
while it runs so pipeline stage execution times are available in
`generate_video(...).logging_info`. Stage logs use pipeline-unique keys such as
`generate(...).logging_info`. Stage logs use pipeline-unique keys such as
`prompt_encoding_stage` so duplicate stage classes do not collide. For
`PipelineStage` entries, shared component stage bases emit a stable
`component_metric`: text encoding stages map to `text_encoder_time_s`,
+154 -125
View File
@@ -2,136 +2,158 @@ status_definitions:
kept: "Public field remains on a public adapter surface with the same meaning."
moved: "Public field remains supported but normalizes into a different nested path."
preset_owned: "Public field remains supported only through a model/preset-specific surface."
compatibility_only: "Legacy public field remains adapter-only during migration and is not part of the canonical typed schema."
compatibility_only: "Public field that an adapter or an open mapping accepts outside the typed fields of the canonical schema."
private_only: "Field should only be handled by private adapters and is not a public FastVideo compatibility promise."
internal_only: "Field is runtime/config plumbing and should not be part of the new public typed inference API."
internal_only: "Field is runtime/config plumbing that model code, config resolution, or the runtime fills; it is not a public input."
unsupported: "Typed config field that no runtime code reads; resolution rejects a value."
surfaces:
fastvideo_args:
generator_config:
kept:
- model_path
- mode
- revision
- trust_remote_code
- engine.num_gpus
- engine.execution_backend
- engine.parallelism.tp_size
- engine.parallelism.sp_size
- engine.parallelism.hsdp_replicate_dim
- engine.parallelism.hsdp_shard_dim
- engine.parallelism.dist_timeout
- engine.offload.dit
- engine.offload.dit_layerwise
- engine.offload.text_encoder
- engine.offload.image_encoder
- engine.offload.vae
- engine.offload.pin_cpu_memory
- engine.compile.enabled
- engine.compile.backend
- engine.compile.fullgraph
- engine.compile.mode
- engine.compile.dynamic
- engine.compile.extras
- engine.enable_stage_verification
- engine.use_fsdp_inference
- engine.disable_autocast
- engine.attention.nvfp4_fa4
- pipeline.components.lora_path
- pipeline.components.lora_strength
- pipeline.output_type
- engine.parallelism.master_port
- engine.offload.lazy_module_load
- engine.compile.text_encoder_enabled
- engine.compile.vae_enabled
- engine.compile.audio_vae_enabled
- engine.compile.regional
- engine.compile.dit_kwargs
- engine.compile.text_encoder_kwargs
- engine.compile.vae_kwargs
- engine.compile.audio_vae_kwargs
- engine.attention.backend
- engine.attention.vsa_sparsity
- engine.attention.vsa_tile_size
- engine.attention.moba_config_path
- engine.precision.dit
- engine.precision.vae
- engine.precision.vae_decode
- engine.precision.image_encoder
- engine.precision.text_encoders
- engine.quantization.text_encoder_quant
- engine.quantization.transformer_quant
- pipeline.workload_type
- pipeline.components.config_root
- pipeline.components.pipeline_config_path
- pipeline.components.text_encoder_weights
- pipeline.components.transformer_weights
- pipeline.components.transformer_2_weights
- pipeline.components.upsampler_weights
- pipeline.components.lora_nickname
- pipeline.components.lora_target_modules
- pipeline.components.override_pipeline_cls_name
- pipeline.components.override_transformer_cls_name
- pipeline.vae_tiling
- pipeline.vae_sp
- pipeline.flow_shift
- pipeline.embedded_cfg_scale
- pipeline.dmd_denoising_steps
- pipeline.boundary_ratio
- pipeline.model.generic.dit
- pipeline.model.generic.vae
- pipeline.model.ltx2.dit
- pipeline.model.ltx2.vae
- pipeline.model.minimax_h3.dit
- pipeline.model.minimax_h3.vae
- pipeline.model.longcat.dit
- pipeline.model.longcat.vae
unsupported:
- pipeline.preset
- pipeline.preset_version
- pipeline.components.vae_weights
moved:
model_path: generator.model_path
workload_type: generator.pipeline.workload_type
distributed_executor_backend: generator.engine.execution_backend
trust_remote_code: generator.trust_remote_code
revision: generator.revision
num_gpus: generator.engine.num_gpus
tp_size: generator.engine.parallelism.tp_size
sp_size: generator.engine.parallelism.sp_size
hsdp_replicate_dim: generator.engine.parallelism.hsdp_replicate_dim
hsdp_shard_dim: generator.engine.parallelism.hsdp_shard_dim
dist_timeout: generator.engine.parallelism.dist_timeout
lora_path: generator.pipeline.components.lora_path
lora_nickname: generator.pipeline.components.lora_nickname
lora_strength: generator.pipeline.components.lora_strength
dit_cpu_offload: generator.engine.offload.dit
use_fsdp_inference: generator.engine.use_fsdp_inference
dit_layerwise_offload: generator.engine.offload.dit_layerwise
text_encoder_cpu_offload: generator.engine.offload.text_encoder
image_encoder_cpu_offload: generator.engine.offload.image_encoder
vae_cpu_offload: generator.engine.offload.vae
pin_cpu_memory: generator.engine.offload.pin_cpu_memory
lazy_module_load: generator.engine.offload.lazy_module_load
enable_torch_compile: generator.engine.compile.enabled
enable_torch_compile_text_encoder: generator.engine.compile.text_encoder_enabled
enable_torch_compile_vae: generator.engine.compile.vae_enabled
enable_torch_compile_audio_vae: generator.engine.compile.audio_vae_enabled
torch_compile_kwargs: generator.engine.compile.backend,fullgraph,mode,dynamic,extras
torch_compile_kwargs_dit: generator.engine.compile.dit_kwargs
torch_compile_kwargs_text_encoder: generator.engine.compile.text_encoder_kwargs
torch_compile_kwargs_vae: generator.engine.compile.vae_kwargs
torch_compile_kwargs_audio_vae: generator.engine.compile.audio_vae_kwargs
transformer_quant: generator.engine.quantization.transformer_quant
disable_autocast: generator.engine.disable_autocast
enable_stage_verification: generator.engine.enable_stage_verification
prompt_txt: request.inputs.prompt_path
override_text_encoder_safetensors: generator.pipeline.components.text_encoder_weights
override_text_encoder_quant: generator.engine.quantization.text_encoder_quant
transformer_quant: generator.engine.quantization.transformer_quant
override_transformer_cls_name: generator.pipeline.components.override_transformer_cls_name
init_weights_from_safetensors: generator.pipeline.components.transformer_weights
init_weights_from_safetensors_2: generator.pipeline.components.transformer_2_weights
override_pipeline_cls_name: generator.pipeline.components.override_pipeline_cls_name
boundary_ratio: request.sampling.boundary_ratio
ltx2_vae_tiling: generator.pipeline.vae_tiling
refine_enabled: generator.pipeline.preset_overrides.refine.enabled
refine_upsampler_path: generator.pipeline.components.upsampler_weights
refine_lora_path: generator.pipeline.components.lora_path
refine_num_inference_steps: request.stage_overrides.refine.num_inference_steps
refine_guidance_scale: request.stage_overrides.refine.guidance_scale
refine_add_noise: generator.pipeline.preset_overrides.refine.add_noise
ltx2_refine_enabled: generator.pipeline.preset_overrides.refine.enabled
ltx2_refine_upsampler_path: generator.pipeline.components.upsampler_weights
ltx2_refine_lora_path: generator.pipeline.components.lora_path
ltx2_refine_num_inference_steps: request.stage_overrides.refine.num_inference_steps
ltx2_refine_guidance_scale: request.stage_overrides.refine.guidance_scale
ltx2_refine_add_noise: generator.pipeline.preset_overrides.refine.add_noise
pipeline.preset_overrides:
target: generator.pipeline.model.ltx2.refine
note: "Only the refine mapping applies: resolution copies pipeline.preset_overrides.refine into the pipeline.model.ltx2.refine fields of the same names when the model is LTX-2. Other keys have no effect."
preset_owned:
ltx2_vae_spatial_tile_size_in_pixels: generator.pipeline.preset_overrides.ltx2.vae.spatial_tile_size_in_pixels
ltx2_vae_spatial_tile_overlap_in_pixels: generator.pipeline.preset_overrides.ltx2.vae.spatial_tile_overlap_in_pixels
ltx2_vae_temporal_tile_size_in_frames: generator.pipeline.preset_overrides.ltx2.vae.temporal_tile_size_in_frames
ltx2_vae_temporal_tile_overlap_in_frames: generator.pipeline.preset_overrides.ltx2.vae.temporal_tile_overlap_in_frames
ltx2_initial_latent_path: request.extensions.ltx2.initial_latent_path
ltx2_audio_latent_path: request.extensions.ltx2.audio_latent_path
- pipeline.model.ltx2.vae_spatial_tile_size_in_pixels
- pipeline.model.ltx2.vae_spatial_tile_overlap_in_pixels
- pipeline.model.ltx2.vae_temporal_tile_size_in_frames
- pipeline.model.ltx2.vae_temporal_tile_overlap_in_frames
- pipeline.model.ltx2.initial_latent_path
- pipeline.model.ltx2.audio_latent_path
- pipeline.model.ltx2.legacy_native_noise_order
- pipeline.model.ltx2.use_distilled_sigmas
- pipeline.model.ltx2.refine.enabled
- pipeline.model.ltx2.refine.num_inference_steps
- pipeline.model.ltx2.refine.guidance_scale
- pipeline.model.ltx2.refine.add_noise
- pipeline.model.ltx2.refine.image_crf
- pipeline.model.ltx2.refine.video_position_offset_sec
- pipeline.model.ltx2.refine.transformer_path
- pipeline.model.ltx2.refine.lora_path
- pipeline.model.ltx2.refine.noise_path
- pipeline.model.ltx2.refine.audio_noise_path
- pipeline.model.minimax_h3.sequential_load
- pipeline.model.minimax_h3.video_decode_backend
- pipeline.model.minimax_h3.taeh3_checkpoint
- pipeline.model.minimax_h3.taeh3_chunk_size
- pipeline.model.minimax_h3.vae_parallel_decode
- pipeline.model.minimax_h3.vae_parallel_encode
- pipeline.model.minimax_h3.vae_parallel_decode_strategy
- pipeline.model.longcat.enable_bsa
- pipeline.model.longcat.bsa_sparsity
- pipeline.model.longcat.bsa_cdf_threshold
- pipeline.model.longcat.bsa_chunk_q
- pipeline.model.longcat.bsa_chunk_k
compatibility_only:
mode: "Legacy multi-mode FastVideoArgs switch; typed inference config should not expose execution mode."
inference_mode: "Legacy boolean mirror of mode; kept only through adapters while FastVideoArgs remains."
lora_target_modules: "Legacy LoRA configuration surface pending dedicated component API."
output_type: "Legacy output formatting surface pending GenerationResult cleanup."
VSA_sparsity: "Model-specific inference optimization not yet represented in the typed public schema."
VSA_tile_size: "VSA-H3 tile geometry request; model-specific optimization not yet represented in the typed public schema."
inference_torch_compile: "Regional inference compile opt-in currently carried through PipelineSelection.experimental rather than CompileConfig."
vae_parallel_decode: "MiniMax-H3 sequence-parallel VAE decode opt-in; model-specific optimization not yet represented in the typed public schema."
h3_sequential_load: "MiniMax-H3 sequential text-encoder then DiT/VAE load; model-specific optimization not yet represented in the typed public schema."
video_decode_backend: "MiniMax-H3 video decoder selection (full VAE vs TAEH3 preview); model-specific optimization not yet represented in the typed public schema."
taeh3_checkpoint: "Optional local TAEH3 safetensors path; model-specific optimization not yet represented in the typed public schema."
taeh3_chunk_size: "TAEH3 temporal chunk length; model-specific optimization not yet represented in the typed public schema."
vae_parallel_encode: "MiniMax-H3 sequence-parallel reference VAE encode opt-in; model-specific optimization not yet represented in the typed public schema."
vae_parallel_decode_strategy: "Chunk-transport collective for vae_parallel_decode; model-specific optimization not yet represented in the typed public schema."
attention_backend: "Process-wide default attention-backend request applied per component at load time; kernel-selection knob not yet represented in the typed public schema."
moba_config_path: "Model-specific MoBA optimization surface not yet represented in the typed public schema."
master_port: "Executor/bootstrap compatibility field; not part of the canonical inference schema."
refine_transformer_path: "Generic stage-2 refine transformer override; no typed equivalent yet."
refine_noise_path: "Generic stage-2 refine noise override; no typed equivalent yet."
refine_audio_noise_path: "Generic stage-2 refine audio noise override; no typed equivalent yet."
ltx2_refine_transformer_path: "LTX-2 refine transformer carrier; no typed equivalent yet."
ltx2_refine_noise_path: "LTX-2 refine noise carrier; no typed equivalent yet."
ltx2_refine_audio_noise_path: "LTX-2 refine audio noise carrier; no typed equivalent yet."
ltx2_legacy_native_noise_order: "LTX-2 SSIM compatibility knob preserving legacy native latent noise ordering."
ltx2_use_distilled_sigmas: "LTX-2 compatibility knob gating use of distilled sigma schedule."
private_only:
ray_placement_group: "Ray deployment-only field."
ray_runtime_env: "Ray deployment-only field."
pipeline.experimental: "Open mapping for settings without a typed path: the pipeline_config source (a JSON path, a mapping, or a PipelineConfig), keys that runtime code reads by name (for example ray_runtime_env), and PipelineConfig attribute overrides for model-only fields (for example flow_shift_sr). Resolution rejects a key whose PipelineConfig attribute has a typed path."
internal_only:
pipeline_config: "Legacy internal carrier object."
preprocess_config: "Legacy preprocess carrier object."
moba_config: "Derived runtime config loaded from moba_config_path."
model_paths: "Runtime bookkeeping."
model_loaded: "Runtime bookkeeping."
engine.attention.moba_config: "V-MoBA attention settings that resolution loads from engine.attention.moba_config_path."
pipeline_config_base:
moved:
model_path: generator.model_path
pipeline_config_path: generator.pipeline.components.pipeline_config_path
embedded_cfg_scale: generator.pipeline.embedded_cfg_scale
flow_shift: generator.pipeline.flow_shift
disable_autocast: generator.engine.disable_autocast
vae_tiling: generator.pipeline.vae_tiling
vae_sp: generator.pipeline.vae_sp
dmd_denoising_steps: generator.pipeline.dmd_denoising_steps
boundary_ratio: generator.pipeline.boundary_ratio
dit_precision: generator.engine.precision.dit
vae_precision: generator.engine.precision.vae
vae_decode_precision: generator.engine.precision.vae_decode
image_encoder_precision: generator.engine.precision.image_encoder
text_encoder_precisions: generator.engine.precision.text_encoders
preset_owned:
embedded_cfg_scale: generator.pipeline.preset_overrides.embedded_cfg_scale
flow_shift: generator.pipeline.preset_overrides.flow_shift
flow_shift_sr: generator.pipeline.preset_overrides.flow_shift_sr
is_causal: generator.pipeline.preset_overrides.is_causal
vae_tiling: generator.pipeline.preset_overrides.vae_tiling
vae_sp: generator.pipeline.preset_overrides.vae_sp
dmd_denoising_steps: generator.pipeline.preset_overrides.dmd_denoising_steps
ti2v_task: generator.pipeline.preset_overrides.ti2v_task
lucy_edit_task: generator.pipeline.preset_overrides.lucy_edit_task
boundary_ratio: generator.pipeline.preset_overrides.boundary_ratio
flow_shift_sr: generator.pipeline.experimental.flow_shift_sr
is_causal: generator.pipeline.experimental.is_causal
ti2v_task: generator.pipeline.experimental.ti2v_task
lucy_edit_task: generator.pipeline.experimental.lucy_edit_task
compatibility_only:
model_path: "Redundant with generator.model_path."
disable_autocast: "Duplicated by generator.engine.disable_autocast during migration."
dit_precision: "Precision override pending dedicated typed component precision design."
upsampler_precision: "Precision override pending dedicated typed component precision design."
vae_precision: "Precision override pending dedicated typed component precision design."
vae_decode_precision: "Decode-only precision override pending dedicated typed component precision design."
image_encoder_precision: "Precision override pending dedicated typed component precision design."
image_encoder_precisions: "Precision overrides pending dedicated typed component precision design."
text_encoder_precisions: "Precision override pending dedicated typed component precision design."
internal_only:
dit_config: "Legacy internal component config object."
upsampler_config: "Legacy internal component config object."
@@ -144,6 +166,22 @@ surfaces:
scheduler_step_in_fp32: "Runtime scheduler precision toggle; not part of the public typed inference API."
pipeline_config_extensions:
moved:
enable_bsa:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
target: generator.pipeline.model.longcat.enable_bsa
bsa_sparsity:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
target: generator.pipeline.model.longcat.bsa_sparsity
bsa_cdf_threshold:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
target: generator.pipeline.model.longcat.bsa_cdf_threshold
bsa_chunk_q:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
target: generator.pipeline.model.longcat.bsa_chunk_q
bsa_chunk_k:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
target: generator.pipeline.model.longcat.bsa_chunk_k
preset_owned:
flux2_text_encoder_type:
sources:
@@ -324,18 +362,8 @@ surfaces:
- fastvideo.configs.pipelines.wan.WanI2V720PConfig
- fastvideo.configs.pipelines.wan.WanT2V480PConfig
- fastvideo.configs.pipelines.wan.WanT2V720PConfig
bsa_cdf_threshold:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
bsa_chunk_k:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
bsa_chunk_q:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
bsa_params:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
bsa_sparsity:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
enable_bsa:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
enable_kv_cache:
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
enhance_hf:
@@ -520,6 +548,7 @@ surfaces:
cfg_truncation: request.sampling.cfg_truncation
guidance_rescale: request.sampling.guidance_rescale
use_embedded_guidance: request.sampling.use_embedded_guidance
embedded_cfg_scale: request.sampling.embedded_cfg_scale
true_cfg_scale: request.sampling.true_cfg_scale
boundary_ratio: request.sampling.boundary_ratio
sigmas: request.sampling.sigmas
+11 -12
View File
@@ -28,19 +28,15 @@ Minimal usage (from `examples/inference/basic/basic.py`):
```python
from fastvideo import VideoGenerator
from fastvideo.api.sampling_param import SamplingParam
model_id = "Wan-AI/Wan2.1-T2V-1.3B-Diffusers" # or official_weights/<model_name>/
generator = VideoGenerator.from_pretrained(model_id, num_gpus=1)
generator = VideoGenerator.from_pretrained(model_id, {"engine": {"num_gpus": 1}})
sampling = SamplingParam.from_pretrained(model_id)
sampling.num_frames = 45
video = generator.generate_video(
"A vibrant city street at sunset.",
sampling_param=sampling,
output_path="video_samples",
save_video=True,
)
video = generator.generate({
"prompt": "A vibrant city street at sunset.",
"sampling": {"num_frames": 45},
"output": {"output_path": "video_samples", "save_video": True},
})
```
## Configuration system
@@ -74,8 +70,11 @@ not override checkpoint manifests, user pipeline overrides, or component
precision settings. HF IDs, local checkpoints, and old config imports retain
their existing resolution behavior, including first-match detector ordering.
`FastVideoArgs` (in `fastvideo/fastvideo_args.py`) provides runtime settings and
is passed into pipeline construction and stages.
`ResolvedGeneratorConfig` (in `fastvideo/api/resolution.py`) provides runtime
settings and is passed into pipeline construction and stages as `resolved_config`.
`resolve_inference_config` (in `fastvideo/api/inference_resolution.py`) builds it
from the typed config in `fastvideo/api/schema.py` and attaches the model's
frozen `PipelineConfig` as `resolved_config.pipeline_config`.
## Weights and Diffusers format
+11 -10
View File
@@ -32,7 +32,7 @@ from fastvideo.api import (
| Surface | Availability | Notes |
| --- | --- | --- |
| `VideoGenerator.from_pretrained(model_path, **typed_kwargs)` | Today | `typed_kwargs` is a stable subset from `GeneratorConfig` — no flat legacy LTX-2 kwargs (guaranteed after PR 6) |
| `VideoGenerator.from_pretrained(model_path, config)` | Today | `config` is a nested `GeneratorConfig` mapping without `model_path`; no flat keywords |
| `VideoGenerator.generate(request: GenerationRequest) -> GenerationResult` | Today | Aggregated; Dynamo wraps in `asyncio.to_thread` under `asyncio.Lock` |
| `VideoGenerator.generate_async(request) -> AsyncGenerator[VideoEvent, None]` | **PR 7.10** | Canonical execution substrate; sync wrapper reroutes through this |
| `VideoGenerator.default_health_check_request() -> GenerationRequest` | **PR 7.10** | 256x256 / 8 frames / 1 step; lets Dynamo build its health payload without knowing any FastVideo internals |
@@ -225,7 +225,7 @@ async def init_video_generation(runtime, config, shutdown_endpoints):
from fastvideo.api import config_to_dict
server_args, dynamo_args = config.server_args, config.dynamo_args
generator = VideoGenerator.from_pretrained(**config.fastvideo_kwargs())
generator = VideoGenerator.from_config(build_generator_config(server_args))
dump_config(dynamo_args.dump_config_to, config)
@@ -262,7 +262,8 @@ this adapter can build the config purely from the public typed schema:
def build_generator_config(args) -> "GeneratorConfig":
from fastvideo.api import (
CompileConfig, ComponentConfig, EngineConfig, GeneratorConfig,
OffloadConfig, ParallelismConfig, PipelineSelection,
LTX2Options, LTX2RefineOptions, OffloadConfig, ParallelismConfig,
PipelineSelection,
)
return GeneratorConfig(
model_path=args.model_path,
@@ -275,10 +276,8 @@ def build_generator_config(args) -> "GeneratorConfig":
pipeline=PipelineSelection(
workload_type=args.workload or "t2v",
preset=args.preset, # e.g. "ltx2_two_stage"
components=ComponentConfig(
upsampler_weights=args.refine_upsampler,
lora_path=args.refine_lora,
),
components=ComponentConfig(upsampler_weights=args.refine_upsampler),
model=LTX2Options(refine=LTX2RefineOptions(lora_path=args.refine_lora)),
),
)
```
@@ -310,8 +309,11 @@ re-chase FastVideo drift:
2. `ContinuationState.payload` is JSON-serializable or references
opaque blob ids. Dynamo can round-trip it through RPC without
special-casing torch tensors.
3. `VideoGenerator.from_pretrained` accepts a typed `GeneratorConfig`;
legacy flat kwargs are compatibility-only and deprecate in PR 13.
3. `VideoGenerator.from_pretrained(model_path, config)` takes a typed
`GeneratorConfig` or its nested mapping; any flat keyword raises
`TypeError` that points to the nested config.
`VideoGenerator.from_config(...)` takes the same settings with
`model_path` inside.
4. `generate_async` (PR 7.10+) emits events in order
`Progress* → Partial* → Final`; the final event always has exactly
one occurrence per request.
@@ -329,7 +331,6 @@ at FastVideo's CI — before the Dynamo-side integration even knows.
* Anything under `fastvideo.pipelines.*` directly (pipelines are
internal; presets identify them by name on
`PipelineSelection.preset`).
* `fastvideo.fastvideo_args.FastVideoArgs` (legacy compat type).
* `fastvideo.api.compat.*` private helpers
(`_validate_continuation_state` etc.) — the public boundary is
`VideoGenerator` + `fastvideo.api`.
@@ -17,7 +17,7 @@ xDiT vendor. Do not install xDiT for this path.
| Goal | How | Use two Sparks? |
|---|---|---|
| Two independent videos at once | One process per box, `num_gpus=1` | Throughput only. Each clip still takes the 1-GPU time for that size. |
| Two independent videos at once | One process per box, `engine.num_gpus: 1` | Throughput only. Each clip still takes the 1-GPU time for that size. |
| One clip, faster | Ray + `sp_size=2` + parallel VAE | **Yes.** One 768×1344×124 recipe was 292 s vs 374 s on one GB10. |
| One clip, longer | Same, more frames | **Yes.** 345 frames (~14.4 s at 24 fps) finished in 587 s at 768×1344. |
@@ -35,7 +35,7 @@ over ~21 GB/s RoCE.
QSFP; do not download 100+ GB twice over Wi-Fi.
- Ray in the FastVideo venv (`uv pip install ray` if it is not already there).
Each Spark has **one** GPU. `num_gpus=2` therefore means two nodes, which is
Each Spark has **one** GPU. `engine.num_gpus: 2` therefore means two nodes, which is
why the executor must be Ray (`mp` only works inside one process tree).
## 1. Put IPv4 on the QSFP NICs
@@ -197,8 +197,8 @@ sm_100a VSA kernel is not on this chip, so denoise is slower than a GB200
| NCCL hangs or uses Wi-Fi | `source spark_pair_env.sh`. Confirm `NCCL_SOCKET_IFNAME` is the QSFP NIC. |
| Gloo `connectFullMesh` / `remote=[127.0.0.1]` | Two 1-GPU nodes must not use loopback as the Gloo store. Source `spark_pair_env.sh` so `GLOO_SOCKET_IFNAME` is the QSFP NIC on **each** box. FastVideo no longer copies that NIC name from the driver onto workers. |
| Second `generate()` crashes `NoneType.parameters` | Sequential load used to drop the text encoder without reloading it. This branch reloads Qwen for later requests so `--warmup --repeats N` works. |
| OOM / `earlyoom` prefers Python | Lazy module load must stay on (do not pass `--no-lazy-module-load`). Peak GPU during 345-frame denoise is ~90 GiB/node. |
| `num_gpus=2` on one Spark | Each Spark has one GPU. Use Ray across two nodes, or `num_gpus=1` on one box. |
| OOM / `earlyoom` prefers Python | Lazy module load must stay on (do not pass `--no-lazy-module-load` to `basic_fasth3.py` or set `engine.offload.lazy_module_load: false`). Peak GPU during 345-frame denoise is ~90 GiB/node. |
| `engine.num_gpus: 2` on one Spark | Each Spark has one GPU. Use Ray across two nodes, or `engine.num_gpus: 1` on one box. |
## What we are not claiming
@@ -62,10 +62,12 @@ nothing to set. If you run a model that still defaults to an fp32 decode, set th
decode-only override yourself:
```python
from fastvideo.configs.pipelines.base import PipelineConfig
from fastvideo import VideoGenerator
pipeline_config = PipelineConfig.from_pretrained(model_id)
pipeline_config.vae_decode_precision = "bf16" # decode-only; leaves encode precision alone
generator = VideoGenerator.from_config({
"model_path": model_id,
"engine": {"precision": {"vae_decode": "bf16"}}, # decode-only; leaves encode precision alone
})
```
Decode is output-only, so lowering its precision is safe. (Encode seeds the
@@ -166,8 +168,9 @@ is power-cycled. To avoid it:
encoder is still resident, the process is a typical `earlyoom` kill (Python is
preferred). On unified memory, `lazy_module_load` auto-enables and owns that
split (encoder, then DiT, then VAE; DiT can drop before decode). Sequential
load is the H3-only fallback when lazy is off; do not pass
`--no-lazy-module-load` here. Geometry scalars come from checkpoint
load is the H3-only fallback when lazy is off; do not set
`engine.offload.lazy_module_load` to false here (`--no-lazy-module-load` in
`basic_fasth3.py` and `basic_minimax_h3_t2v.py`). Geometry scalars come from checkpoint
`config.json`, not live weights. See [Offloading](../../inference/offloading.md).
- **FastH3 TAEH3** (`--video-decode-backend taeh3`) is an opt-in preview decoder.
T2VA never materializes the 9.7 GiB video VAE (DiT still loads after Qwen via
@@ -201,7 +204,7 @@ A few things that surprise people on this box (beyond the memory notes above):
build recent enough to include its `transformers`-compatibility handling before
running it.
- **MiniMax H3 worker init can look healthy and still die on the first generate**
if deferred loading is off (`--no-lazy-module-load` and sequential also off)
if deferred loading is off (`engine.offload.lazy_module_load: false` and sequential also off)
and encoder, VAE, and DiT load together. On GB10 the log should show
`lazy_module_load owns deferral` (or, if lazy is off, sequential
`Released MiniMax-H3 text encoder after conditioning` before
+4 -4
View File
@@ -210,7 +210,7 @@ from fastvideo.pipelines.stages import (
InputValidationStage, CLIPTextEncodingStage, TimestepPreparationStage,
LatentPreparationStage, DenoisingStage, DecodingStage
)
from fastvideo.fastvideo_args import FastVideoArgs
from fastvideo.api.resolution import ResolvedGeneratorConfig
from fastvideo.pipelines.pipeline_batch_info import ForwardBatch
import torch
@@ -226,11 +226,11 @@ class MyCustomPipeline(ComposedPipelineBase):
def required_config_modules(self) -> List[str]:
return self._required_config_modules
def initialize_pipeline(self, fastvideo_args: FastVideoArgs):
def initialize_pipeline(self, resolved_config: ResolvedGeneratorConfig):
"""Initialize pipeline-specific components."""
pass
def create_pipeline_stages(self, fastvideo_args: FastVideoArgs):
def create_pipeline_stages(self, resolved_config: ResolvedGeneratorConfig):
"""Set up pipeline stages with proper dependency injection."""
self.add_stage(
stage_name="input_validation_stage",
@@ -294,7 +294,7 @@ class MyCustomStage(PipelineStage):
self.custom_module = custom_module
self.other_param = other_param
def forward(self, batch: ForwardBatch, fastvideo_args: FastVideoArgs) -> ForwardBatch:
def forward(self, batch: ForwardBatch, resolved_config: ResolvedGeneratorConfig) -> ForwardBatch:
# Access input data
input_data = batch.some_attribute
+51 -38
View File
@@ -103,6 +103,10 @@ PipelineConfig (fastvideo/configs/pipelines/base.py)
- Precision settings: `dit_precision`, `vae_precision`,
`text_encoder_precisions`.
These generation and precision attributes hold the model defaults. Resolution
copies them into the typed fields of the resolved config (`pipeline.flow_shift`,
`engine.precision.dit`, ...), and runtime code reads the typed fields.
Model-specific subclasses override defaults. For example,
`WanT2V480PConfig` sets `flow_shift=3.0` and uses `WanVideoConfig` as
its DiT config.
@@ -127,9 +131,11 @@ Concrete hierarchy: `DiTConfig` → `DiTArchConfig`, `VAEConfig` →
- `PipelineConfig.from_pretrained(model_path)` — resolves config class
via `get_pipeline_config_cls_from_name()`, instantiates with defaults.
- `PipelineConfig.from_kwargs(kwargs)` — resolves class, optionally loads
JSON via `load_from_json()`, then applies CLI overrides via
`update_config_from_dict()`.
- `PipelineConfig.from_source(model_path, source)` — resolves the registry
class of `model_path`, then updates it from `source`: a JSON path loaded
via `load_from_json()`, a mapping of field values applied via
`update_pipeline_config()`, or a `PipelineConfig` that replaces the
registry instance.
- `dump_to_json()` / `load_from_json()` — JSON persistence. Callable
fields and `arch_config` are excluded from dumps.
@@ -147,7 +153,7 @@ sp = SamplingParam.from_pretrained("Wan-AI/Wan2.1-T2V-1.3B-Diffusers")
### ComponentLoader (`fastvideo/models/loader/component_loader.py`)
Abstract base with a `load(model_path, fastvideo_args)` method.
Abstract base with a `load(model_path, resolved_config)` method.
`ComponentLoader.for_module_type(module_type, library)` is a factory
that dispatches to specialized loaders via a `module_loaders` dict:
@@ -167,7 +173,7 @@ that dispatches to specialized loaders via a `module_loaders` dict:
`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`.
offload are applied based on `resolved_config.engine.offload`.
Unknown module types fall back to `GenericComponentLoader`.
@@ -205,14 +211,14 @@ loading by calling `ComponentLoader.for_module_type()` then `.load()`.
Abstract base class using the Template Method pattern:
- `__call__(batch, fastvideo_args)` — orchestrates verification, timing,
- `__call__(batch, resolved_config)` — orchestrates verification, timing,
and error handling. Not overridden by subclasses.
- `forward(batch, fastvideo_args) -> ForwardBatch` — abstract, contains
- `forward(batch, resolved_config) -> ForwardBatch` — abstract, contains
the stage logic.
- `verify_input()` / `verify_output()` — optional hooks returning
`VerificationResult`. Default: no checks.
When `fastvideo_args.enable_stage_verification` is `True`, `__call__`
When `resolved_config.engine.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.
@@ -295,7 +301,7 @@ provides detailed error messages. Failed verification raises
Abstract base for all inference pipelines. Lifecycle:
1. **`__init__(model_path, fastvideo_args)`** — initializes distributed
1. **`__init__(model_path, resolved_config)`** — initializes distributed
environment via `maybe_init_distributed_environment_and_model_parallel
(tp_size, sp_size)`, then calls `load_modules()` to populate
`self.modules`.
@@ -303,7 +309,7 @@ Abstract base for all inference pipelines. Lifecycle:
setup), `create_pipeline_stages()` (abstract — subclasses wire stages),
optionally applies `torch.compile` to transformers, and calls
`warmup_sequence_parallel_communication()`.
3. **`forward(batch, fastvideo_args)`** — iterates `self.stages` calling
3. **`forward(batch, resolved_config)`** — iterates `self.stages` calling
each stage in order. Decorated with `@torch.no_grad()`.
Key class attributes:
@@ -316,8 +322,9 @@ Key methods:
- `add_stage(name, stage)` — appends to `_stages` list and
`_stage_name_mapping` dict, also sets attribute on `self`.
- `get_module(name, default)` — retrieves a loaded module.
- `from_pretrained(model_path, **kwargs)` — class method constructing
`FastVideoArgs` and calling `cls(...)` then `post_init()`.
- `from_pretrained(model_path, *, resolved_config)` — class method that
builds the pipeline from a resolved config (from
`resolve_inference_config({...})`) by calling `cls(...)` then `post_init()`.
### LoRAPipeline (`fastvideo/pipelines/lora_pipeline.py`)
@@ -352,12 +359,12 @@ Key APIs: `get_tp_rank()`, `get_tp_world_size()`, `get_sp_rank()`,
`warmup_sequence_parallel_communication()` pre-warms NCCL communicators
to avoid slow first forward passes.
Usage: `torchrun --nproc-per-node=N -m fastvideo.entrypoints.cli.main
generate --model-path ... --tp-size N --sp-size M`.
Usage: `fastvideo generate --config run.yaml
--generator.engine.parallelism.tp_size N --generator.engine.parallelism.sp_size M`.
### torch.compile Integration
When `fastvideo_args.enable_torch_compile` is `True`,
When `resolved_config.engine.compile.enabled` 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
@@ -369,47 +376,53 @@ modules are skipped.
```python
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,
"Wan-AI/Wan2.1-T2V-14B-Diffusers",
{"engine": {"num_gpus": 1, "parallelism": {"tp_size": 1, "sp_size": 1}}},
)
result = generator.generate({
"prompt": "A cat dancing",
"sampling": {"height": 720, "width": 1280, "num_frames": 81},
})
```
**CLI** (`fastvideo/entrypoints/cli/`):
```bash
# run.yaml holds `generator: {model_path: Wan-AI/Wan2.1-T2V-14B-Diffusers}`.
fastvideo generate \
--model-path "Wan-AI/Wan2.1-T2V-14B-Diffusers" \
--prompt "A cat dancing" \
--num-gpus 1
--config run.yaml \
--request.prompt "A cat dancing" \
--generator.engine.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`.
**ResolvedGeneratorConfig** (`fastvideo/api/resolution.py`): The frozen
runtime config that the executor, workers, pipelines, stages, and loaders
read. Key paths: `model_path`, `mode` (`ExecutionMode`),
`pipeline.workload_type` (`WorkloadType`), `engine.num_gpus`,
`engine.parallelism.tp_size`, `engine.parallelism.sp_size`,
`pipeline.components.lora_path`, `engine.offload.dit`,
`engine.offload.dit_layerwise`, `engine.compile.enabled`,
`engine.enable_stage_verification`, and `pipeline_config` (the frozen
`PipelineConfig`).
Constructed via `FastVideoArgs.from_kwargs(**kwargs)` which resolves the
`PipelineConfig` from the registry, applies JSON config if provided, and
merges CLI overrides.
Built by `resolve_inference_config(config)`
(`fastvideo/api/inference_resolution.py`), which runs the named resolution
steps (environment variables, model defaults, derived values, validation) in
order, records each decision, and then builds the `PipelineConfig` from the
registry, applies a JSON config if provided, and freezes it.
## End-to-End Inference Flow
```
User: VideoGenerator.from_pretrained(model_path, **kwargs)
User: VideoGenerator.from_pretrained(model_path, config)
│
├─ FastVideoArgs.from_kwargs() → PipelineConfig resolved via registry
├─ resolve_inference_config() → 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)
├─ pipeline_cls.__init__(model_path, resolved_config)
│ ├─ maybe_init_distributed(tp_size, sp_size)
│ └─ load_modules() → reads model_index.json, loads each component
│ ├─ ComponentLoader.for_module_type() → specialized loader
@@ -421,10 +434,10 @@ User: VideoGenerator.from_pretrained(model_path, **kwargs)
├─ torch.compile (if enabled)
└─ warmup_sequence_parallel_communication()
User: generator.generate_video(prompt, ...)
User: generator.generate(request)
│
├─ ForwardBatch constructed from SamplingParam + user args
└─ pipeline.forward(batch, fastvideo_args)
└─ pipeline.forward(batch, resolved_config)
├─ InputValidationStage → validates dims
├─ TextEncodingStage → prompt → embeddings
├─ ConditioningStage → prepares conditioning
+57 -42
View File
@@ -8,7 +8,7 @@ FastVideo automatically distributes the generation process when multiple GPUs ar
# Will use 4 GPUs in parallel for faster generation
generator = VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
num_gpus=4,
{"engine": {"num_gpus": 4}},
)
```
@@ -30,58 +30,53 @@ 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(model_path, config)` takes the startup
settings as a nested mapping at their typed config paths, such as
`{"engine": {"num_gpus": 2, "offload": {"dit": False}}}`; it is
`VideoGenerator.from_config` with `model_path` added to the mapping. Pass
generation settings to `VideoGenerator.generate` as a request:
```python
from fastvideo import VideoGenerator, SamplingParam, 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_pretrained(
model_name,
num_gpus=1,
dit_layerwise_offload=True, # FastVideoArgs option
pipeline_config=config
)
# Create and customize sampling parameters
sampling_param = SamplingParam.from_pretrained("Wan-AI/Wan2.1-T2V-1.3B-Diffusers")
# How many frames to generate
sampling_param.num_frames = 45
# Video resolution (width, height)
sampling_param.width = 1024
sampling_param.height = 576
# How many steps we denoise the video (higher = better quality, slower generation)
sampling_param.num_inference_steps = 30
# How strongly the video conforms to the prompt (higher = more faithful to prompt)
sampling_param.guidance_scale = 7.5
# Random seed for reproducibility
sampling_param.seed = 42 # Optional, leave unset for random results
generator = VideoGenerator.from_config({
"model_path": model_name,
"engine": {
"num_gpus": 1,
"offload": {"dit_layerwise": True},
"precision": {"vae": "fp16"},
},
})
# Generate video with custom parameters
prompt = "A beautiful sunset over a calm ocean, with gentle waves."
video = generator.generate_video(
prompt,
sampling_param=sampling_param,
output_path="my_videos/", # Controls where videos are saved
save_video=True
)
video = generator.generate({
"prompt": prompt,
"sampling": {
# How many frames to generate
"num_frames": 45,
# Video resolution (width, height)
"width": 1024,
"height": 576,
# How many steps we denoise the video (higher = better quality, slower generation)
"num_inference_steps": 30,
# How strongly the video conforms to the prompt (higher = more faithful to prompt)
"guidance_scale": 7.5,
# Random seed for reproducibility
"seed": 42, # Optional, leave unset for random results
},
"output": {
"output_path": "my_videos/", # Controls where videos are saved
"save_video": True,
},
})
# If return_frames=True, frames are available in video["frames"]
print(f"Generated {len(video['frames'])} frames")
# If return_frames=True, frames are available in video.frames
print(f"Generated {len(video.frames)} frames")
if __name__ == '__main__':
main()
@@ -125,6 +120,26 @@ Override individual values from the CLI with dotted paths:
fastvideo generate --config config.yaml --request.sampling.seed 42
```
## Where a Value Came From
FastVideo resolves the generator config once at startup and records the source of every value: the input config
(`input`; `explicit` tells whether you wrote the value or it is the schema default), a `FASTVIDEO_*` environment
variable, the model's defaults, or a derived value. A worker's device policy and values read from checkpoint files
are recorded too.
```python
generator = VideoGenerator.from_config(config)
generator.resolved_config.provenance("engine.parallelism.sp_size")
# PathProvenance(path='engine.parallelism.sp_size', value=2, source='derive_parallel_sizes', ...)
result = generator.generate(request)
result.resolved_request.provenance("sampling.num_frames")
# PathProvenance(..., value=81, source='fill_sampling_defaults[preset wan_t2v_1_3b]', explicit=False)
```
`resolved_config.provenance_table()` lists every path. Every value is decided before resolution ends, including the
device offload policy and the checkpoint defaults; after that, `resolved_config` is read-only.
## Performance Optimization
For configuring optimizations, please see our [optimizations guide](optimizations.md)
+1 -1
View File
@@ -66,7 +66,7 @@ as above.
For exports without this sidecar, an explicit ladder is supported via
`MiniMaxH3PipelineConfig.dmd_denoising_steps`, or through the typed API's
`PipelineSelection(experimental={"dmd_denoising_steps": [...]})`. The shifts
`PipelineSelection(dmd_denoising_steps=[...])`. The shifts
still come from the checkpoint scheduler configs. Keep generic `flow_shift`
unset: H3 has separate video and audio shifts, not one shared shift.
+24 -21
View File
@@ -38,18 +38,20 @@ def main():
# Create a video generator with a pre-trained model
generator = VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
num_gpus=1, # Adjust based on your hardware
{"engine": {"num_gpus": 1}}, # Adjust based on your hardware
)
# Define a prompt for your video
prompt = "A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes wide with interest."
# Generate the video
video = generator.generate_video(
prompt,
output_path="my_videos/", # Controls where videos are saved
save_video=True
)
video = generator.generate({
"prompt": prompt,
"output": {
"output_path": "my_videos/", # Controls where videos are saved
"save_video": True,
},
})
if __name__ == '__main__':
main()
@@ -75,23 +77,24 @@ Please see the [support matrix](support_matrix.md) for the list of supported mod
You can generate a video starting from an initial image:
```python
from fastvideo import VideoGenerator, SamplingParam
from fastvideo import VideoGenerator
def main():
# Create the generator
model_name = "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers"
generator = VideoGenerator.from_pretrained(model_name, num_gpus=1)
# Set up parameters with an initial image
sampling_param = SamplingParam.from_pretrained(model_name)
sampling_param.image_path = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/astronaut.jpg"
sampling_param.num_frames = 107
generator = VideoGenerator.from_pretrained(model_name, {"engine": {"num_gpus": 1}})
# Generate video based on the image
prompt = "A photograph coming to life with gentle movement"
generator.generate_video(prompt, sampling_param=sampling_param,
output_path="my_videos/",
save_video=True)
generator.generate({
"prompt": prompt,
# Set up parameters with an initial image
"inputs": {
"image_path": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/astronaut.jpg",
},
"sampling": {"num_frames": 107},
"output": {"output_path": "my_videos/", "save_video": True},
})
if __name__ == '__main__':
main()
@@ -106,12 +109,12 @@ Common issues and their solutions:
If you encounter CUDA out of memory errors:
- Reduce `num_frames` or video resolution
- Enable FastVideo offloading options such as `dit_layerwise_offload=True`
(single GPU) or `use_fsdp_inference=True` (multi-GPU)
- Enable FastVideo offloading options such as `engine.offload.dit_layerwise: true`
(single GPU) or `engine.use_fsdp_inference: true` (multi-GPU)
- Try a smaller model or use distilled versions
- Use `num_gpus` > 1 if multiple GPUs are available
- Try enabling FSDP inference with `use_fsdp_inference=True` (may slow down generation)
- Try enabling DiT layerwise offload with `dit_layerwise_offload=True` (now only a few models support this, but may introduce less overhead than FSDP)
- Use `engine.num_gpus` > 1 if multiple GPUs are available
- Try enabling FSDP inference with `engine.use_fsdp_inference: true` (may slow down generation)
- Try enabling DiT layerwise offload with `engine.offload.dit_layerwise: true` (now only a few models support this, but may introduce less overhead than FSDP)
### Slow Generation
+53 -36
View File
@@ -4,15 +4,17 @@ This page describes how to use offloading techniques for inference to reduce GPU
## Default Behavior
```python
dit_cpu_offload: bool = True
use_fsdp_inference: bool = False
dit_layerwise_offload: bool = True
text_encoder_cpu_offload: bool = True
image_encoder_cpu_offload: bool = True
vae_cpu_offload: bool = True
pin_cpu_memory: bool = True
lazy_module_load: bool | None = None
```yaml
engine:
use_fsdp_inference: false
offload:
dit: true # dit_cpu_offload
dit_layerwise: true # dit_layerwise_offload
text_encoder: true # text_encoder_cpu_offload
image_encoder: true # image_encoder_cpu_offload
vae: true # vae_cpu_offload
pin_cpu_memory: true
lazy_module_load: null # auto
```
On unified-memory accelerators such as NVIDIA GB10 and Apple silicon, FastVideo
@@ -35,8 +37,8 @@ channels, DiT patch size) so those stages do not materialize weights just to
read two integers. The MLX FastH3 runtime always uses this phase order. When
host offload is off, DiT safetensors are read onto the accelerator instead of
CPU-then-copy. Both flags default to auto (`None`) and turn on for
unified-memory devices such as GB10; lazy then disables sequential. Pass
`--no-lazy-module-load` to keep every component resident (sequential may still
unified-memory devices such as GB10; lazy then disables sequential. Set
`engine.offload.lazy_module_load: false` to keep every component resident (sequential may still
auto-arm). Two-node Spark
jobs still need this split: sequence parallel replicates the DiT on each GB10
(~66 GiB of weights plus activations). See
@@ -45,7 +47,10 @@ jobs still need this split: sequence parallel replicates the DiT on each GB10
## Behavior Explanation
!!! note
For CLI usage, replace underscores (`_`) with hyphens (`-`).
`VideoGenerator.from_pretrained` accepts the option names below as keywords, except `lazy_module_load` and
`h3_sequential_load`. In a YAML config or a dotted override, each option is a typed field:
`engine.use_fsdp_inference`, `engine.offload.<field>` as listed in the defaults above, and
`pipeline.model.minimax_h3.sequential_load` for `h3_sequential_load`.
### `use_fsdp_inference`
@@ -104,8 +109,8 @@ because the encoder has been released.
Leave the default on Spark / DGX Spark when `lazy_module_load` is off. When
both would arm (the GB10 auto case), lazy owns deferral and sequential stands
down so VAE `torch.compile` can attach to the lazy proxy. Force
`--h3-sequential-load` only when you need the split on a discrete GPU without
lazy load. Use `--no-h3-sequential-load` when you need more than one prompt per
`pipeline.model.minimax_h3.sequential_load: true` only when you need the split on a discrete GPU without
lazy load. Set `pipeline.model.minimax_h3.sequential_load: false` when you need more than one prompt per
worker and have enough memory to keep the encoder.
### `text_encoder_cpu_offload`
@@ -160,7 +165,7 @@ options above cannot help with because they act after loading. It is
particularly relevant on unified-memory devices, where host and device draw on
the same pool and moving weights to the host frees nothing. FastVideo
auto-enables it there (`lazy_module_load=None`). Leave it off when the model
already fits, or pass `--no-lazy-module-load` to keep components resident for
already fits, or set `engine.offload.lazy_module_load: false` to keep components resident for
later `generate()` calls.
This option applies to inference only. Training keeps every component resident
@@ -203,19 +208,25 @@ from fastvideo import VideoGenerator
generator = VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
num_gpus=1,
# Recommended for single GPU
dit_layerwise_offload=True,
# Enable if OOM happens
vae_cpu_offload=True,
image_encoder_cpu_offload=True,
text_encoder_cpu_offload=True,
# Speeds up CPU-GPU transfer
pin_cpu_memory=True,
{
"engine": {
"num_gpus": 1,
"offload": {
# Recommended for single GPU
"dit_layerwise": True,
# Enable if OOM happens
"vae": True,
"image_encoder": True,
"text_encoder": True,
# Speeds up CPU-GPU transfer
"pin_cpu_memory": True,
},
},
},
)
prompt = "A curious raccoon peers through a vibrant field of yellow sunflowers."
video = generator.generate_video(prompt, output_path="output/", save_video=True)
video = generator.generate({"prompt": prompt, "output": {"output_path": "output/", "save_video": True}})
```
### Multi-GPU with FSDP
@@ -225,18 +236,24 @@ from fastvideo import VideoGenerator
generator = VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
num_gpus=2,
# Recommended for multi-GPU
use_fsdp_inference=True,
dit_layerwise_offload=False,
dit_cpu_offload=False,
# Enable if OOM happens
vae_cpu_offload=True,
image_encoder_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
{
"engine": {
"num_gpus": 2,
# Recommended for multi-GPU
"use_fsdp_inference": True,
"offload": {
"dit_layerwise": False,
"dit": False,
# Enable if OOM happens
"vae": True,
"image_encoder": True,
"text_encoder": True,
"pin_cpu_memory": True,
},
},
},
)
prompt = "A majestic lion strides across the golden savanna."
video = generator.generate_video(prompt, output_path="output/", save_video=True)
video = generator.generate({"prompt": prompt, "output": {"output_path": "output/", "save_video": True}})
```
+64 -40
View File
@@ -166,22 +166,26 @@ Enable FP4 attention via the `--nvfp4_fa4` flag:
python examples/inference/optimizations/fp4_attn_wan2_1_1_3b.py --nvfp4_fa4
```
Or in Python via the `nvfp4_fa4` kwarg (sets env vars automatically):
Or in Python via the `engine.attention.nvfp4_fa4` field (resolution sets the env vars):
```python
from fastvideo import VideoGenerator
gen = VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
nvfp4_fa4=True,
num_gpus=1,
use_fsdp_inference=False, # FSDP is incompatible with FP4 pointer path
{
"engine": {
"attention": {"nvfp4_fa4": True},
"num_gpus": 1,
"use_fsdp_inference": False, # FSDP is incompatible with FP4 pointer path
},
},
)
gen.generate_video(prompt="A raccoon in sunflowers", save_video=True)
gen.generate(request={"prompt": "A raccoon in sunflowers", "output": {"save_video": True}})
```
#### Known Limitations
- `use_fsdp_inference=True` is incompatible with the FP4 path (FSDP shards invalidate tensor pointers)
- `engine.use_fsdp_inference: true` is incompatible with the FP4 path (FSDP shards invalidate tensor pointers)
- Per-call cosine similarity vs BF16: ~0.99 (slight quantization error accumulates over denoising steps)
- Only supports `headdim >= 128`
@@ -205,15 +209,15 @@ import os
os.environ["FASTVIDEO_ATTENTION_BACKEND"] = "ATTN_QAT_INFER"
from fastvideo import VideoGenerator
from fastvideo.layers.quantization import get_quantization_config
gen = VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
num_gpus=1,
# Wan-2.1 uses the nvfp4_qat config (NVFP4 is LTX2-specific). Pass an
# instance — the bare string is not resolved on the from_pretrained path.
transformer_quant=get_quantization_config("nvfp4_qat")(),
use_fsdp_inference=False, # FSDP shards invalidate the FP4 tensor pointers
)
gen = VideoGenerator.from_config({
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # FSDP shards invalidate the FP4 tensor pointers
# Wan-2.1 uses the nvfp4_qat config (NVFP4 is LTX2-specific).
"quantization": {"transformer_quant": "nvfp4_qat"},
},
})
gen.generate(request={"prompt": "A raccoon in sunflowers", "output": {"save_video": True}})
```
@@ -301,17 +305,34 @@ automatically.
### Usage
```python
from fastvideo import VideoGenerator
gen = VideoGenerator.from_config({
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
"engine": {"quantization": {"transformer_quant": "FP8"}}, # per-tensor (default)
})
gen.generate(request={"prompt": "A raccoon in sunflowers", "output": {"save_video": True}})
```
`engine.quantization.transformer_quant` takes a quantization registry name and builds that config with its default
arguments. To pass constructor arguments, such as per-channel granularity, set the config instance on the DiT config
through `pipeline.model.generic.dit` instead:
```python
from fastvideo import VideoGenerator
from fastvideo.layers.quantization import get_quantization_config
gen = VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
# Pass an instance — the bare string is not resolved on the from_pretrained path.
transformer_quant=get_quantization_config("FP8")(), # per-tensor (default)
# transformer_quant=get_quantization_config("FP8")(granularity="channel"), # slower, higher accuracy
)
gen.generate(request={"prompt": "A raccoon in sunflowers", "output": {"save_video": True}})
gen = VideoGenerator.from_config({
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
"pipeline": {
"model": {
"generic": {
"dit": {"quant_config": get_quantization_config("FP8")(granularity="channel")}, # slower, higher accuracy
},
},
},
})
```
Or run the example script:
@@ -341,7 +362,7 @@ end-to-end speedup. It is **off by default** and enabled per-run.
```python
generator = VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
enable_torch_compile=True,
{"engine": {"compile": {"enabled": True}}},
)
```
@@ -376,12 +397,14 @@ device is unsupported. Legacy VSA, MiniMax-H3 tile-256 VSA, and the explicit
eager with one warning instead of failing mid-denoise.
```python
generator = VideoGenerator.from_pretrained(
"MiniMaxAI/MiniMax-H3",
inference_torch_compile=True, # or FASTVIDEO_INFERENCE_TORCH_COMPILE=1
)
generator = VideoGenerator.from_config({
"model_path": "MiniMaxAI/MiniMax-H3",
"engine": {"compile": {"regional": True}}, # or FASTVIDEO_INFERENCE_TORCH_COMPILE=1
})
```
In a YAML config, set `generator.engine.compile.regional: true`.
Do not combine it with `torch_compile_kwargs['mode']` (the loader injects
inductor options, and torch.compile forbids mode+options); it is
independent of `enable_torch_compile`, and when both are set the regional
@@ -390,7 +413,7 @@ compile wins for the DiT.
### What to expect from generic compile
The Wan result below measures the existing generic
`enable_torch_compile=True` path. It is useful evidence that compile can help,
`engine.compile.enabled: true` path. It is useful evidence that compile can help,
but it is **not** a benchmark or numerical gate for the stricter regional
fullgraph path above.
@@ -433,12 +456,12 @@ not asserted by any standing SSIM regression here — the SSIM tests in
run with `enable_torch_compile` disabled. If you depend on compile
output staying close to eager (or your previous compiled run), run an
MS-SSIM gate on *your* config, especially when combining
`enable_torch_compile=True` with other numerics-affecting flags
`engine.compile.enabled: true` with other numerics-affecting flags
(quantized attention backends, FP4, layerwise offload edge cases).
### Known interactions
- **Layerwise CPU offload** (`dit_layerwise_offload=True`, the default):
- **Layerwise CPU offload** (`engine.offload.dit_layerwise: true`, the default):
the offload hook previously caused an implicit graph break once per
transformer layer, fragmenting the compiled region. Addressed in
hao-ai-lab/FastVideo#1365 — keep that fix to get a clean compiled
@@ -453,16 +476,17 @@ MS-SSIM gate on *your* config, especially when combining
grad-enabled path remain outside it. Use the default inductor mode shown
above unless your exact configuration has its own gate.
Extra `torch.compile` options are passed through `torch_compile_kwargs`
(a dict), accepted by `VideoGenerator.from_pretrained(...)` and by the
CLI as a JSON string via `--torch-compile-kwargs`. Example (currently
Extra `torch.compile` options live at `engine.compile.backend`,
`fullgraph`, `mode`, and `dynamic`; any other `torch.compile` kwargs go in
`engine.compile.extras`. Set them in the nested config, in a config file,
or as a CLI dotted override (for example
`--generator.engine.compile.mode reduce-overhead`). Example (currently
**not** recommended — see the CUDA-graphs caveat above):
```python
VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
enable_torch_compile=True,
torch_compile_kwargs={"mode": "reduce-overhead"}, # may error today
{"engine": {"compile": {"enabled": True, "mode": "reduce-overhead"}}}, # may error today
)
```
@@ -475,7 +499,7 @@ config; **discard the first generation** (graph build):
import time
from fastvideo import VideoGenerator
gen = VideoGenerator.from_pretrained("your-model-id", enable_torch_compile=True)
gen = VideoGenerator.from_pretrained("your-model-id", {"engine": {"compile": {"enabled": True}}})
req = {"prompt": "Your prompt", "sampling": {"seed": 1024},
"output": {"save_video": False}}
gen.generate(req) # warmup: graph build, discard
@@ -499,10 +523,10 @@ for backend in ["TORCH_SDPA", "FLASH_ATTN", "SAGE_ATTN"]:
os.environ["FASTVIDEO_ATTENTION_BACKEND"] = backend
generator = VideoGenerator.from_pretrained("your-model-id")
start_time = time.perf_counter()
generator.generate_video(
prompt="Your prompt",
seed=1024,
)
generator.generate({
"prompt": "Your prompt",
"sampling": {"seed": 1024},
})
elapsed = time.perf_counter() - start_time
print(f"{backend}: {elapsed:.2f}s")
```
+38 -28
View File
@@ -19,46 +19,56 @@ bash examples/training/finetune/wan_t2v_1.3B/crush_smol/preprocess_wan_data_t2v_
## Preprocessing Pipeline
The new preprocessing pipeline supports multiple dataset formats and video loaders:
The preprocessing pipeline supports multiple dataset formats and video loaders. It reads a `PreprocessRunConfig`
YAML file (`fastvideo/api/training_schema.py`): the model and workload type at the top level, and the preprocessing
settings in the `preprocess` section (the fields of `PreprocessConfig` in `fastvideo/configs/configs.py`):
```yaml
# preprocess_t2v.yaml
model_path: Wan-AI/Wan2.1-T2V-1.3B-Diffusers
pipeline:
workload_type: t2v
preprocess:
video_loader_type: torchvision
dataset_type: merged
preprocess_video_batch_size: 2
dataloader_num_workers: 0
max_height: 480
max_width: 832
num_frames: 77
train_fps: 16
samples_per_file: 8
flush_frequency: 8
video_length_tolerance_range: 5
```
Pass the file with `--config`. Each dotted override after it sets one field, for example the values that come from
shell variables:
```bash
GPU_NUM=2
MODEL_PATH="Wan-AI/Wan2.1-T2V-1.3B-Diffusers"
DATASET_PATH="data/crush-smol/"
OUTPUT_DIR="data/crush-smol_processed_t2v/"
torchrun --nproc_per_node=$GPU_NUM \
-m fastvideo.pipelines.preprocess.v1_preprocessing_new \
--model_path $MODEL_PATH \
--mode preprocess \
--workload_type t2v \
--preprocess.video_loader_type torchvision \
--preprocess.dataset_type merged \
--preprocess.dataset_path $DATASET_PATH \
--preprocess.dataset_output_dir $OUTPUT_DIR \
--preprocess.preprocess_video_batch_size 2 \
--preprocess.dataloader_num_workers 0 \
--preprocess.max_height 480 \
--preprocess.max_width 832 \
--preprocess.num_frames 77 \
--preprocess.train_fps 16 \
--preprocess.samples_per_file 8 \
--preprocess.flush_frequency 8 \
--preprocess.video_length_tolerance_range 5
--config preprocess_t2v.yaml \
--preprocess.dataset_path "$DATASET_PATH" \
--preprocess.dataset_output_dir "$OUTPUT_DIR"
```
### Key Parameters
| Parameter | Description |
|-----------|-------------|
| `--workload_type` | Task type: `t2v` (text-to-video) or `i2v` (image-to-video) |
| `--preprocess.dataset_type` | Input format: `hf` (HuggingFace) or `merged` (local folder) |
| `--preprocess.dataset_path` | Path to dataset (HF repo ID or local folder) |
| `--preprocess.dataset_output_dir` | Output directory for Parquet files |
| `--preprocess.video_loader_type` | Video decoder: `torchcodec` or `torchvision` |
| `--preprocess.max_height` / `max_width` | Target resolution for videos |
| `--preprocess.num_frames` | Number of frames to extract per video |
| `--preprocess.train_fps` | Target FPS for frame extraction |
| Parameter | Description |
| ------------------------------------- | ----------------------------------------------------------- |
| `pipeline.workload_type` | Task type: `t2v` (text-to-video) or `i2v` (image-to-video) |
| `preprocess.dataset_type` | Input format: `hf` (HuggingFace) or `merged` (local folder) |
| `preprocess.dataset_path` | Path to dataset (HF repo ID or local folder) |
| `preprocess.dataset_output_dir` | Output directory for Parquet files |
| `preprocess.video_loader_type` | Video decoder: `torchcodec` or `torchvision` |
| `preprocess.max_height` / `max_width` | Target resolution for videos |
| `preprocess.num_frames` | Number of frames to extract per video |
| `preprocess.train_fps` | Target FPS for frame extraction |
## Dataset Formats
+48 -37
View File
@@ -4,48 +4,58 @@ This guide covers finetuning video diffusion models with FastVideo, including fu
## Training Arguments
FastVideo training scripts use several argument groups:
Each training launcher passes a `TrainingRunConfig` YAML file to its entry point with `--config` (the schema is in
`fastvideo/api/training_schema.py`). A dotted override after `--config` sets one field, for example
`--training.optimizer.learning_rate 1e-5` or `--engine.num_gpus "$NUM_GPUS"`:
```bash
torchrun --nnodes 1 --nproc_per_node 4 \
fastvideo/training/wan_training_pipeline.py \
--config finetune_t2v.yaml \
--engine.num_gpus 4
```
The settings are grouped as follows:
### Training Arguments
| Argument | Description |
|----------|-------------|
| `--max_train_steps` | Total training steps |
| `--train_batch_size` | Batch size per GPU |
| `--gradient_accumulation_steps` | Steps to accumulate before optimizer update |
| `--num_latent_t` | Temporal latent dimension (reduce to save memory) |
| `--num_height` / `--num_width` | Video resolution |
| `--num_frames` | Number of frames per video |
| `--output_dir` | Directory for checkpoints |
| Config path | Description |
| ------------------------------------------------------ | ------------------------------------------------- |
| `training.loop.max_train_steps` | Total training steps |
| `training.data.train_batch_size` | Batch size per GPU |
| `training.loop.gradient_accumulation_steps` | Steps to accumulate before optimizer update |
| `training.data.num_latent_t` | Temporal latent dimension (reduce to save memory) |
| `training.data.num_height` / `training.data.num_width` | Video resolution |
| `training.data.num_frames` | Number of frames per video |
| `training.checkpoint.output_dir` | Directory for checkpoints |
### Parallelism Arguments
| Argument | Description |
|----------|-------------|
| `--num_gpus` | Total number of GPUs |
| `--sp_size` | Sequence parallel size (increase to reduce memory per GPU) |
| `--tp_size` | Tensor parallel size |
| `--hsdp_replicate_dim` | HSDP replication dimension |
| `--hsdp_shard_dim` | HSDP sharding dimension |
| Config path | Description |
| --------------------------------------- | ---------------------------------------------------------- |
| `engine.num_gpus` | Total number of GPUs |
| `engine.parallelism.sp_size` | Sequence parallel size (increase to reduce memory per GPU) |
| `engine.parallelism.tp_size` | Tensor parallel size |
| `engine.parallelism.hsdp_replicate_dim` | HSDP replication dimension |
| `engine.parallelism.hsdp_shard_dim` | HSDP sharding dimension |
### Optimizer Arguments
| Argument | Description |
|----------|-------------|
| `--learning_rate` | Base learning rate |
| `--mixed_precision` | Precision mode (`bf16` recommended) |
| `--weight_decay` | Weight decay for regularization |
| `--max_grad_norm` | Gradient clipping threshold |
| Config path | Description |
| ---------------------------------- | ------------------------------- |
| `training.optimizer.learning_rate` | Base learning rate |
| `training.optimizer.weight_decay` | Weight decay for regularization |
| `training.optimizer.max_grad_norm` | Gradient clipping threshold |
### Validation Arguments
| Argument | Description |
|----------|-------------|
| `--log_validation` | Enable validation logging |
| `--validation_dataset_file` | JSON file with validation prompts |
| `--validation_steps` | Run validation every N steps |
| `--validation_sampling_steps` | Inference steps for validation |
| `--validation_guidance_scale` | CFG scale for validation |
| Config path | Description |
| ------------------------------------ | ----------------------------------------------------------- |
| `training.validation.enabled` | Enable validation logging |
| `training.validation.dataset_file` | JSON file with validation prompts |
| `training.validation.every_steps` | Run validation every N steps |
| `training.validation.sampling_steps` | Inference steps for validation (a list, for example `[50]`) |
| `training.validation.guidance_scale` | CFG scale for validation |
## Full Finetuning
@@ -59,8 +69,8 @@ bash examples/training/finetune/wan_t2v_1.3B/crush_smol/finetune_t2v.sh
**Typical settings:**
- Learning rate: `1e-5` to `5e-5`
- Gradient checkpointing: `--enable_gradient_checkpointing_type "full"`
- Memory scaling: Increase `--sp_size` or reduce `--num_latent_t` to fit in memory
- Gradient checkpointing: `training.model.enable_gradient_checkpointing_type: full`
- Memory scaling: Increase `engine.parallelism.sp_size` or reduce `training.data.num_latent_t` to fit in memory
## Attention Quantization-Aware Training
@@ -80,10 +90,10 @@ LoRA (Low-Rank Adaptation) trains lightweight adapters while keeping the base mo
### LoRA-Specific Arguments
| Argument | Description |
|----------|-------------|
| `--lora_training True` | Enable LoRA mode |
| `--lora_rank` | Rank of LoRA adapters (16, 32, 64, 128) |
| Config path | Description |
| ----------------------------- | --------------------------------------- |
| `training.lora.enabled: true` | Enable LoRA mode |
| `training.lora.rank` | Rank of LoRA adapters (16, 32, 64, 128) |
### Learning Rate for LoRA
@@ -103,7 +113,7 @@ bash examples/training/finetune/wan_t2v_1.3B/crush_smol/finetune_t2v_lora.sh
Key differences from full finetune:
- Add `--lora_training True --lora_rank 32`
- Set `training.lora.enabled: true` and `training.lora.rank: 32`
- Use higher learning rate (10–20× full finetune)
- Can run on fewer GPUs (even single GPU)
- Outputs adapter weights instead of full model
@@ -186,4 +196,5 @@ Each example includes:
- `preprocess_*.sh` — run preprocessing
- `finetune_*.sh` — full finetune launcher
- `finetune_*_lora.sh` — LoRA finetune launcher
- a YAML file next to each launcher — the `TrainingRunConfig` that the launcher passes with `--config`
- `validation.json` — validation prompts
+10 -104
View File
@@ -16,111 +16,17 @@ DATA_DIR="data/matrixgame2"
VALIDATION_DATASET_FILE="examples/distill/MatrixGame2.0/validation.json"
NUM_GPUS=1
# Training arguments
training_args=(
--tracker_project_name "matrixgame2_sf"
--output_dir "checkpoints/matrixgame2_sf_${RUN_NAME}"
--wandb_run_name "${RUN_NAME}_test"
--max_train_steps 5
--train_batch_size 1
--train_sp_batch_size 1
--gradient_accumulation_steps 1
--num_latent_t 21
--num_height 352
--num_width 640
--enable_gradient_checkpointing_type "full"
--simulate_generator_forward
--num_frames 81
--num_frame_per_block 3 # Frame generation block size for self-forcing
# --enable_gradient_masking
# --gradient_mask_last_n_frames 21
# --init_weights_from_safetensors "path/to/generator_ema.safetensors"
)
# Parallel arguments
parallel_args=(
--num_gpus $NUM_GPUS
--sp_size 1
--tp_size 1
--hsdp_replicate_dim 1
--hsdp_shard_dim $NUM_GPUS
)
model_args=(
--model_path $GENERATOR_MODEL_PATH # TODO: check if you can remove this in this script
--pretrained_model_name_or_path $GENERATOR_MODEL_PATH
--real_score_model_path $REAL_SCORE_MODEL_PATH
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
)
dataset_args=(
--data_path "$DATA_DIR"
--dataloader_num_workers 4
)
# Validation arguments
validation_args=(
--log_validation
--log_visualization
--visualization-steps 100
--validation_dataset_file "$VALIDATION_DATASET_FILE"
--validation_steps 100
--validation_sampling_steps "4"
--validation_guidance_scale "6.0"
)
# Optimizer arguments
optimizer_args=(
--learning_rate 3e-6
--mixed_precision "bf16"
--weight_only_checkpointing_steps 400
--training_state_checkpointing_steps 400
--weight_decay 0
--betas "0.9,0.95"
--max_grad_norm 1.0
)
# Miscellaneous arguments
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--flow_shift 5
--seed 1000
--use_ema True
--ema_decay 0.99
--ema_start_step 200
)
dmd_args=(
--dmd_denoising_steps '1000,750,500,250'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--dfake_gen_update_ratio 5
--real_score_guidance_scale 3.0
--fake_score_learning_rate 3e-7
--fake_score_betas "0.9,0.95"
--warp_denoising_step
)
self_forcing_args=(
--independent_first_frame False # Whether to treat first frame independently
--same_step_across_blocks True # Whether to use same denoising step across all blocks
--last_step_only False # Whether to only use the last denoising step
--context_noise 0 # Amount of noise to add during context caching (0 = no noise)
)
torchrun \
--nnodes 1 \
--nproc_per_node $NUM_GPUS \
fastvideo/training/matrixgame2_self_forcing_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}" \
"${self_forcing_args[@]}"
--config examples/distill/MatrixGame2.0/distill_dmd.yaml \
--model_path "$GENERATOR_MODEL_PATH" \
--engine.num_gpus "$NUM_GPUS" \
--engine.parallelism.hsdp_shard_dim "$NUM_GPUS" \
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
--training.data.data_path "$DATA_DIR" \
--training.checkpoint.output_dir "checkpoints/matrixgame2_sf_${RUN_NAME}" \
--training.tracker.run_name "${RUN_NAME}_test" \
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
@@ -63,93 +63,6 @@ FAKE_SCORE_MODEL_PATH="FastVideo/Matrix-Game-2.0-Base-Diffusers"
DATA_DIR="data/matrixgame2"
VALIDATION_DATASET_FILE="examples/distill/MatrixGame2.0/validation.json"
training_args=(
--tracker_project_name "matrixgame2_sf"
--output_dir "checkpoints/matrixgame2_sf_${RUN_NAME}"
--wandb_run_name "${RUN_NAME}_test"
--max_train_steps 1200
--train_batch_size 1
--train_sp_batch_size 1
--gradient_accumulation_steps 2
--num_latent_t 21
--num_height 352
--num_width 640
--simulate_generator_forward
--num_frames 81
--num_frame_per_block 3
# --init_weights_from_safetensors "path/to/generator_ema.safetensors"
)
parallel_args=(
--num_gpus "${TOTAL_GPUS}"
--sp_size 1
--tp_size 1
--hsdp_replicate_dim 1
--hsdp_shard_dim "${TOTAL_GPUS}"
)
model_args=(
--model_path "${GENERATOR_MODEL_PATH}"
--pretrained_model_name_or_path "${GENERATOR_MODEL_PATH}"
--real_score_model_path "${REAL_SCORE_MODEL_PATH}"
--fake_score_model_path "${FAKE_SCORE_MODEL_PATH}"
)
dataset_args=(
--data_path "${DATA_DIR}"
--dataloader_num_workers 4
)
validation_args=(
--log_validation
--log_visualization
--visualization-steps 100
--validation_dataset_file "${VALIDATION_DATASET_FILE}"
--validation_steps 100
--validation_sampling_steps "4"
--validation_guidance_scale "6.0"
)
optimizer_args=(
--learning_rate 3e-6
--mixed_precision "bf16"
--weight_only_checkpointing_steps 400
--training_state_checkpointing_steps 400
--weight_decay 0
--betas "0.9,0.95"
--max_grad_norm 1.0
)
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--flow_shift 5
--seed 1000
--use_ema True
--ema_decay 0.99
--ema_start_step 200
)
dmd_args=(
--dmd_denoising_steps "1000,750,500,250"
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--dfake_gen_update_ratio 5
--real_score_guidance_scale 3.0
--fake_score_learning_rate 3e-7
--fake_score_betas "0.9,0.95"
--warp_denoising_step
)
self_forcing_args=(
--independent_first_frame False
--same_step_across_blocks True
--last_step_only False
--context_noise 0
)
srun python -m torch.distributed.run \
--nnodes "${SLURM_JOB_NUM_NODES}" \
--nproc_per_node "${GPUS_PER_NODE}" \
@@ -157,12 +70,13 @@ srun python -m torch.distributed.run \
--rdzv_backend=c10d \
--rdzv_endpoint="${MASTER_ADDR}:${MASTER_PORT}" \
fastvideo/training/matrixgame2_self_forcing_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}" \
"${self_forcing_args[@]}"
--config examples/distill/MatrixGame2.0/distill_dmd_slurm.yaml \
--model_path "${GENERATOR_MODEL_PATH}" \
--engine.num_gpus "${TOTAL_GPUS}" \
--engine.parallelism.hsdp_shard_dim "${TOTAL_GPUS}" \
--training.distillation.real_score_model_path "${REAL_SCORE_MODEL_PATH}" \
--training.distillation.fake_score_model_path "${FAKE_SCORE_MODEL_PATH}" \
--training.data.data_path "${DATA_DIR}" \
--training.checkpoint.output_dir "checkpoints/matrixgame2_sf_${RUN_NAME}" \
--training.tracker.run_name "${RUN_NAME}_test" \
--training.validation.dataset_file "${VALIDATION_DATASET_FILE}"
@@ -0,0 +1,68 @@
# Read by distill_dmd.sh, which passes model_path, engine.num_gpus, engine.parallelism.hsdp_shard_dim,
# training.distillation.real_score_model_path, training.distillation.fake_score_model_path, training.data.data_path,
# training.checkpoint.output_dir, training.tracker.run_name, and training.validation.dataset_file on the command line.
engine:
parallelism:
tp_size: 1
sp_size: 1
hsdp_replicate_dim: 1
precision:
dit: fp32
pipeline:
# components:
# transformer_weights: path/to/generator_ema.safetensors
flow_shift: 5.0
dmd_denoising_steps: [1000, 750, 500, 250]
training:
data:
dataloader_num_workers: 4
num_height: 352
num_width: 640
num_frames: 81
train_batch_size: 1
num_latent_t: 21
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 3.0e-06
betas: [0.9, 0.95]
weight_decay: 0.0
max_grad_norm: 1.0
loop:
max_train_steps: 5
gradient_accumulation_steps: 1
checkpoint:
training_state_checkpointing_steps: 400
weight_only_checkpointing_steps: 400
checkpoints_total_limit: 3
tracker:
project_name: matrixgame2_sf
validation:
enabled: true
sampling_steps: [4]
guidance_scale: 6.0
every_steps: 100
log_visualization: true
visualization_steps: 100
distillation:
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.0
fake_score_learning_rate: 3.0e-07
fake_score_betas: [0.9, 0.95]
simulate_generator_forward: true
warp_denoising_step: true
ema:
enabled: true
decay: 0.99
start_step: 200
self_forcing:
dfake_gen_update_ratio: 5
num_frame_per_block: 3 # Frame generation block size for self-forcing
independent_first_frame: false # Whether to treat first frame independently
same_step_across_blocks: true # Whether to use same denoising step across all blocks
last_step_only: false # Whether to only use the last denoising step
context_noise: 0 # Amount of noise to add during context caching (0 = no noise)
model:
enable_gradient_checkpointing_type: full
@@ -0,0 +1,66 @@
# Read by distill_dmd.slurm, which passes model_path, engine.num_gpus, engine.parallelism.hsdp_shard_dim,
# training.distillation.real_score_model_path, training.distillation.fake_score_model_path, training.data.data_path,
# training.checkpoint.output_dir, training.tracker.run_name, and training.validation.dataset_file on the command line.
engine:
parallelism:
tp_size: 1
sp_size: 1
hsdp_replicate_dim: 1
precision:
dit: fp32
pipeline:
# components:
# transformer_weights: path/to/generator_ema.safetensors
flow_shift: 5.0
dmd_denoising_steps: [1000, 750, 500, 250]
training:
data:
dataloader_num_workers: 4
num_height: 352
num_width: 640
num_frames: 81
train_batch_size: 1
num_latent_t: 21
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 3.0e-06
betas: [0.9, 0.95]
weight_decay: 0.0
max_grad_norm: 1.0
loop:
max_train_steps: 1200
gradient_accumulation_steps: 2
checkpoint:
training_state_checkpointing_steps: 400
weight_only_checkpointing_steps: 400
checkpoints_total_limit: 3
tracker:
project_name: matrixgame2_sf
validation:
enabled: true
sampling_steps: [4]
guidance_scale: 6.0
every_steps: 100
log_visualization: true
visualization_steps: 100
distillation:
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.0
fake_score_learning_rate: 3.0e-07
fake_score_betas: [0.9, 0.95]
simulate_generator_forward: true
warp_denoising_step: true
ema:
enabled: true
decay: 0.99
start_step: 200
self_forcing:
dfake_gen_update_ratio: 5
num_frame_per_block: 3
independent_first_frame: false
same_step_across_blocks: true
last_step_only: false
context_noise: 0
@@ -36,105 +36,16 @@ VALIDATION_DATASET_FILE=your_validation_data_dir
# export CUDA_VISIBLE_DEVICES=4,5
# IP=[MASTER NODE IP]
training_args=(
--tracker_project_name SFwan_t2v_distill_self_forcing_dmd
--output_dir your_output_dir
--max_train_steps 4000
--train_batch_size 1
--train_sp_batch_size 1
--gradient_accumulation_steps 1
--num_latent_t 21
--num_height 480
--num_width 832
--enable_gradient_checkpointing_type "full"
--log_visualization
--simulate_generator_forward
--num_frames 81
--num_frame_per_block 3 # Frame generation block size for self-forcing
--enable_gradient_masking
--gradient_mask_last_n_frames 21
)
parallel_args=(
--num_gpus $NUM_GPUS # 64
--sp_size 1
--tp_size 1
--hsdp_replicate_dim 1 # 64
--hsdp_shard_dim $NUM_GPUS
)
model_args=(
--model_path $GENERATOR_MODEL_PATH # TODO: check if you can remove this in this script
--pretrained_model_name_or_path $GENERATOR_MODEL_PATH
--real_score_model_path $REAL_SCORE_MODEL_PATH
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
)
dataset_args=(
--data_path "$DATA_DIR"
--dataloader_num_workers 4
)
validation_args=(
--log_validation
--validation_dataset_file "$VALIDATION_DATASET_FILE"
--validation_steps 50
--validation_sampling_steps "4"
--validation_guidance_scale "6.0" # not used for dmd inference
)
optimizer_args=(
--learning_rate 1e-5
--mixed_precision "bf16"
--training_state_checkpointing_steps 500
--weight_only_checkpointing_steps 500
--weight_decay 0.01
--betas '0.0,0.999'
--max_grad_norm 1.0
)
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--flow_shift 5
--seed 1000
--use_ema True
--ema_decay 0.99
--ema_start_step 100
--init_weights_from_safetensors your_ode_init_weights_path
)
dmd_args=(
--dmd_denoising_steps '1000,750,500,250'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--dfake_gen_update_ratio 5
--real_score_guidance_scale 3.0
--fake_score_learning_rate 8e-6
--fake_score_betas '0.0,0.999'
--warp_denoising_step
)
self_forcing_args=(
--independent_first_frame False # Whether to treat first frame independently
--same_step_across_blocks True # Whether to use same denoising step across all blocks
--last_step_only False # Whether to only use the last denoising step
--context_noise 0 # Amount of noise to add during context caching (0 = no noise)
)
torchrun \
--nnodes 1 \
--master_port $MASTER_PORT \
--nproc_per_node $NUM_GPUS \
fastvideo/training/wan_self_forcing_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}" \
"${self_forcing_args[@]}"
--config examples/distill/SFWan2.1-T2V/distill_dmd_t2v_1.3B.yaml \
--model_path "$GENERATOR_MODEL_PATH" \
--engine.num_gpus "$NUM_GPUS" \
--engine.parallelism.hsdp_shard_dim "$NUM_GPUS" \
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
--training.data.data_path "$DATA_DIR" \
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
@@ -0,0 +1,68 @@
# Read by distill_dmd_t2v_1.3B.sh, which passes model_path, engine.num_gpus, engine.parallelism.hsdp_shard_dim,
# training.distillation.real_score_model_path, training.distillation.fake_score_model_path, training.data.data_path, and
# training.validation.dataset_file on the command line.
engine:
parallelism:
tp_size: 1
sp_size: 1
hsdp_replicate_dim: 1 # 64
precision:
dit: fp32
pipeline:
components:
transformer_weights: your_ode_init_weights_path
flow_shift: 5.0
dmd_denoising_steps: [1000, 750, 500, 250]
training:
data:
dataloader_num_workers: 4
num_height: 480
num_width: 832
num_frames: 81
train_batch_size: 1
num_latent_t: 21
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 1.0e-05
betas: [0.0, 0.999]
weight_decay: 0.01
max_grad_norm: 1.0
loop:
max_train_steps: 4000
gradient_accumulation_steps: 1
checkpoint:
output_dir: your_output_dir
training_state_checkpointing_steps: 500
weight_only_checkpointing_steps: 500
checkpoints_total_limit: 3
tracker:
project_name: SFwan_t2v_distill_self_forcing_dmd
validation:
enabled: true
sampling_steps: [4]
guidance_scale: 6.0 # not used for dmd inference
every_steps: 50
log_visualization: true
distillation:
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.0
fake_score_learning_rate: 8.0e-06
fake_score_betas: [0.0, 0.999]
simulate_generator_forward: true
warp_denoising_step: true
ema:
enabled: true
decay: 0.99
start_step: 100
self_forcing:
dfake_gen_update_ratio: 5
num_frame_per_block: 3 # Frame generation block size for self-forcing
independent_first_frame: false # Whether to treat first frame independently
same_step_across_blocks: true # Whether to use same denoising step across all blocks
last_step_only: false # Whether to only use the last denoising step
context_noise: 0 # Amount of noise to add during context caching (0 = no noise)
model:
enable_gradient_checkpointing_type: full
@@ -8,17 +8,7 @@ OUTPUT_DIR="data/crush-smol_processed_t2v/"
torchrun --nproc_per_node=$GPU_NUM \
fastvideo/pipelines/preprocess/v1_preprocess.py \
--config examples/distill/SFWan2.1-T2V/preprocess_data.yaml \
--model_path $MODEL_PATH \
--data_merge_path $DATA_MERGE_PATH \
--preprocess_video_batch_size 8 \
--seed 42 \
--max_height 480 \
--max_width 832 \
--num_frames 81 \
--dataloader_num_workers 0 \
--output_dir=$OUTPUT_DIR \
--train_fps 16 \
--samples_per_file 8 \
--flush_frequency 8 \
--video_length_tolerance_range 5 \
--preprocess_task "t2v"
--preprocess.data_merge_path $DATA_MERGE_PATH \
--preprocess.dataset_output_dir=$OUTPUT_DIR
@@ -0,0 +1,19 @@
# Read by preprocess_data.sh, which passes model_path, preprocess.data_merge_path, and preprocess.dataset_output_dir.
# Offload settings of the per-task preprocessing pipelines (v1_preprocess.py).
engine:
offload:
dit_layerwise: true
image_encoder: true
pin_cpu_memory: true
preprocess:
preprocess_video_batch_size: 8
seed: 42
max_height: 480
max_width: 832
num_frames: 81
dataloader_num_workers: 0
train_fps: 16
samples_per_file: 8
flush_frequency: 8
video_length_tolerance_range: 5
preprocess_task: t2v
+6 -99
View File
@@ -49,96 +49,6 @@ VALIDATION_DATASET_FILE="/mnt/weka/home/hao.zhang/wl/FastVideo/examples/distill/
# export CUDA_VISIBLE_DEVICES=4,5
# IP=[MASTER NODE IP]
training_args=(
--tracker_project_name SFwan2.2_t2v_distill_self_forcing_dmd # Updated for Wan2.2
--output_dir "/mnt/sharefs/users/hao.zhang/SFwan2.2_t2v_finetune"
--override_transformer_cls_name "CausalWanTransformer3DModel"
--max_train_steps 4000
--train_batch_size 1
--train_sp_batch_size 1
--gradient_accumulation_steps 1
--num_latent_t 21
--num_height 448 # Updated to match Wan2.2 config
--num_width 832 # Updated to match Wan2.2 config
--enable_gradient_checkpointing_type "full"
--simulate_generator_forward
# --log_visualization
--num_frames 81
--num_frame_per_block 3 # Frame generation block size for self-forcing
--enable_gradient_masking
--gradient_mask_last_n_frames 21
# --init_weights_from_safetensors /mnt/sharefs/users/hao.zhang/wl/models/sf_ode_init_wan22_checkpoints/high/3k/
# --init_weights_from_safetensors_2 /mnt/sharefs/users/hao.zhang/wl/models/sf_ode_init_wan22_checkpoints/low/3k/
)
parallel_args=(
--num_gpus 32 # 64
--sp_size 1
--tp_size 1
--hsdp_replicate_dim 1 # 64
--hsdp_shard_dim 32
)
model_args=(
--model_path $GENERATOR_MODEL_PATH
--pretrained_model_name_or_path $GENERATOR_MODEL_PATH
--real_score_model_path $REAL_SCORE_MODEL_PATH
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
)
dataset_args=(
--data_path "$DATA_DIR"
--dataloader_num_workers 4
)
validation_args=(
--log_validation
--validation_dataset_file "$VALIDATION_DATASET_FILE"
--validation_steps 20
--validation_sampling_steps "4"
--validation_guidance_scale "6.0" # not used for dmd inference
)
optimizer_args=(
--learning_rate 1e-5
--mixed_precision "bf16"
--training_state_checkpointing_steps 500
--weight_only_checkpointing_steps 500
--weight_decay 0.01
--betas '0.0,0.999'
--max_grad_norm 1.0
)
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--flow_shift 5
--seed 1000
--use_ema True
--ema_decay 0.99
--ema_start_step 100
)
dmd_args=(
--dmd_denoising_steps '1000,750,500,250'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--dfake_gen_update_ratio 5
--real_score_guidance_scale 3.0
--fake_score_learning_rate 8e-6
--fake_score_betas '0.0,0.999'
--warp_denoising_step
)
self_forcing_args=(
--independent_first_frame False # Whether to treat first frame independently
--same_step_across_blocks True # Whether to use same denoising step across all blocks
--last_step_only False # Whether to only use the last denoising step
--context_noise 0 # Amount of noise to add during context caching (0 = no noise)
)
srun torchrun \
--nnodes $SLURM_JOB_NUM_NODES \
--nproc_per_node $NUM_GPUS \
@@ -146,12 +56,9 @@ srun torchrun \
--rdzv_backend=c10d \
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
fastvideo/training/wan_self_forcing_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}" \
"${self_forcing_args[@]}"
--config examples/distill/SFWan2.2-A14B/distill_dmd.yaml \
--model_path "$GENERATOR_MODEL_PATH" \
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
--training.data.data_path "$DATA_DIR" \
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
@@ -0,0 +1,72 @@
# Read by distill_dmd.sh, which passes model_path, training.distillation.real_score_model_path,
# training.distillation.fake_score_model_path, training.data.data_path, and training.validation.dataset_file on the
# command line.
engine:
num_gpus: 32 # 64
parallelism:
tp_size: 1
sp_size: 1
hsdp_replicate_dim: 1 # 64
hsdp_shard_dim: 32
precision:
dit: fp32
pipeline:
components:
# transformer_weights: /mnt/sharefs/users/hao.zhang/wl/models/sf_ode_init_wan22_checkpoints/high/3k/
# transformer_2_weights: /mnt/sharefs/users/hao.zhang/wl/models/sf_ode_init_wan22_checkpoints/low/3k/
override_transformer_cls_name: CausalWanTransformer3DModel
flow_shift: 5.0
dmd_denoising_steps: [1000, 750, 500, 250]
training:
data:
dataloader_num_workers: 4
num_height: 448 # Updated to match Wan2.2 config
num_width: 832 # Updated to match Wan2.2 config
num_frames: 81
train_batch_size: 1
num_latent_t: 21
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 1.0e-05
betas: [0.0, 0.999]
weight_decay: 0.01
max_grad_norm: 1.0
loop:
max_train_steps: 4000
gradient_accumulation_steps: 1
checkpoint:
output_dir: /mnt/sharefs/users/hao.zhang/SFwan2.2_t2v_finetune
training_state_checkpointing_steps: 500
weight_only_checkpointing_steps: 500
checkpoints_total_limit: 3
tracker:
project_name: SFwan2.2_t2v_distill_self_forcing_dmd # Updated for Wan2.2
validation:
enabled: true
sampling_steps: [4]
guidance_scale: 6.0 # not used for dmd inference
every_steps: 20
# log_visualization: true
distillation:
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.0
fake_score_learning_rate: 8.0e-06
fake_score_betas: [0.0, 0.999]
simulate_generator_forward: true
warp_denoising_step: true
ema:
enabled: true
decay: 0.99
start_step: 100
self_forcing:
dfake_gen_update_ratio: 5
num_frame_per_block: 3 # Frame generation block size for self-forcing
independent_first_frame: false # Whether to treat first frame independently
same_step_across_blocks: true # Whether to use same denoising step across all blocks
last_step_only: false # Whether to only use the last denoising step
context_noise: 0 # Amount of noise to add during context caching (0 = no noise)
model:
enable_gradient_checkpointing_type: full
@@ -47,84 +47,6 @@ OUTPUT_DIR="checkpoints/wan_t2v_finetune"
# export CUDA_VISIBLE_DEVICES=4,5
# IP=[MASTER NODE IP]
# Training arguments
training_args=(
--tracker_project_name wan_t2v_distill_dmd_VSA
--output_dir $OUTPUT_DIR
--max_train_steps 4000
--train_batch_size 1
--train_sp_batch_size 1
--gradient_accumulation_steps 1
--num_latent_t 21
--num_height 480
--num_width 832
--num_frames 81
--enable_gradient_checkpointing_type "full"
)
# Parallel arguments
parallel_args=(
--num_gpus 64
--sp_size 1
--tp_size 1
--hsdp_replicate_dim 64
--hsdp_shard_dim 1
)
# Model arguments
model_args=(
--model_path $MODEL_PATH
--pretrained_model_name_or_path $MODEL_PATH
--real_score_model_path $REAL_SCORE_MODEL_PATH
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
)
# Dataset arguments
dataset_args=(
--data_path "$DATA_DIR"
--dataloader_num_workers 4
)
# Validation arguments
validation_args=(
--log_validation
--validation_dataset_file "$VALIDATION_DATASET_FILE"
--validation_steps 200
--validation_sampling_steps "3"
--validation_guidance_scale "6.0" # not used for dmd inference
)
# Optimizer arguments
optimizer_args=(
--learning_rate 2e-6
--mixed_precision "bf16"
--training_state_checkpointing_steps 500
--weight_only_checkpointing_steps 500
--weight_decay 0.01
--max_grad_norm 1.0
)
# Miscellaneous arguments
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--ema_start_step 0
--flow_shift 8
--seed 1000
)
# DMD arguments
dmd_args=(
--dmd_denoising_steps '1000,757,522'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--generator_update_interval 5
--real_score_guidance_scale 3.5
--VSA_sparsity 0.8
)
srun torchrun \
--nnodes $SLURM_JOB_NUM_NODES \
--nproc_per_node $NUM_GPUS \
@@ -132,11 +54,10 @@ srun torchrun \
--rdzv_backend=c10d \
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
fastvideo/training/wan_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}"
--config examples/distill/Wan2.1-T2V/Wan-Syn-Data-480P/distill_dmd_VSA_t2v_1.3B.yaml \
--model_path "$MODEL_PATH" \
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
--training.data.data_path "$DATA_DIR" \
--training.checkpoint.output_dir "$OUTPUT_DIR" \
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
@@ -0,0 +1,55 @@
# Read by distill_dmd_VSA_t2v_1.3B.slurm, which passes model_path, training.distillation.real_score_model_path,
# training.distillation.fake_score_model_path, training.data.data_path, training.checkpoint.output_dir, and
# training.validation.dataset_file on the command line.
engine:
num_gpus: 64
parallelism:
tp_size: 1
sp_size: 1
hsdp_replicate_dim: 64
hsdp_shard_dim: 1
attention:
vsa_sparsity: 0.8
precision:
dit: fp32
pipeline:
flow_shift: 8.0
dmd_denoising_steps: [1000, 757, 522]
training:
data:
dataloader_num_workers: 4
num_height: 480
num_width: 832
num_frames: 81
train_batch_size: 1
num_latent_t: 21
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 2.0e-06
weight_decay: 0.01
max_grad_norm: 1.0
loop:
max_train_steps: 4000
gradient_accumulation_steps: 1
checkpoint:
training_state_checkpointing_steps: 500
weight_only_checkpointing_steps: 500
checkpoints_total_limit: 3
tracker:
project_name: wan_t2v_distill_dmd_VSA
validation:
enabled: true
sampling_steps: [3]
guidance_scale: 6.0 # not used for dmd inference
every_steps: 200
distillation:
generator_update_interval: 5
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.5
ema:
start_step: 0
model:
enable_gradient_checkpointing_type: full
@@ -47,84 +47,6 @@ OUTPUT_DIR="checkpoints/wan_t2v_finetune"
# export CUDA_VISIBLE_DEVICES=4,5
# IP=[MASTER NODE IP]
# Training arguments
training_args=(
--tracker_project_name wan_t2v_distill_dmd_VSA
--output_dir "$OUTPUT_DIR"
--max_train_steps 4000
--train_batch_size 1
--train_sp_batch_size 1
--gradient_accumulation_steps 1
--num_latent_t 21
--num_height 480
--num_width 832
--num_frames 81
--enable_gradient_checkpointing_type "full"
)
# Parallel arguments
parallel_args=(
--num_gpus 64
--sp_size 4
--tp_size 1
--hsdp_replicate_dim 8
--hsdp_shard_dim 8
)
# Model arguments
model_args=(
--model_path $MODEL_PATH
--pretrained_model_name_or_path $MODEL_PATH
--real_score_model_path $REAL_SCORE_MODEL_PATH
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
)
# Dataset arguments
dataset_args=(
--data_path "$DATA_DIR"
--dataloader_num_workers 4
)
# Validation arguments
validation_args=(
--log_validation
--validation_dataset_file "$VALIDATION_DATASET_FILE"
--validation_steps 200
--validation_sampling_steps "3"
--validation_guidance_scale "6.0" # not used for dmd inference
)
# Optimizer arguments
optimizer_args=(
--learning_rate 2e-6
--mixed_precision "bf16"
--training_state_checkpointing_steps 500
--weight_only_checkpointing_steps 500
--weight_decay 0.01
--max_grad_norm 1.0
)
# Miscellaneous arguments
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--ema_start_step 0
--flow_shift 3
--seed 1000
)
# DMD arguments
dmd_args=(
--dmd_denoising_steps '1000,757,522'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--generator_update_interval 5
--real_score_guidance_scale 3.5
--VSA_sparsity 0.9
)
srun torchrun \
--nnodes $SLURM_JOB_NUM_NODES \
--nproc_per_node $NUM_GPUS \
@@ -132,11 +54,10 @@ srun torchrun \
--rdzv_backend=c10d \
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
fastvideo/training/wan_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}"
--config examples/distill/Wan2.1-T2V/Wan-Syn-Data-480P/distill_dmd_VSA_t2v_14B.yaml \
--model_path "$MODEL_PATH" \
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
--training.data.data_path "$DATA_DIR" \
--training.checkpoint.output_dir "$OUTPUT_DIR" \
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
@@ -0,0 +1,55 @@
# Read by distill_dmd_VSA_t2v_14B.slurm, which passes model_path, training.distillation.real_score_model_path,
# training.distillation.fake_score_model_path, training.data.data_path, training.checkpoint.output_dir, and
# training.validation.dataset_file on the command line.
engine:
num_gpus: 64
parallelism:
tp_size: 1
sp_size: 4
hsdp_replicate_dim: 8
hsdp_shard_dim: 8
attention:
vsa_sparsity: 0.9
precision:
dit: fp32
pipeline:
flow_shift: 3.0
dmd_denoising_steps: [1000, 757, 522]
training:
data:
dataloader_num_workers: 4
num_height: 480
num_width: 832
num_frames: 81
train_batch_size: 1
num_latent_t: 21
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 2.0e-06
weight_decay: 0.01
max_grad_norm: 1.0
loop:
max_train_steps: 4000
gradient_accumulation_steps: 1
checkpoint:
training_state_checkpointing_steps: 500
weight_only_checkpointing_steps: 500
checkpoints_total_limit: 3
tracker:
project_name: wan_t2v_distill_dmd_VSA
validation:
enabled: true
sampling_steps: [3]
guidance_scale: 6.0 # not used for dmd inference
every_steps: 200
distillation:
generator_update_interval: 5
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.5
ema:
start_step: 0
model:
enable_gradient_checkpointing_type: full
@@ -47,83 +47,6 @@ OUTPUT_DIR="checkpoints/wan_t2v_finetune"
# export CUDA_VISIBLE_DEVICES=4,5
# IP=[MASTER NODE IP]
# Training arguments
training_args=(
--tracker_project_name wan_t2v_distill_dmd
--output_dir "$OUTPUT_DIR"
--max_train_steps 4000
--train_batch_size 1
--train_sp_batch_size 1
--gradient_accumulation_steps 1
--num_latent_t 21
--num_height 480
--num_width 832
--num_frames 81
--enable_gradient_checkpointing_type "full"
)
# Parallel arguments
parallel_args=(
--num_gpus 64
--sp_size 1
--tp_size 1
--hsdp_replicate_dim 64
--hsdp_shard_dim 1
)
# Model arguments
model_args=(
--model_path $MODEL_PATH
--pretrained_model_name_or_path $MODEL_PATH
--real_score_model_path $REAL_SCORE_MODEL_PATH
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
)
# Dataset arguments
dataset_args=(
--data_path "$DATA_DIR"
--dataloader_num_workers 4
)
# Validation arguments
validation_args=(
--log_validation
--validation_dataset_file "$VALIDATION_DATASET_FILE"
--validation_steps 200
--validation_sampling_steps "3"
--validation_guidance_scale "6.0" # not used for dmd inference
)
# Optimizer arguments
optimizer_args=(
--learning_rate 2e-6
--mixed_precision "bf16"
--training_state_checkpointing_steps 500
--weight_only_checkpointing_steps 500
--weight_decay 0.01
--max_grad_norm 1.0
)
# Miscellaneous arguments
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--ema_start_step 0
--flow_shift 8
--seed 1000
)
# DMD arguments
dmd_args=(
--dmd_denoising_steps '1000,757,522'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--generator_update_interval 5
--real_score_guidance_scale 3.5
)
srun torchrun \
--nnodes $SLURM_JOB_NUM_NODES \
--nproc_per_node $NUM_GPUS \
@@ -131,11 +54,10 @@ srun torchrun \
--rdzv_backend=c10d \
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
fastvideo/training/wan_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}"
--config examples/distill/Wan2.1-T2V/Wan-Syn-Data-480P/distill_dmd_t2v_1.3B.yaml \
--model_path "$MODEL_PATH" \
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
--training.data.data_path "$DATA_DIR" \
--training.checkpoint.output_dir "$OUTPUT_DIR" \
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
@@ -0,0 +1,53 @@
# Read by distill_dmd_t2v_1.3B.slurm, which passes model_path, training.distillation.real_score_model_path,
# training.distillation.fake_score_model_path, training.data.data_path, training.checkpoint.output_dir, and
# training.validation.dataset_file on the command line.
engine:
num_gpus: 64
parallelism:
tp_size: 1
sp_size: 1
hsdp_replicate_dim: 64
hsdp_shard_dim: 1
precision:
dit: fp32
pipeline:
flow_shift: 8.0
dmd_denoising_steps: [1000, 757, 522]
training:
data:
dataloader_num_workers: 4
num_height: 480
num_width: 832
num_frames: 81
train_batch_size: 1
num_latent_t: 21
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 2.0e-06
weight_decay: 0.01
max_grad_norm: 1.0
loop:
max_train_steps: 4000
gradient_accumulation_steps: 1
checkpoint:
training_state_checkpointing_steps: 500
weight_only_checkpointing_steps: 500
checkpoints_total_limit: 3
tracker:
project_name: wan_t2v_distill_dmd
validation:
enabled: true
sampling_steps: [3]
guidance_scale: 6.0 # not used for dmd inference
every_steps: 200
distillation:
generator_update_interval: 5
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.5
ema:
start_step: 0
model:
enable_gradient_checkpointing_type: full
@@ -8,4 +8,4 @@ uv pip install vsa
```
### Data-free Distillation
When `--simulate_generator_forward` is enabled, distillation becomes data-free by simulating intermediate steps through forward inference of the generator. This helps avoid training–inference mismatch. See Section 4.5 of [DMD2](https://arxiv.org/pdf/2405.14867) for details.
When `training.distillation.simulate_generator_forward` is enabled, distillation becomes data-free by simulating intermediate steps through forward inference of the generator. This helps avoid training–inference mismatch. See Section 4.5 of [DMD2](https://arxiv.org/pdf/2405.14867) for details.
@@ -48,90 +48,6 @@ OUTPUT_DIR="checkpoints/wan_t2v_finetune"
# export CUDA_VISIBLE_DEVICES=4,5
# IP=[MASTER NODE IP]
# Training arguments
training_args=(
--tracker_project_name Wan_distillation
--output_dir "$OUTPUT_DIR"
--max_train_steps 4000
--train_batch_size 1
--train_sp_batch_size 1
--gradient_accumulation_steps 1
--num_latent_t 31
--num_height 704
--num_width 1280
--num_frames 121
--enable_gradient_checkpointing_type "full"
)
# Parallel arguments
parallel_args=(
--num_gpus 64
--sp_size 1
--tp_size 1
--hsdp_replicate_dim 64
--hsdp_shard_dim 1
)
# Model arguments
model_args=(
--model_path $MODEL_PATH
--pretrained_model_name_or_path $MODEL_PATH
--real_score_model_path $REAL_SCORE_MODEL_PATH
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
)
# Dataset arguments
dataset_args=(
--data_path "$DATA_DIR"
--dataloader_num_workers 4
)
# Validation arguments
validation_args=(
--log_validation
--validation_dataset_file "$VALIDATION_DIR"
--validation_steps 200
--validation_sampling_steps "3"
--validation_guidance_scale "6.0" # not used for dmd inference
)
# Optimizer arguments
optimizer_args=(
--learning_rate 4e-6
--lr_scheduler "cosine_with_min_lr"
--min_lr_ratio 0.5
--lr_warmup_steps 100
--fake_score_learning_rate 2e-6
--fake_score_lr_scheduler "cosine_with_min_lr"
--mixed_precision "bf16"
--training_state_checkpointing_steps 500
--weight_only_checkpointing_steps 200
--weight_decay 0.01
--max_grad_norm 1.0
)
# Miscellaneous arguments
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--ema_start_step 0
--flow_shift 5
--seed 1000
)
# DMD arguments
dmd_args=(
--dmd_denoising_steps '1000,757,522'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--generator_update_interval 5
--real_score_guidance_scale 3
--simulate_generator_forward
--log_visualization # disable if oom
)
srun torchrun \
--nnodes $SLURM_JOB_NUM_NODES \
--nproc_per_node $NUM_GPUS \
@@ -139,11 +55,10 @@ srun torchrun \
--rdzv_backend=c10d \
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
fastvideo/training/wan_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}"
--config examples/distill/Wan2.2-TI2V-5B-Diffusers/Data-free/distill_dmd_t2v_5B.yaml \
--model_path "$MODEL_PATH" \
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
--training.data.data_path "$DATA_DIR" \
--training.checkpoint.output_dir "$OUTPUT_DIR" \
--training.validation.dataset_file "$VALIDATION_DIR"
@@ -0,0 +1,60 @@
# Read by distill_dmd_t2v_5B.sh, which passes model_path, training.distillation.real_score_model_path,
# training.distillation.fake_score_model_path, training.data.data_path, training.checkpoint.output_dir, and
# training.validation.dataset_file on the command line.
engine:
num_gpus: 64
parallelism:
tp_size: 1
sp_size: 1
hsdp_replicate_dim: 64
hsdp_shard_dim: 1
precision:
dit: fp32
pipeline:
flow_shift: 5.0
dmd_denoising_steps: [1000, 757, 522]
training:
data:
dataloader_num_workers: 4
num_height: 704
num_width: 1280
num_frames: 121
train_batch_size: 1
num_latent_t: 31
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 4.0e-06
weight_decay: 0.01
lr_scheduler: cosine_with_min_lr
lr_warmup_steps: 100
min_lr_ratio: 0.5
max_grad_norm: 1.0
loop:
max_train_steps: 4000
gradient_accumulation_steps: 1
checkpoint:
training_state_checkpointing_steps: 500
weight_only_checkpointing_steps: 200
checkpoints_total_limit: 3
tracker:
project_name: Wan_distillation
validation:
enabled: true
sampling_steps: [3]
guidance_scale: 6.0 # not used for dmd inference
every_steps: 200
log_visualization: true # disable if oom
distillation:
generator_update_interval: 5
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.0
fake_score_learning_rate: 2.0e-06
fake_score_lr_scheduler: cosine_with_min_lr
simulate_generator_forward: true
ema:
start_step: 0
model:
enable_gradient_checkpointing_type: full
@@ -47,91 +47,6 @@ VALIDATION_DIR=your_validation_path #(example:validation_64.json)
# export CUDA_VISIBLE_DEVICES=4,5
# IP=[MASTER NODE IP]
# Training arguments
training_args=(
--tracker_project_name Wan_distillation
--output_dir "your_output_dir"
--max_train_steps 4000
--train_batch_size 1
--train_sp_batch_size 1
--gradient_accumulation_steps 1
--num_latent_t 31
--num_height 704
--num_width 1280
--num_frames 121
--enable_gradient_checkpointing_type "full"
)
# Parallel arguments
parallel_args=(
--num_gpus 64
--sp_size 1
--tp_size 1
--hsdp_replicate_dim 64
--hsdp_shard_dim 1
)
# Model arguments
model_args=(
--model_path $MODEL_PATH
--pretrained_model_name_or_path $MODEL_PATH
--real_score_model_path $REAL_SCORE_MODEL_PATH
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
)
# Dataset arguments
dataset_args=(
--data_path "$DATA_DIR"
--dataloader_num_workers 4
)
# Validation arguments
validation_args=(
--log_validation
--validation_dataset_file "$VALIDATION_DIR"
--validation_steps 200
--validation_sampling_steps "3"
--validation_guidance_scale "6.0" # not used for dmd inference
)
# Optimizer arguments
optimizer_args=(
--learning_rate 4e-6
--lr_scheduler "cosine_with_min_lr"
--min_lr_ratio 0.5
--lr_warmup_steps 100
--fake_score_learning_rate 2e-6
--fake_score_lr_scheduler "cosine_with_min_lr"
--mixed_precision "bf16"
--training_state_checkpointing_steps 500
--weight_only_checkpointing_steps 200
--weight_decay 0.01
--max_grad_norm 1.0
)
# Miscellaneous arguments
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--ema_start_step 0
--flow_shift 5
--seed 1000
)
# DMD arguments
dmd_args=(
--dmd_denoising_steps '1000,757,522'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--generator_update_interval 5
--real_score_guidance_scale 3
--simulate_generator_forward
--log_visualization # disable if oom
--VSA_sparsity 0.8
)
srun torchrun \
--nnodes $SLURM_JOB_NUM_NODES \
--nproc_per_node $NUM_GPUS \
@@ -139,11 +54,9 @@ srun torchrun \
--rdzv_backend=c10d \
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
fastvideo/training/wan_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}"
--config examples/distill/Wan2.2-TI2V-5B-Diffusers/Data-free/distill_dmd_t2v_5B_VSA.yaml \
--model_path "$MODEL_PATH" \
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
--training.data.data_path "$DATA_DIR" \
--training.validation.dataset_file "$VALIDATION_DIR"
@@ -0,0 +1,63 @@
# Read by distill_dmd_t2v_5B_VSA.sh, which passes model_path, training.distillation.real_score_model_path,
# training.distillation.fake_score_model_path, training.data.data_path, and training.validation.dataset_file on the
# command line.
engine:
num_gpus: 64
parallelism:
tp_size: 1
sp_size: 1
hsdp_replicate_dim: 64
hsdp_shard_dim: 1
attention:
vsa_sparsity: 0.8
precision:
dit: fp32
pipeline:
flow_shift: 5.0
dmd_denoising_steps: [1000, 757, 522]
training:
data:
dataloader_num_workers: 4
num_height: 704
num_width: 1280
num_frames: 121
train_batch_size: 1
num_latent_t: 31
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 4.0e-06
weight_decay: 0.01
lr_scheduler: cosine_with_min_lr
lr_warmup_steps: 100
min_lr_ratio: 0.5
max_grad_norm: 1.0
loop:
max_train_steps: 4000
gradient_accumulation_steps: 1
checkpoint:
output_dir: your_output_dir
training_state_checkpointing_steps: 500
weight_only_checkpointing_steps: 200
checkpoints_total_limit: 3
tracker:
project_name: Wan_distillation
validation:
enabled: true
sampling_steps: [3]
guidance_scale: 6.0 # not used for dmd inference
every_steps: 200
log_visualization: true # disable if oom
distillation:
generator_update_interval: 5
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.0
fake_score_learning_rate: 2.0e-06
fake_score_lr_scheduler: cosine_with_min_lr
simulate_generator_forward: true
ema:
start_step: 0
model:
enable_gradient_checkpointing_type: full
@@ -22,94 +22,15 @@ OUTPUT_DIR="checkpoints/wan_t2v_finetune"
# export CUDA_VISIBLE_DEVICES=4,5
# IP=[MASTER NODE IP]
# Training arguments
training_args=(
--tracker_project_name wan_t2v_distill_dmd_VSA
--output_dir "$OUTPUT_DIR"
--max_train_steps 4000
--train_batch_size 1
--train_sp_batch_size 1
--gradient_accumulation_steps 1
--num_latent_t 31
--num_height 704
--num_width 1280
--num_frames 121
--enable_gradient_checkpointing_type "full"
--training_state_checkpointing_steps 500
--weight_only_checkpointing_steps 500
)
# Parallel arguments
parallel_args=(
--num_gpus 1
--sp_size 1
--tp_size 1
--hsdp_replicate_dim 1
--hsdp_shard_dim 1
)
# Model arguments
model_args=(
--model_path $MODEL_PATH
--pretrained_model_name_or_path $MODEL_PATH
--real_score_model_path $REAL_SCORE_MODEL_PATH
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
)
# Dataset arguments
dataset_args=(
--data_path "$DATA_DIR"
--dataloader_num_workers 4
)
# Validation arguments
validation_args=(
--log_validation
--validation_dataset_file "$VALIDATION_DATASET_FILE"
--validation_steps 200
--validation_sampling_steps "3"
--validation_guidance_scale "6.0" # not used for dmd inference
)
# Optimizer arguments
optimizer_args=(
--learning_rate 2e-6
--mixed_precision "bf16"
--weight_decay 0.01
--max_grad_norm 1.0
)
# Miscellaneous arguments
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--ema_start_step 0
--flow_shift 8
--seed 1000
)
# DMD arguments
dmd_args=(
--dmd_denoising_steps '1000,757,522'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--generator_update_interval 5
--real_score_guidance_scale 3.5
--VSA_sparsity 0.8
)
torchrun \
--nnodes 1 \
--nproc_per_node $NUM_GPUS \
--master_port $MASTER_PORT \
fastvideo/training/wan_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}"
--config examples/distill/Wan2.2-TI2V-5B-Diffusers/crush_smol/distill_dmd_VSA_t2v_5B.yaml \
--model_path "$MODEL_PATH" \
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
--training.data.data_path "$DATA_DIR" \
--training.checkpoint.output_dir "$OUTPUT_DIR" \
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
@@ -0,0 +1,55 @@
# Read by distill_dmd_VSA_t2v_5B.sh, which passes model_path, training.distillation.real_score_model_path,
# training.distillation.fake_score_model_path, training.data.data_path, training.checkpoint.output_dir, and
# training.validation.dataset_file on the command line.
engine:
num_gpus: 1
parallelism:
tp_size: 1
sp_size: 1
hsdp_replicate_dim: 1
hsdp_shard_dim: 1
attention:
vsa_sparsity: 0.8
precision:
dit: fp32
pipeline:
flow_shift: 8.0
dmd_denoising_steps: [1000, 757, 522]
training:
data:
dataloader_num_workers: 4
num_height: 704
num_width: 1280
num_frames: 121
train_batch_size: 1
num_latent_t: 31
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 2.0e-06
weight_decay: 0.01
max_grad_norm: 1.0
loop:
max_train_steps: 4000
gradient_accumulation_steps: 1
checkpoint:
training_state_checkpointing_steps: 500
weight_only_checkpointing_steps: 500
checkpoints_total_limit: 3
tracker:
project_name: wan_t2v_distill_dmd_VSA
validation:
enabled: true
sampling_steps: [3]
guidance_scale: 6.0 # not used for dmd inference
every_steps: 200
distillation:
generator_update_interval: 5
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.5
ema:
start_step: 0
model:
enable_gradient_checkpointing_type: full
@@ -21,96 +21,14 @@ VALIDATION_DATASET_FILE="examples/distill/Wan2.2-TI2V-5B-Diffusers/crush_smol/va
# export CUDA_VISIBLE_DEVICES=4,5
# IP=[MASTER NODE IP]
# Training arguments
training_args=(
--tracker_project_name wan_t2v_distill_dmd_VSA
--output_dir="checkpoints/wan_t2v_finetune"
--max_train_steps=4000
--train_batch_size=1
--train_sp_batch_size 1
--gradient_accumulation_steps=1
--num_latent_t 31
--num_height 704
--num_width 1280
--num_frames 121
--enable_gradient_checkpointing_type "full"
--training_state_checkpointing_steps=500
--weight_only_checkpointing_steps=500
--lora_rank 32
--lora_training True
)
# Parallel arguments
parallel_args=(
--num_gpus 1
--sp_size 1
--tp_size 1
--hsdp_replicate_dim 1
--hsdp_shard_dim 1
)
# Model arguments
model_args=(
--model_path $MODEL_PATH
--pretrained_model_name_or_path $MODEL_PATH
--real_score_model_path $REAL_SCORE_MODEL_PATH
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
)
# Dataset arguments
dataset_args=(
--data_path "$DATA_DIR"
--dataloader_num_workers 4
)
# Validation arguments
validation_args=(
--log_validation
--validation_dataset_file "$VALIDATION_DATASET_FILE"
--validation_steps 200
--validation_sampling_steps "3"
--validation_guidance_scale "6.0" # not used for dmd inference
)
# Optimizer arguments
optimizer_args=(
--learning_rate=1e-4
--mixed_precision="bf16"
--weight_decay 0.01
--max_grad_norm 1.0
)
# Miscellaneous arguments
miscellaneous_args=(
--inference_mode False
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--ema_start_step 0
--flow_shift 8
--seed 1000
)
# DMD arguments
dmd_args=(
--dmd_denoising_steps '1000,757,522'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--generator_update_interval 5
--real_score_guidance_scale 3.5
--VSA_sparsity 0.8
)
torchrun \
--nnodes 1 \
--nproc_per_node $NUM_GPUS \
--master_port $MASTER_PORT \
fastvideo/training/wan_distillation_pipeline.py \
"${parallel_args[@]}" \
"${model_args[@]}" \
"${dataset_args[@]}" \
"${training_args[@]}" \
"${optimizer_args[@]}" \
"${validation_args[@]}" \
"${miscellaneous_args[@]}" \
"${dmd_args[@]}"
--config examples/distill/Wan2.2-TI2V-5B-Diffusers/crush_smol/distill_dmd_VSA_t2v_5B_lora.yaml \
--model_path "$MODEL_PATH" \
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
--training.data.data_path "$DATA_DIR" \
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
@@ -0,0 +1,59 @@
# Read by distill_dmd_VSA_t2v_5B_lora.sh, which passes model_path, training.distillation.real_score_model_path,
# training.distillation.fake_score_model_path, training.data.data_path, and training.validation.dataset_file on the
# command line.
engine:
num_gpus: 1
parallelism:
tp_size: 1
sp_size: 1
hsdp_replicate_dim: 1
hsdp_shard_dim: 1
attention:
vsa_sparsity: 0.8
precision:
dit: fp32
pipeline:
flow_shift: 8.0
dmd_denoising_steps: [1000, 757, 522]
training:
data:
dataloader_num_workers: 4
num_height: 704
num_width: 1280
num_frames: 121
train_batch_size: 1
num_latent_t: 31
training_cfg_rate: 0.0
seed: 1000
train_sp_batch_size: 1
optimizer:
learning_rate: 0.0001
weight_decay: 0.01
max_grad_norm: 1.0
loop:
max_train_steps: 4000
gradient_accumulation_steps: 1
checkpoint:
output_dir: checkpoints/wan_t2v_finetune
training_state_checkpointing_steps: 500
weight_only_checkpointing_steps: 500
checkpoints_total_limit: 3
tracker:
project_name: wan_t2v_distill_dmd_VSA
validation:
enabled: true
sampling_steps: [3]
guidance_scale: 6.0 # not used for dmd inference
every_steps: 200
distillation:
generator_update_interval: 5
min_timestep_ratio: 0.02
max_timestep_ratio: 0.98
real_score_guidance_scale: 3.5
ema:
start_step: 0
lora:
enabled: true
rank: 32
model:
enable_gradient_checkpointing_type: full
@@ -8,17 +8,7 @@ OUTPUT_DIR="data/crush-smol_processed_ti2v/"
torchrun --nproc_per_node=$GPU_NUM \
fastvideo/pipelines/preprocess/v1_preprocess.py \
--config examples/distill/Wan2.2-TI2V-5B-Diffusers/crush_smol/preprocess_wan_data_ti2v_5b.yaml \
--model_path $MODEL_PATH \
--data_merge_path $DATA_MERGE_PATH \
--preprocess_video_batch_size 8 \
--seed 42 \
--max_height 704 \
--max_width 1280 \
--num_frames 121 \
--dataloader_num_workers 0 \
--output_dir=$OUTPUT_DIR \
--train_fps 24 \
--samples_per_file 8 \
--flush_frequency 8 \
--video_length_tolerance_range 5 \
--preprocess_task "t2v"
--preprocess.data_merge_path $DATA_MERGE_PATH \
--preprocess.dataset_output_dir=$OUTPUT_DIR
@@ -0,0 +1,20 @@
# Read by preprocess_wan_data_ti2v_5b.sh, which passes model_path, preprocess.data_merge_path, and
# preprocess.dataset_output_dir.
# Offload settings of the per-task preprocessing pipelines (v1_preprocess.py).
engine:
offload:
dit_layerwise: true
image_encoder: true
pin_cpu_memory: true
preprocess:
preprocess_video_batch_size: 8
seed: 42
max_height: 704
max_width: 1280
num_frames: 121
dataloader_num_workers: 0
train_fps: 24
samples_per_file: 8
flush_frequency: 8
video_length_tolerance_range: 5
preprocess_task: t2v
+2 -2
View File
@@ -177,13 +177,13 @@ from fastvideo import VideoGenerator
def main():
generator = VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
num_gpus=1,
{"engine": {"num_gpus": 1}},
)
prompt = ("A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes "
"wide with interest. The playful yet serene atmosphere is complemented by soft "
"natural light filtering through the petals. Mid-shot, warm and cheerful tones.")
video = generator.generate_video(prompt)
video = generator.generate({"prompt": prompt})
if __name__ == "__main__":
main()
+27 -15
View File
@@ -1,7 +1,5 @@
from fastvideo import VideoGenerator
# from fastvideo.api.sampling_param import SamplingParam
OUTPUT_PATH = "video_samples"
@@ -13,24 +11,35 @@ def main():
generator = VideoGenerator.from_pretrained(
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
# FastVideo will automatically handle distributed setup
num_gpus=1,
use_fsdp_inference=False, # set to True if GPU is out of memory
dit_cpu_offload=False,
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
pin_cpu_memory=True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
# image_encoder_cpu_offload=False,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set to True if GPU is out of memory
"offload": {
"dit": False,
"vae": False,
"text_encoder": True,
"pin_cpu_memory": True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
# "image_encoder": False,
},
},
},
)
# sampling_param = SamplingParam.from_pretrained("Wan-AI/Wan2.1-T2V-1.3B-Diffusers")
# sampling_param.num_frames = 45
# sampling_param.image_path = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/astronaut.jpg"
# Generate videos with the same simple API, regardless of GPU count
prompt = ("A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes "
"wide with interest. The playful yet serene atmosphere is complemented by soft "
"natural light filtering through the petals. Mid-shot, warm and cheerful tones.")
video = generator.generate_video(prompt, output_path=OUTPUT_PATH, save_video=True)
# video = generator.generate_video(prompt, sampling_param=sampling_param, output_path="wan_t2v_videos/")
video = generator.generate({
"prompt": prompt,
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
# video = generator.generate({
# "prompt": prompt,
# "inputs": {"image_path": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/astronaut.jpg"},
# "sampling": {"num_frames": 45},
# "output": {"output_path": "wan_t2v_videos/"},
# })
# Generate another video with a different prompt, without reloading the
# model!
@@ -39,7 +48,10 @@ def main():
"the breeze, enhancing the lion's commanding presence. The tone is vibrant, "
"embodying the raw energy of the wild. Low angle, steady tracking shot, "
"cinematic.")
video2 = generator.generate_video(prompt2, output_path=OUTPUT_PATH, save_video=True)
video2 = generator.generate({
"prompt": prompt2,
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
if __name__ == "__main__":
+18 -17
View File
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
from fastvideo import VideoGenerator
from fastvideo.api.sampling_param import SamplingParam
def main():
@@ -9,16 +8,20 @@ def main():
generator = VideoGenerator.from_pretrained(
model_path,
num_gpus=1,
use_fsdp_inference=False, # set True if GPU is out of memory
dit_cpu_offload=False,
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set True if GPU is out of memory
"offload": {
"dit": False,
"vae": False,
"text_encoder": True,
"pin_cpu_memory": True,
},
},
},
)
sampling_param = SamplingParam.from_pretrained(model_path)
# image2world example from official repo
image_path = "assets/images/bus_terminal.jpg"
@@ -32,14 +35,12 @@ def main():
"It then comes to a smooth stop, resuming its position in line. "
"Overhead signage in Chinese characters remains illuminated, enhancing the vibrant, urban night scene.")
generator.generate_video(
prompt,
sampling_param=sampling_param,
image_path=str(image_path),
num_cond_frames=1,
output_path="outputs_video/cosmos2_5_i2w.mp4",
save_video=True,
)
generator.generate({
"prompt": prompt,
"inputs": {"image_path": str(image_path)},
"output": {"output_path": "outputs_video/cosmos2_5_i2w.mp4", "save_video": True},
"extensions": {"num_cond_frames": 1},
})
generator.shutdown()
+16 -16
View File
@@ -1,5 +1,4 @@
from fastvideo import VideoGenerator
from fastvideo.api.sampling_param import SamplingParam
def main():
@@ -8,17 +7,20 @@ def main():
generator = VideoGenerator.from_pretrained(
model_path,
num_gpus=1,
use_fsdp_inference=False, # set True if GPU is out of memory
dit_cpu_offload=False,
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set True if GPU is out of memory
"offload": {
"dit": False,
"vae": False,
"text_encoder": True,
"pin_cpu_memory": True,
},
},
},
)
# Load default sampling parameters (negative_prompt, resolution, steps, etc.)
sampling_param = SamplingParam.from_pretrained(model_path)
prompt = (
"A high-definition video captures the precision of robotic welding in an industrial setting. "
"The first frame showcases a robotic arm, equipped with a welding torch, positioned over a large metal structure. "
@@ -33,12 +35,10 @@ def main():
"The scene retains its industrial ambiance, with the welding sparks and smoke dominating the visual field, "
"underscoring the ongoing nature of the welding operation.")
generator.generate_video(
prompt,
sampling_param=sampling_param,
output_path="outputs_video/cosmos2_5_t2w.mp4",
save_video=True,
)
generator.generate({
"prompt": prompt,
"output": {"output_path": "outputs_video/cosmos2_5_t2w.mp4", "save_video": True},
})
generator.shutdown()
+18 -17
View File
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
from fastvideo import VideoGenerator
from fastvideo.api.sampling_param import SamplingParam
def main():
@@ -9,16 +8,20 @@ def main():
generator = VideoGenerator.from_pretrained(
model_path,
num_gpus=1,
use_fsdp_inference=False, # set True if GPU is out of memory
dit_cpu_offload=False,
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set True if GPU is out of memory
"offload": {
"dit": False,
"vae": False,
"text_encoder": True,
"pin_cpu_memory": True,
},
},
},
)
sampling_param = SamplingParam.from_pretrained(model_path)
# video2world example from official repo
video_path = "assets/videos/robot_pouring.mp4"
@@ -36,14 +39,12 @@ def main():
"The final frame captures the robotic arm with the pitcher finishing the pour, with the glass now filled to a higher level, while the pitcher is slightly tilted but still held securely by the gripper."
)
generator.generate_video(
prompt,
sampling_param=sampling_param,
video_path=str(video_path),
num_cond_frames=1,
output_path="outputs_video/cosmos2_5_v2w.mp4",
save_video=True,
)
generator.generate({
"prompt": prompt,
"inputs": {"video_path": str(video_path)},
"output": {"output_path": "outputs_video/cosmos2_5_v2w.mp4", "save_video": True},
"extensions": {"num_cond_frames": 1},
})
generator.shutdown()
+26 -19
View File
@@ -2,8 +2,6 @@ import os
import time
from fastvideo import VideoGenerator
from fastvideo.api.sampling_param import SamplingParam
OUTPUT_PATH = "video_samples_dmd2"
@@ -12,29 +10,34 @@ def main():
load_start_time = time.perf_counter()
model_name = "FastVideo/FastWan2.1-T2V-1.3B-Diffusers"
generator = VideoGenerator.from_pretrained(
model_name,
# FastVideo will automatically handle distributed setup
num_gpus=1,
use_fsdp_inference=False, # set to True if GPU is out of memory
# Adjust these offload parameters if you have < 32GB of VRAM
text_encoder_cpu_offload=True,
pin_cpu_memory=True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
dit_cpu_offload=False,
vae_cpu_offload=False,
VSA_sparsity=0.8,
)
generator = VideoGenerator.from_config({
"model_path": model_name,
"engine": {
# FastVideo will automatically handle distributed setup
"num_gpus": 1,
"use_fsdp_inference": False, # set to True if GPU is out of memory
# Adjust these offload parameters if you have < 32GB of VRAM
"offload": {
"text_encoder": True,
"pin_cpu_memory": True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
"dit": False,
"vae": False,
},
"attention": {"vsa_sparsity": 0.8},
},
})
load_end_time = time.perf_counter()
load_time = load_end_time - load_start_time
sampling_param = SamplingParam.from_pretrained(model_name)
sampling_param.num_frames = 81
prompt = (
"A neon-lit alley in futuristic Tokyo during a heavy rainstorm at night. The puddles reflect glowing signs in kanji, advertising ramen, karaoke, and VR arcades. A woman in a translucent raincoat walks briskly with an LED umbrella. Steam rises from a street food cart, and a cat darts across the screen. Raindrops are visible on the camera lens, creating a cinematic bokeh effect."
)
start_time = time.perf_counter()
video = generator.generate_video(prompt, output_path=OUTPUT_PATH, save_video=True, sampling_param=sampling_param)
video = generator.generate({
"prompt": prompt,
"sampling": {"num_frames": 81},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
end_time = time.perf_counter()
gen_time = end_time - start_time
@@ -46,7 +49,11 @@ def main():
"embodying the raw energy of the wild. Low angle, steady tracking shot, "
"cinematic.")
start_time = time.perf_counter()
video2 = generator.generate_video(prompt2, output_path=OUTPUT_PATH, save_video=True, num_frames=81)
video2 = generator.generate({
"prompt": prompt2,
"sampling": {"num_frames": 81},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
end_time = time.perf_counter()
gen_time2 = end_time - start_time
@@ -3,12 +3,12 @@ import time
from fastvideo import VideoGenerator
from fastvideo.api import (
AttentionConfig,
EngineConfig,
GenerationRequest,
GeneratorConfig,
OffloadConfig,
OutputConfig,
PipelineSelection,
)
OUTPUT_PATH = "video_samples_dmd2_typed"
@@ -29,12 +29,8 @@ def main():
dit=False,
vae=False,
),
attention=AttentionConfig(vsa_sparsity=0.8),
),
# PR 2 still routes a few advanced inference knobs through the
# compatibility bridge until they get first-class typed fields.
pipeline=PipelineSelection(experimental={
"VSA_sparsity": 0.8,
}, ),
)
load_start_time = time.perf_counter()
+34 -23
View File
@@ -15,16 +15,20 @@ def _env_float(name: str, default: float) -> float:
def main():
model_name = os.getenv("DREAMX_WORLD_MODEL_DIR", "FastVideo/DreamX-World-5B-Cam-Diffusers")
generator = VideoGenerator.from_pretrained(
model_name,
num_gpus=1,
use_fsdp_inference=False,
dit_cpu_offload=False,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=False,
override_pipeline_cls_name="DreamXWorldPipeline",
)
generator = VideoGenerator.from_config({
"model_path": model_name,
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False,
"offload": {
"dit": False,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": False,
},
},
"pipeline": {"components": {"override_pipeline_cls_name": "DreamXWorldPipeline"}},
})
prompt = os.getenv(
"DREAMX_WORLD_PROMPT",
@@ -36,23 +40,30 @@ def main():
"https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/wan_i2v_input.JPG",
)
kwargs = {
"output_path": OUTPUT_PATH,
"save_video": os.getenv("DREAMX_WORLD_SAVE_VIDEO", "1") != "0",
"height": _env_int("DREAMX_WORLD_HEIGHT", 480),
"width": _env_int("DREAMX_WORLD_WIDTH", 832),
"num_frames": _env_int("DREAMX_WORLD_NUM_FRAMES", 161),
"num_inference_steps": _env_int("DREAMX_WORLD_STEPS", 30),
"guidance_scale": _env_float("DREAMX_WORLD_GUIDANCE", 5.0),
"action_list": os.getenv("DREAMX_WORLD_ACTIONS", "w,d,w").split(","),
"action_speed_list":
[float(value) for value in os.getenv("DREAMX_WORLD_ACTION_SPEEDS", "4.0,2.0,4.0").split(",")],
request = {
"prompt": prompt,
"sampling": {
"height": _env_int("DREAMX_WORLD_HEIGHT", 480),
"width": _env_int("DREAMX_WORLD_WIDTH", 832),
"num_frames": _env_int("DREAMX_WORLD_NUM_FRAMES", 161),
"num_inference_steps": _env_int("DREAMX_WORLD_STEPS", 30),
"guidance_scale": _env_float("DREAMX_WORLD_GUIDANCE", 5.0),
},
"output": {
"output_path": OUTPUT_PATH,
"save_video": os.getenv("DREAMX_WORLD_SAVE_VIDEO", "1") != "0",
},
"extensions": {
"action_list": os.getenv("DREAMX_WORLD_ACTIONS", "w,d,w").split(","),
"action_speed_list":
[float(value) for value in os.getenv("DREAMX_WORLD_ACTION_SPEEDS", "4.0,2.0,4.0").split(",")],
},
}
if image_path:
kwargs["image_path"] = image_path
request["inputs"] = {"image_path": image_path}
try:
generator.generate_video(prompt, **kwargs)
generator.generate(request)
finally:
generator.shutdown()
+15 -18
View File
@@ -25,11 +25,13 @@ from pathlib import Path
from fastvideo import VideoGenerator
from fastvideo.api import (
AttentionConfig,
CompileConfig,
ComponentConfig,
EngineConfig,
GenerationRequest,
GeneratorConfig,
MiniMaxH3Options,
OffloadConfig,
OutputConfig,
ParallelismConfig,
@@ -254,23 +256,6 @@ def _execution_backend(args: argparse.Namespace) -> str:
def build_generator_config(args: argparse.Namespace) -> GeneratorConfig:
use_vsa = _uses_vsa(args)
experimental: dict[str, object] = {
"attention_backend": "VIDEO_SPARSE_ATTN_H3" if use_vsa else "FLASH_ATTN",
"inference_torch_compile": args.inference_torch_compile,
"vae_parallel_decode": args.parallel_vae,
"vae_parallel_decode_strategy": "gather",
}
if args.h3_sequential_load is not None:
experimental["h3_sequential_load"] = args.h3_sequential_load
if args.video_decode_backend != "h3-vae":
experimental["video_decode_backend"] = args.video_decode_backend
if args.taeh3_checkpoint is not None:
experimental["taeh3_checkpoint"] = args.taeh3_checkpoint
if use_vsa:
experimental.update({
"VSA_sparsity": args.vsa_sparsity,
"VSA_tile_size": args.vsa_tile_size,
})
return GeneratorConfig(
model_path=args.model_path,
pipeline=PipelineSelection(
@@ -278,7 +263,13 @@ def build_generator_config(args: argparse.Namespace) -> GeneratorConfig:
lora_path=getattr(args, "lora_path", None),
lora_strength=float(getattr(args, "lora_strength", 1.0)),
),
experimental=experimental,
model=MiniMaxH3Options(
sequential_load=args.h3_sequential_load,
video_decode_backend=None if args.video_decode_backend == "h3-vae" else args.video_decode_backend,
taeh3_checkpoint=args.taeh3_checkpoint,
vae_parallel_decode=args.parallel_vae,
vae_parallel_decode_strategy="gather",
),
),
engine=EngineConfig(
num_gpus=args.num_gpus,
@@ -297,6 +288,12 @@ def build_generator_config(args: argparse.Namespace) -> GeneratorConfig:
enabled=args.torch_compile,
mode=args.compile_mode,
vae_enabled=args.compile_vae,
regional=args.inference_torch_compile,
),
attention=AttentionConfig(
backend="VIDEO_SPARSE_ATTN_H3" if use_vsa else "FLASH_ATTN",
vsa_sparsity=args.vsa_sparsity if use_vsa else None,
vsa_tile_size=args.vsa_tile_size if use_vsa else None,
),
),
)
@@ -17,11 +17,13 @@ sys.path.insert(0, str(FASTVIDEO_ROOT))
from fastvideo import VideoGenerator # noqa: E402
from fastvideo.api import ( # noqa: E402
AttentionConfig,
CompileConfig,
ComponentConfig,
EngineConfig,
GenerationRequest,
GeneratorConfig,
MiniMaxH3Options,
OffloadConfig,
OutputConfig,
ParallelismConfig,
@@ -57,17 +59,7 @@ def main() -> None:
args = parse_args()
attention_backend = VARIANT_BACKENDS[args.variant]
experimental = {
"attention_backend": attention_backend,
"inference_torch_compile": attention_backend == "FLASH_ATTN",
"vae_parallel_decode": True,
"vae_parallel_decode_strategy": "gather",
}
if attention_backend == "VIDEO_SPARSE_ATTN_H3":
experimental.update({
"VSA_sparsity": 0.9,
"VSA_tile_size": 64,
})
use_vsa = attention_backend == "VIDEO_SPARSE_ATTN_H3"
adapter_path = hf_hub_download(
repo_id="FastVideo/FastVideo-FastH3-4-step-Preview-v1-LoRA",
@@ -81,13 +73,18 @@ def main() -> None:
model_path="MiniMaxAI/MiniMax-H3",
pipeline=PipelineSelection(
components=ComponentConfig(lora_path=adapter_path, lora_strength=1.0),
experimental=experimental,
model=MiniMaxH3Options(vae_parallel_decode=True, vae_parallel_decode_strategy="gather"),
),
engine=EngineConfig(
num_gpus=4,
parallelism=ParallelismConfig(tp_size=1, sp_size=4),
offload=OffloadConfig(dit=False, dit_layerwise=False),
compile=CompileConfig(vae_enabled=True),
compile=CompileConfig(vae_enabled=True, regional=attention_backend == "FLASH_ATTN"),
attention=AttentionConfig(
backend=attention_backend,
vsa_sparsity=0.9 if use_vsa else None,
vsa_tile_size=64 if use_vsa else None,
),
),
)
)
@@ -11,11 +11,13 @@ from huggingface_hub import hf_hub_download
from fastvideo import VideoGenerator
from fastvideo.api import (
AttentionConfig,
CompileConfig,
ComponentConfig,
EngineConfig,
GenerationRequest,
GeneratorConfig,
MiniMaxH3Options,
OffloadConfig,
OutputConfig,
ParallelismConfig,
@@ -50,17 +52,7 @@ def main() -> None:
output_dir.mkdir(parents=True, exist_ok=True)
attention_backend = VARIANT_BACKENDS[args.variant]
experimental = {
"attention_backend": attention_backend,
"inference_torch_compile": attention_backend == "FLASH_ATTN",
"vae_parallel_decode": True,
"vae_parallel_decode_strategy": "gather",
}
if attention_backend == "VIDEO_SPARSE_ATTN_H3":
experimental.update({
"VSA_sparsity": 0.9,
"VSA_tile_size": 64,
})
use_vsa = attention_backend == "VIDEO_SPARSE_ATTN_H3"
adapter_path = hf_hub_download(
repo_id="FastVideo/FastVideo-FastH3-4-step-Preview-v1-LoRA",
@@ -71,13 +63,18 @@ def main() -> None:
model_path="MiniMaxAI/MiniMax-H3",
pipeline=PipelineSelection(
components=ComponentConfig(lora_path=adapter_path, lora_strength=1.0),
experimental=experimental,
model=MiniMaxH3Options(vae_parallel_decode=True, vae_parallel_decode_strategy="gather"),
),
engine=EngineConfig(
num_gpus=4,
parallelism=ParallelismConfig(tp_size=1, sp_size=4),
offload=OffloadConfig(dit=False, dit_layerwise=False),
compile=CompileConfig(vae_enabled=True),
compile=CompileConfig(vae_enabled=True, regional=attention_backend == "FLASH_ATTN"),
attention=AttentionConfig(
backend=attention_backend,
vsa_sparsity=0.9 if use_vsa else None,
vsa_tile_size=64 if use_vsa else None,
),
quantization=QuantizationConfig(transformer_quant="MXFP8"),
),
)
@@ -30,14 +30,16 @@ generator:
compile:
enabled: false
vae_enabled: true
regional: true
attention:
backend: VIDEO_SPARSE_ATTN_H3
vsa_sparsity: 0.9
vsa_tile_size: 64
pipeline:
experimental:
attention_backend: VIDEO_SPARSE_ATTN_H3
VSA_sparsity: 0.9
VSA_tile_size: 64
inference_torch_compile: true
vae_parallel_decode: true
vae_parallel_decode_strategy: gather
model:
minimax_h3:
vae_parallel_decode: true
vae_parallel_decode_strategy: gather
request:
prompt: >-
A wide cinematic shot of an alpine meadow at sunrise, pale pink mountain
@@ -30,14 +30,16 @@ generator:
compile:
enabled: false
vae_enabled: true
regional: true
attention:
backend: VIDEO_SPARSE_ATTN_H3
vsa_sparsity: 0.9
vsa_tile_size: 64
pipeline:
experimental:
attention_backend: VIDEO_SPARSE_ATTN_H3
VSA_sparsity: 0.9
VSA_tile_size: 64
inference_torch_compile: true
vae_parallel_decode: true
vae_parallel_decode_strategy: gather
model:
minimax_h3:
vae_parallel_decode: true
vae_parallel_decode_strategy: gather
request:
prompt: >-
A wide cinematic shot of an alpine meadow at sunrise, pale pink mountain
+32 -31
View File
@@ -83,24 +83,23 @@ def main() -> None:
os.makedirs(args.out_dir, exist_ok=True)
init_kwargs = {
"num_gpus": args.num_gpus,
"workload_type": "t2i",
"sp_size": 1,
"tp_size": 1,
"dit_cpu_offload": False,
"dit_layerwise_offload": False,
"text_encoder_cpu_offload": False,
"vae_cpu_offload": False,
"image_encoder_cpu_offload": False,
"pin_cpu_memory": False,
"use_fsdp_inference": False,
}
generator = VideoGenerator.from_pretrained(
model_path=args.model_path,
**init_kwargs,
)
generator = VideoGenerator.from_config({
"model_path": args.model_path,
"engine": {
"num_gpus": args.num_gpus,
"parallelism": {"sp_size": 1, "tp_size": 1},
"offload": {
"dit": False,
"dit_layerwise": False,
"text_encoder": False,
"vae": False,
"image_encoder": False,
"pin_cpu_memory": False,
},
"use_fsdp_inference": False,
},
"pipeline": {"workload_type": "t2i"},
})
try:
for i, prompt in enumerate(prompts):
seed = args.seed + i
@@ -109,21 +108,23 @@ def main() -> None:
output_path = os.path.join(args.out_dir, f"{filename_base}.png")
print(f"[flux] prompt_idx={i} seed={seed} output_path={output_path}")
generation_kwargs = {
"output_path": output_path,
"height": args.height,
"width": args.width,
"num_frames": 1,
"fps": 1,
"num_inference_steps": args.steps,
"guidance_scale": args.guidance,
"use_embedded_guidance": True,
"true_cfg_scale": 1.0,
"seed": seed,
"save_video": True,
request = {
"prompt": prompt,
"sampling": {
"height": args.height,
"width": args.width,
"num_frames": 1,
"fps": 1,
"num_inference_steps": args.steps,
"guidance_scale": args.guidance,
"use_embedded_guidance": True,
"true_cfg_scale": 1.0,
"seed": seed,
},
"output": {"output_path": output_path, "save_video": True},
}
generator.generate_video(prompt, **generation_kwargs)
generator.generate(request)
print(f"[flux] done. outputs written to: {args.out_dir}")
finally:
+29 -22
View File
@@ -56,12 +56,18 @@ def main():
# FastVideo will automatically download weights from HuggingFace
generator = VideoGenerator.from_pretrained(
MODEL_PATH,
num_gpus=1,
use_fsdp_inference=True,
dit_cpu_offload=True,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": True,
"offload": {
"dit": True,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": True,
},
},
},
)
# Video parameters
@@ -95,23 +101,24 @@ def main():
prompt = DEFAULT_I2V_PROMPT if is_i2v else DEFAULT_PROMPTS["temple"]
print(f"Mode: {'I2V' if is_i2v else 'T2V'}, prompt: {prompt[:60]}...")
gen_kw = dict(
prompt=prompt,
negative_prompt="",
camera_states=camera_states,
height=height,
width=width,
num_frames=num_frames,
num_inference_steps=50,
guidance_scale=6.0,
seed=42,
fps=24,
output_path=OUTPUT_PATH,
save_video=True,
)
request = {
"prompt": prompt,
"negative_prompt": "",
"sampling": {
"height": height,
"width": width,
"num_frames": num_frames,
"num_inference_steps": 50,
"guidance_scale": 6.0,
"seed": 42,
"fps": 24,
},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
"extensions": {"camera_states": camera_states},
}
if is_i2v:
gen_kw["image_path"] = image_path
generator.generate_video(**gen_kw)
request["inputs"] = {"image_path": image_path}
generator.generate(request)
if __name__ == "__main__":
+32 -23
View File
@@ -60,31 +60,40 @@ def main():
generator = VideoGenerator.from_pretrained(
args.model_path,
num_gpus=1,
use_fsdp_inference=False,
dit_cpu_offload=False,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False,
"offload": {
"dit": False,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": True,
},
},
},
)
video = generator.generate_video(
args.prompt,
negative_prompt=args.negative_prompt,
image_path=args.image_path,
trajectory_type=args.trajectory,
movement_distance=args.movement_distance,
camera_rotation=args.camera_rotation,
height=args.height,
width=args.width,
num_frames=args.num_frames,
num_inference_steps=args.num_inference_steps,
guidance_scale=args.guidance_scale,
fps=24,
seed=args.seed,
output_path=args.output_path,
save_video=True,
)
video = generator.generate({
"prompt": args.prompt,
"negative_prompt": args.negative_prompt,
"inputs": {"image_path": args.image_path},
"sampling": {
"height": args.height,
"width": args.width,
"num_frames": args.num_frames,
"num_inference_steps": args.num_inference_steps,
"guidance_scale": args.guidance_scale,
"fps": 24,
"seed": args.seed,
},
"output": {"output_path": args.output_path, "save_video": True},
"extensions": {
"trajectory_type": args.trajectory,
"movement_distance": args.movement_distance,
"camera_rotation": args.camera_rotation,
},
})
generator.shutdown()
+25 -19
View File
@@ -13,25 +13,31 @@ def main():
generator = VideoGenerator.from_pretrained(
"hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-480p_t2v",
# FastVideo will automatically handle distributed setup
num_gpus=1,
use_fsdp_inference=False, # set to True if GPU is out of memory
dit_cpu_offload=True,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
# image_encoder_cpu_offload=False,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set to True if GPU is out of memory
"offload": {
"dit": True,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
# "image_encoder": False,
},
},
},
)
prompt = ("A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes "
"wide with interest. The playful yet serene atmosphere is complemented by soft "
"natural light filtering through the petals. Mid-shot, warm and cheerful tones.")
video = generator.generate_video(prompt,
output_path=OUTPUT_PATH,
save_video=True,
negative_prompt="",
num_frames=81,
fps=16)
video = generator.generate({
"prompt": prompt,
"negative_prompt": "",
"sampling": {"num_frames": 81, "fps": 16},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
prompt2 = ("A majestic lion strides across the golden savanna, its powerful frame "
"glistening under the warm afternoon sun. The tall grass ripples gently in "
@@ -39,12 +45,12 @@ def main():
"embodying the raw energy of the wild. Low angle, steady tracking shot, "
"cinematic.")
video2 = generator.generate_video(prompt2,
output_path=OUTPUT_PATH,
save_video=True,
negative_prompt="",
num_frames=81,
fps=16)
video2 = generator.generate({
"prompt": prompt2,
"negative_prompt": "",
"sampling": {"num_frames": 81, "fps": 16},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
if __name__ == "__main__":
+23 -9
View File
@@ -14,20 +14,30 @@ def main():
"weizhou03/HunyuanVideo-1.5-Diffusers-1080p-2SR", # 480p -> 720p -> 1080p
# or "weizhou03/HunyuanVideo-1.5-Diffusers-1080p" # 720p -> 1080p
# FastVideo will automatically handle distributed setup
num_gpus=1,
use_fsdp_inference=False, # set to True if GPU is out of memory
dit_cpu_offload=True,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
# image_encoder_cpu_offload=False,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set to True if GPU is out of memory
"offload": {
"dit": True,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
# "image_encoder": False,
},
},
},
)
prompt = ("A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes "
"wide with interest. The playful yet serene atmosphere is complemented by soft "
"natural light filtering through the petals. Mid-shot, warm and cheerful tones.")
video = generator.generate_video(prompt, output_path=OUTPUT_PATH, save_video=True, negative_prompt="")
video = generator.generate({
"prompt": prompt,
"negative_prompt": "",
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
prompt2 = ("A majestic lion strides across the golden savanna, its powerful frame "
"glistening under the warm afternoon sun. The tall grass ripples gently in "
@@ -35,7 +45,11 @@ def main():
"embodying the raw energy of the wild. Low angle, steady tracking shot, "
"cinematic.")
video2 = generator.generate_video(prompt2, output_path=OUTPUT_PATH, save_video=True, negative_prompt="")
video2 = generator.generate({
"prompt": prompt2,
"negative_prompt": "",
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
if __name__ == "__main__":
+29 -20
View File
@@ -35,31 +35,40 @@ def main():
print("\nInitializing VideoGenerator for HYWorld...")
generator = VideoGenerator.from_pretrained(
"FastVideo/HY-WorldPlay-Bidirectional-Diffusers",
num_gpus=1,
use_fsdp_inference=True,
dit_cpu_offload=True,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
image_encoder_cpu_offload=True,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": True,
"offload": {
"dit": True,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": True,
"image_encoder": True,
},
},
},
)
# Generate video
# The pose string is automatically converted to camera matrices by the pipeline
print("\nGenerating video...")
generator.generate_video(
prompt=args.prompt,
image_path=args.image,
pose=args.pose, # Camera trajectory control
output_path=args.output_path,
save_video=True,
negative_prompt="",
num_frames=args.num_frames,
fps=24,
height=HEIGHT,
width=WIDTH,
seed=args.seed,
)
generator.generate({
"prompt": args.prompt,
"negative_prompt": "",
"inputs": {
"image_path": args.image,
"pose": args.pose, # Camera trajectory control
},
"sampling": {
"num_frames": args.num_frames,
"fps": 24,
"height": HEIGHT,
"width": WIDTH,
"seed": args.seed,
},
"output": {"output_path": args.output_path, "save_video": True},
})
print(f"\nVideo saved to: {args.output_path}")
@@ -10,25 +10,28 @@ def main():
"kandinskylab/Kandinsky-5.0-I2V-Pro-distilled-5s-Diffusers",
# "kandinskylab/Kandinsky-5.0-I2V-Pro-sft-5s-Diffusers"
# "kandinskylab/Kandinsky-5.0-I2V-Lite-5s-Diffusers"
num_gpus=1,
use_fsdp_inference=False,
dit_cpu_offload=False,
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
# image_encoder_cpu_offload=False,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False,
"offload": {
"dit": False,
"vae": False,
"text_encoder": True,
"pin_cpu_memory": True,
# "image_encoder": False,
},
},
},
)
prompt = ("A woman stands up and walks away")
_ = generator.generate_video(
prompt,
image_path=IMAGE_PATH,
output_path=OUTPUT_PATH,
save_video=True,
height=1024,
width=1024,
num_frames=121,
)
_ = generator.generate({
"prompt": prompt,
"inputs": {"image_path": IMAGE_PATH},
"sampling": {"height": 1024, "width": 1024, "num_frames": 121},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
if __name__ == "__main__":
@@ -9,36 +9,40 @@ def main():
# "kandinskylab/Kandinsky-5.0-T2V-Pro-sft-5s-Diffusers"
# "kandinskylab/Kandinsky-5.0-T2V-Lite-distilled16steps-5s-Diffusers"
# "kandinskylab/Kandinsky-5.0-T2V-Pro-distilled-5s-Diffusers"
num_gpus=1,
use_fsdp_inference=False,
dit_cpu_offload=False,
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
# image_encoder_cpu_offload=False,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False,
"offload": {
"dit": False,
"vae": False,
"text_encoder": True,
"pin_cpu_memory": True,
# "image_encoder": False,
},
},
},
)
prompt = ("A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes "
"wide with interest. The playful yet serene atmosphere is complemented by soft "
"natural light filtering through the petals. Mid-shot, warm and cheerful tones.")
_ = generator.generate_video(prompt,
output_path=OUTPUT_PATH,
save_video=True,
height=512,
width=768,
num_frames=121)
_ = generator.generate({
"prompt": prompt,
"sampling": {"height": 512, "width": 768, "num_frames": 121},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
prompt2 = ("A majestic lion strides across the golden savanna, its powerful frame "
"glistening under the warm afternoon sun. The tall grass ripples gently in "
"the breeze, enhancing the lion's commanding presence. The tone is vibrant, "
"embodying the raw energy of the wild. Low angle, steady tracking shot, "
"cinematic.")
_ = generator.generate_video(prompt2,
output_path=OUTPUT_PATH,
save_video=True,
height=512,
width=768,
num_frames=121)
_ = generator.generate({
"prompt": prompt2,
"sampling": {"height": 512, "width": 768, "num_frames": 121},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
if __name__ == "__main__":
@@ -29,11 +29,17 @@ def main() -> None:
args = parse_args()
generator = VideoGenerator.from_pretrained(
str(args.model_path),
num_gpus=1,
use_fsdp_inference=False,
text_encoder_cpu_offload=True,
vae_cpu_offload=False,
pin_cpu_memory=True,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False,
"offload": {
"text_encoder": True,
"vae": False,
"pin_cpu_memory": True,
},
},
},
)
try:
generator.generate({
@@ -13,36 +13,43 @@ OUTPUT_PATH = REPO_ROOT / "outputs" / "lingbotworld2_causal_fast.mp4"
def main() -> None:
"""Load the native FastVideo LingBot World 2 causal-fast pipeline and generate one video."""
generator = VideoGenerator.from_pretrained(
os.environ["LINGBOTWORLD2_MODEL_PATH"],
num_gpus=8,
sp_size=8,
hsdp_shard_dim=8,
use_fsdp_inference=True,
dit_layerwise_offload=False,
dit_cpu_offload=False,
vae_cpu_offload=False,
text_encoder_cpu_offload=False,
pin_cpu_memory=True,
override_pipeline_cls_name="LingBotWorld2CausalFastPipeline",
)
generator = VideoGenerator.from_config({
"model_path": os.environ["LINGBOTWORLD2_MODEL_PATH"],
"engine": {
"num_gpus": 8,
"parallelism": {"sp_size": 8, "hsdp_shard_dim": 8},
"use_fsdp_inference": True,
"offload": {
"dit_layerwise": False,
"dit": False,
"vae": False,
"text_encoder": False,
"pin_cpu_memory": True,
},
},
"pipeline": {"components": {"override_pipeline_cls_name": "LingBotWorld2CausalFastPipeline"}},
})
try:
generator.generate_video(
generator.generate({
"prompt":
"A serene lakeside scene with a lone tree standing in calm water, surrounded by distant snow-capped mountains under a bright blue sky with drifting white clouds; gentle ripples reflect the tree and sky, creating a tranquil, meditative atmosphere.",
image_path=str(DATASET_DIR / "image.jpg"),
action_path=str(DATASET_DIR),
output_path=str(OUTPUT_PATH),
save_video=True,
height=480,
width=832,
num_frames=65,
num_inference_steps=4,
guidance_scale=1.0,
negative_prompt="",
fps=16,
seed=42,
)
"negative_prompt": "",
"inputs": {
"image_path": str(DATASET_DIR / "image.jpg"),
"action_path": str(DATASET_DIR),
},
"sampling": {
"height": 480,
"width": 832,
"num_frames": 65,
"num_inference_steps": 4,
"guidance_scale": 1.0,
"fps": 16,
"seed": 42,
},
"output": {"output_path": str(OUTPUT_PATH), "save_video": True},
})
finally:
generator.shutdown()
@@ -13,14 +13,20 @@ def main():
generator = VideoGenerator.from_pretrained(
"FastVideo/LingBot-World-Base-Cam-Diffusers",
# FastVideo will automatically handle distributed setup
num_gpus=1,
use_fsdp_inference=False, # set to True if GPU is out of memory
dit_cpu_offload=True, # DiT need to be offloaded for MoE
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
pin_cpu_memory=True,
# image_encoder_cpu_offload=False,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set to True if GPU is out of memory
"offload": {
"dit": True, # DiT need to be offloaded for MoE
"vae": False,
"text_encoder": True,
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
"pin_cpu_memory": True,
# "image_encoder": False,
},
},
},
)
num_frames = 81
@@ -35,16 +41,12 @@ def main():
spatial_scale=8,
)
generator.generate_video(
prompt,
image_path=image_path,
output_path=OUTPUT_PATH,
save_video=True,
num_frames=num_frames,
height=480,
width=832,
c2ws_plucker_emb=c2ws_plucker_emb,
)
generator.generate({
"prompt": prompt,
"inputs": {"image_path": image_path, "c2ws_plucker_emb": c2ws_plucker_emb},
"sampling": {"num_frames": num_frames, "height": 480, "width": 832},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
if __name__ == "__main__":
+110 -81
View File
@@ -48,33 +48,38 @@ def basic_generation():
print("LongCat I2V: Basic Generation (50 steps, 480p)")
print("=" * 60)
generator = VideoGenerator.from_pretrained(
"FastVideo/LongCat-Video-I2V-Diffusers",
num_gpus=1,
use_fsdp_inference=False, # set to True if GPU is out of memory
dit_cpu_offload=False,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=False,
enable_bsa=False,
)
generator = VideoGenerator.from_config({
"model_path": "FastVideo/LongCat-Video-I2V-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set to True if GPU is out of memory
"offload": {
"dit": False,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": False,
},
},
"pipeline": {"model": {"longcat": {"enable_bsa": False}}},
})
output_path = "outputs_video/longcat_i2v_basic"
generator.generate_video(
prompt=PROMPT,
negative_prompt=NEGATIVE_PROMPT,
image_path=IMAGE_PATH,
output_path=output_path,
save_video=True,
height=480,
width=480, # Square
num_frames=93,
num_inference_steps=50,
fps=15,
guidance_scale=4.0,
seed=SEED,
)
generator.generate({
"prompt": PROMPT,
"negative_prompt": NEGATIVE_PROMPT,
"inputs": {"image_path": IMAGE_PATH},
"sampling": {
"height": 480,
"width": 480, # Square
"num_frames": 93,
"num_inference_steps": 50,
"fps": 15,
"guidance_scale": 4.0,
"seed": SEED,
},
"output": {"output_path": output_path, "save_video": True},
})
print(f"\nBasic generation complete! Video saved to: {output_path}")
generator.shutdown()
@@ -95,35 +100,44 @@ def distill_refine_generation():
print("\n[Stage 1] Distilled generation (16 steps, 480p)")
print("-" * 40)
generator = VideoGenerator.from_pretrained(
"FastVideo/LongCat-Video-I2V-Diffusers",
num_gpus=1,
use_fsdp_inference=True,
dit_cpu_offload=False,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=False,
enable_bsa=False,
lora_path="FastVideo/LongCat-Video-T2V-Distilled-LoRA",
lora_nickname="distilled",
)
generator = VideoGenerator.from_config({
"model_path": "FastVideo/LongCat-Video-I2V-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": True,
"offload": {
"dit": False,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": False,
},
},
"pipeline": {
"model": {"longcat": {"enable_bsa": False}},
"components": {
"lora_path": "FastVideo/LongCat-Video-T2V-Distilled-LoRA",
"lora_nickname": "distilled",
},
},
})
distill_output_path = "outputs_video/longcat_i2v_distill"
generator.generate_video(
prompt=PROMPT,
negative_prompt=NEGATIVE_PROMPT,
image_path=IMAGE_PATH,
output_path=distill_output_path,
save_video=True,
height=480,
width=480, # Square
num_frames=93,
num_inference_steps=16,
fps=15,
guidance_scale=1.0,
seed=SEED,
)
generator.generate({
"prompt": PROMPT,
"negative_prompt": NEGATIVE_PROMPT,
"inputs": {"image_path": IMAGE_PATH},
"sampling": {
"height": 480,
"width": 480, # Square
"num_frames": 93,
"num_inference_steps": 16,
"fps": 15,
"guidance_scale": 1.0,
"seed": SEED,
},
"output": {"output_path": distill_output_path, "save_video": True},
})
print(f"Distilled generation complete! Video saved to: {distill_output_path}")
generator.shutdown()
@@ -144,40 +158,55 @@ def distill_refine_generation():
# Note: Refinement uses the T2V model (not I2V) since it's upscaling the generated video
# For BSA [4, 4, 8]: latent must be divisible by 8
# 768x768: latent 48x48, 48%8=0 ✓
refine_generator = VideoGenerator.from_pretrained(
"FastVideo/LongCat-Video-T2V-Diffusers",
num_gpus=1,
use_fsdp_inference=True,
dit_cpu_offload=True,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=False,
enable_bsa=True,
bsa_sparsity=0.875,
bsa_chunk_q=[4, 4, 4],
bsa_chunk_k=[4, 4, 4],
lora_path="FastVideo/LongCat-Video-T2V-Refinement-LoRA",
lora_nickname="refinement",
)
refine_generator = VideoGenerator.from_config({
"model_path": "FastVideo/LongCat-Video-T2V-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": True,
"offload": {
"dit": True,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": False,
},
},
"pipeline": {
"model": {
"longcat": {
"enable_bsa": True,
"bsa_sparsity": 0.875,
"bsa_chunk_q": [4, 4, 4],
"bsa_chunk_k": [4, 4, 4],
},
},
"components": {
"lora_path": "FastVideo/LongCat-Video-T2V-Refinement-LoRA",
"lora_nickname": "refinement",
},
},
})
refine_output_path = "outputs_video/longcat_i2v_refine_720p"
refine_generator.generate_video(
prompt=PROMPT,
negative_prompt=NEGATIVE_PROMPT,
output_path=refine_output_path,
save_video=True,
refine_from=distill_video_path,
t_thresh=0.5,
spatial_refine_only=False,
num_cond_frames=0,
height=720,
width=720,
num_inference_steps=50,
fps=30,
guidance_scale=1.0,
seed=SEED,
)
refine_generator.generate({
"prompt": PROMPT,
"negative_prompt": NEGATIVE_PROMPT,
"inputs": {"refine_from": distill_video_path},
"sampling": {
"height": 720,
"width": 720,
"num_inference_steps": 50,
"fps": 30,
"guidance_scale": 1.0,
"seed": SEED,
},
"output": {"output_path": refine_output_path, "save_video": True},
"extensions": {
"t_thresh": 0.5,
"spatial_refine_only": False,
"num_cond_frames": 0,
},
})
print(f"Refinement complete! Video saved to: {refine_output_path}")
refine_generator.shutdown()
+108 -79
View File
@@ -41,32 +41,37 @@ def basic_generation():
print("LongCat T2V: Basic Generation (50 steps, 480p)")
print("=" * 60)
generator = VideoGenerator.from_pretrained(
"FastVideo/LongCat-Video-T2V-Diffusers",
num_gpus=1,
use_fsdp_inference=False, # set to True if GPU is out of memory
dit_cpu_offload=False,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=False,
enable_bsa=False,
)
generator = VideoGenerator.from_config({
"model_path": "FastVideo/LongCat-Video-T2V-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set to True if GPU is out of memory
"offload": {
"dit": False,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": False,
},
},
"pipeline": {"model": {"longcat": {"enable_bsa": False}}},
})
output_path = "outputs_video/longcat_t2v_basic"
generator.generate_video(
prompt=PROMPT,
negative_prompt=NEGATIVE_PROMPT,
output_path=output_path,
save_video=True,
height=480,
width=832,
num_frames=93,
num_inference_steps=50,
fps=15,
guidance_scale=4.0,
seed=SEED,
)
generator.generate({
"prompt": PROMPT,
"negative_prompt": NEGATIVE_PROMPT,
"sampling": {
"height": 480,
"width": 832,
"num_frames": 93,
"num_inference_steps": 50,
"fps": 15,
"guidance_scale": 4.0,
"seed": SEED,
},
"output": {"output_path": output_path, "save_video": True},
})
print(f"\nBasic generation complete! Video saved to: {output_path}")
generator.shutdown()
@@ -87,34 +92,43 @@ def distill_refine_generation():
print("\n[Stage 1] Distilled generation (16 steps, 480p)")
print("-" * 40)
generator = VideoGenerator.from_pretrained(
"FastVideo/LongCat-Video-T2V-Diffusers",
num_gpus=1,
use_fsdp_inference=True,
dit_cpu_offload=False,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=False,
enable_bsa=False,
lora_path="FastVideo/LongCat-Video-T2V-Distilled-LoRA",
lora_nickname="distilled",
)
generator = VideoGenerator.from_config({
"model_path": "FastVideo/LongCat-Video-T2V-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": True,
"offload": {
"dit": False,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": False,
},
},
"pipeline": {
"model": {"longcat": {"enable_bsa": False}},
"components": {
"lora_path": "FastVideo/LongCat-Video-T2V-Distilled-LoRA",
"lora_nickname": "distilled",
},
},
})
distill_output_path = "outputs_video/longcat_t2v_distill"
generator.generate_video(
prompt=PROMPT,
negative_prompt=NEGATIVE_PROMPT,
output_path=distill_output_path,
save_video=True,
height=480,
width=832,
num_frames=93,
num_inference_steps=16,
fps=15,
guidance_scale=1.0,
seed=SEED,
)
generator.generate({
"prompt": PROMPT,
"negative_prompt": NEGATIVE_PROMPT,
"sampling": {
"height": 480,
"width": 832,
"num_frames": 93,
"num_inference_steps": 16,
"fps": 15,
"guidance_scale": 1.0,
"seed": SEED,
},
"output": {"output_path": distill_output_path, "save_video": True},
})
print(f"Distilled generation complete! Video saved to: {distill_output_path}")
generator.shutdown()
@@ -132,40 +146,55 @@ def distill_refine_generation():
print(f"Using stage 1 video: {distill_video_path}")
# Create a new generator with refinement LoRA and BSA enabled
refine_generator = VideoGenerator.from_pretrained(
"FastVideo/LongCat-Video-T2V-Diffusers",
num_gpus=1,
use_fsdp_inference=True,
dit_cpu_offload=True,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=False,
enable_bsa=True,
bsa_sparsity=0.875,
bsa_chunk_q=[4, 4, 8],
bsa_chunk_k=[4, 4, 8],
lora_path="FastVideo/LongCat-Video-T2V-Refinement-LoRA",
lora_nickname="refinement",
)
refine_generator = VideoGenerator.from_config({
"model_path": "FastVideo/LongCat-Video-T2V-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": True,
"offload": {
"dit": True,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": False,
},
},
"pipeline": {
"model": {
"longcat": {
"enable_bsa": True,
"bsa_sparsity": 0.875,
"bsa_chunk_q": [4, 4, 8],
"bsa_chunk_k": [4, 4, 8],
},
},
"components": {
"lora_path": "FastVideo/LongCat-Video-T2V-Refinement-LoRA",
"lora_nickname": "refinement",
},
},
})
refine_output_path = "outputs_video/longcat_t2v_refine_720p"
refine_generator.generate_video(
prompt=PROMPT,
negative_prompt=NEGATIVE_PROMPT,
output_path=refine_output_path,
save_video=True,
refine_from=distill_video_path,
t_thresh=0.5,
spatial_refine_only=False,
num_cond_frames=0,
height=720,
width=1280,
num_inference_steps=50,
fps=30,
guidance_scale=1.0,
seed=SEED,
)
refine_generator.generate({
"prompt": PROMPT,
"negative_prompt": NEGATIVE_PROMPT,
"inputs": {"refine_from": distill_video_path},
"sampling": {
"height": 720,
"width": 1280,
"num_inference_steps": 50,
"fps": 30,
"guidance_scale": 1.0,
"seed": SEED,
},
"output": {"output_path": refine_output_path, "save_video": True},
"extensions": {
"t_thresh": 0.5,
"spatial_refine_only": False,
"num_cond_frames": 0,
},
})
print(f"Refinement complete! Video saved to: {refine_output_path}")
refine_generator.shutdown()
+112 -83
View File
@@ -57,34 +57,39 @@ def basic_generation():
raise FileNotFoundError(f"Video not found at {VIDEO_PATH}. "
"Please provide a valid video path.")
generator = VideoGenerator.from_pretrained(
"FastVideo/LongCat-Video-VC-Diffusers",
num_gpus=1,
use_fsdp_inference=False, # set to True if GPU is out of memory
dit_cpu_offload=False,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=False,
enable_bsa=False,
)
generator = VideoGenerator.from_config({
"model_path": "FastVideo/LongCat-Video-VC-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set to True if GPU is out of memory
"offload": {
"dit": False,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": False,
},
},
"pipeline": {"model": {"longcat": {"enable_bsa": False}}},
})
output_path = "outputs_video/longcat_vc_basic"
generator.generate_video(
prompt=PROMPT,
negative_prompt=NEGATIVE_PROMPT,
video_path=VIDEO_PATH,
num_cond_frames=NUM_COND_FRAMES,
output_path=output_path,
save_video=True,
height=480,
width=832,
num_frames=93,
num_inference_steps=50,
fps=15,
guidance_scale=4.0,
seed=SEED,
)
generator.generate({
"prompt": PROMPT,
"negative_prompt": NEGATIVE_PROMPT,
"inputs": {"video_path": VIDEO_PATH},
"sampling": {
"height": 480,
"width": 832,
"num_frames": 93,
"num_inference_steps": 50,
"fps": 15,
"guidance_scale": 4.0,
"seed": SEED,
},
"output": {"output_path": output_path, "save_video": True},
"extensions": {"num_cond_frames": NUM_COND_FRAMES},
})
print(f"\nBasic generation complete! Video saved to: {output_path}")
generator.shutdown()
@@ -110,36 +115,45 @@ def distill_refine_generation():
print("\n[Stage 1] Distilled generation (16 steps, 480p)")
print("-" * 40)
generator = VideoGenerator.from_pretrained(
"FastVideo/LongCat-Video-VC-Diffusers",
num_gpus=1,
use_fsdp_inference=True,
dit_cpu_offload=False,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=False,
enable_bsa=False,
lora_path="FastVideo/LongCat-Video-T2V-Distilled-LoRA",
lora_nickname="distilled",
)
generator = VideoGenerator.from_config({
"model_path": "FastVideo/LongCat-Video-VC-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": True,
"offload": {
"dit": False,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": False,
},
},
"pipeline": {
"model": {"longcat": {"enable_bsa": False}},
"components": {
"lora_path": "FastVideo/LongCat-Video-T2V-Distilled-LoRA",
"lora_nickname": "distilled",
},
},
})
distill_output_path = "outputs_video/longcat_vc_distill"
generator.generate_video(
prompt=PROMPT,
negative_prompt=NEGATIVE_PROMPT,
video_path=VIDEO_PATH,
num_cond_frames=NUM_COND_FRAMES,
output_path=distill_output_path,
save_video=True,
height=480,
width=832,
num_frames=93,
num_inference_steps=16,
fps=15,
guidance_scale=1.0,
seed=SEED,
)
generator.generate({
"prompt": PROMPT,
"negative_prompt": NEGATIVE_PROMPT,
"inputs": {"video_path": VIDEO_PATH},
"sampling": {
"height": 480,
"width": 832,
"num_frames": 93,
"num_inference_steps": 16,
"fps": 15,
"guidance_scale": 1.0,
"seed": SEED,
},
"output": {"output_path": distill_output_path, "save_video": True},
"extensions": {"num_cond_frames": NUM_COND_FRAMES},
})
print(f"Distilled generation complete! Video saved to: {distill_output_path}")
generator.shutdown()
@@ -158,40 +172,55 @@ def distill_refine_generation():
# Create a new generator with refinement LoRA and BSA enabled
# Note: Refinement uses the T2V model (not VC) since it's upscaling the generated video
refine_generator = VideoGenerator.from_pretrained(
"FastVideo/LongCat-Video-T2V-Diffusers",
num_gpus=1,
use_fsdp_inference=True,
dit_cpu_offload=True,
vae_cpu_offload=True,
text_encoder_cpu_offload=True,
pin_cpu_memory=False,
enable_bsa=True,
bsa_sparsity=0.875,
bsa_chunk_q=[4, 4, 8],
bsa_chunk_k=[4, 4, 8],
lora_path="FastVideo/LongCat-Video-T2V-Refinement-LoRA",
lora_nickname="refinement",
)
refine_generator = VideoGenerator.from_config({
"model_path": "FastVideo/LongCat-Video-T2V-Diffusers",
"engine": {
"num_gpus": 1,
"use_fsdp_inference": True,
"offload": {
"dit": True,
"vae": True,
"text_encoder": True,
"pin_cpu_memory": False,
},
},
"pipeline": {
"model": {
"longcat": {
"enable_bsa": True,
"bsa_sparsity": 0.875,
"bsa_chunk_q": [4, 4, 8],
"bsa_chunk_k": [4, 4, 8],
},
},
"components": {
"lora_path": "FastVideo/LongCat-Video-T2V-Refinement-LoRA",
"lora_nickname": "refinement",
},
},
})
refine_output_path = "outputs_video/longcat_vc_refine_720p"
refine_generator.generate_video(
prompt=PROMPT,
negative_prompt=NEGATIVE_PROMPT,
output_path=refine_output_path,
save_video=True,
refine_from=distill_video_path,
t_thresh=0.5,
spatial_refine_only=False,
num_cond_frames=0, # For refinement, no conditioning frames
height=720,
width=1280,
num_inference_steps=50,
fps=30,
guidance_scale=1.0,
seed=SEED,
)
refine_generator.generate({
"prompt": PROMPT,
"negative_prompt": NEGATIVE_PROMPT,
"inputs": {"refine_from": distill_video_path},
"sampling": {
"height": 720,
"width": 1280,
"num_inference_steps": 50,
"fps": 30,
"guidance_scale": 1.0,
"seed": SEED,
},
"output": {"output_path": refine_output_path, "save_video": True},
"extensions": {
"t_thresh": 0.5,
"spatial_refine_only": False,
"num_cond_frames": 0, # For refinement, no conditioning frames
},
})
print(f"Refinement complete! Video saved to: {refine_output_path}")
refine_generator.shutdown()
+10 -9
View File
@@ -17,18 +17,19 @@ def main() -> None:
# Uses FastVideo default sampling settings for LTX2 base.
generator = VideoGenerator.from_pretrained(
"Davids048/LTX2-Base-Diffusers",
num_gpus=1,
{
"engine": {
"num_gpus": 1,
},
},
)
output_path = "outputs_video/ltx2_basic/output_ltx2_base_t2v_1088_1920_1.1.mp4"
generator.generate_video(
prompt=PROMPT,
output_path=output_path,
save_video=True,
num_frames=121,
height=1088,
width=1920,
)
generator.generate({
"prompt": PROMPT,
"sampling": {"num_frames": 121, "height": 1088, "width": 1920},
"output": {"output_path": output_path, "save_video": True},
})
generator.shutdown()
@@ -49,6 +49,7 @@ from pathlib import Path
import torch._inductor.config as _inductor
from fastvideo import VideoGenerator
from fastvideo.api import GenerationResult
from fastvideo.configs.pipelines.base import PipelineConfig
from fastvideo.utils import maybe_download_model
@@ -80,9 +81,9 @@ PROMPT = os.getenv("LTX23_I2V_PROMPT", DEFAULT_PROMPT)
# Per-stage timing helpers --------------------------------------------------
def _print_stage_breakdown(result: dict, label: str) -> float | None:
def _print_stage_breakdown(result: GenerationResult, label: str) -> float | None:
"""Print stage execution times and return the sum, or None if missing."""
logging_info = result.get("logging_info")
logging_info = result.logging_info
stages = getattr(logging_info, "stages", None) if logging_info else None
if not stages:
print(f" [{label}] stage breakdown unavailable")
@@ -98,11 +99,11 @@ def _print_stage_breakdown(result: dict, label: str) -> float | None:
def _collect_stage_times(
result: dict,
result: GenerationResult,
stage_times: dict[str, list[float]],
stage_order: OrderedDict[str, None],
) -> None:
logging_info = result.get("logging_info")
logging_info = result.logging_info
stages = getattr(logging_info, "stages", None) if logging_info else None
if not stages:
return
@@ -158,52 +159,68 @@ def main() -> None:
pipeline_config = PipelineConfig.from_pretrained(model_root)
pipeline_config.dit_config.quant_config = None
generator = VideoGenerator.from_pretrained(
model_root,
num_gpus=1,
# LTX-2.3 distilled uses the two-stage refine pipeline; the refine
# LoRA is intentionally empty for the distilled student.
ltx2_refine_enabled=True,
ltx2_refine_upsampler_path=str(refine_upsampler_path),
ltx2_refine_lora_path="",
ltx2_refine_num_inference_steps=3,
ltx2_refine_guidance_scale=1.0,
ltx2_refine_add_noise=True,
pipeline_config=pipeline_config,
enable_torch_compile=True,
enable_torch_compile_text_encoder=True,
# Compile the VAE codec submodules (encoder / decoder) too. The
# `LTX2CausalVideoAutoencoder` declares `_compile_conditions` so
# `_compile_with_conditions` targets just those submodules and
# leaves the surrounding tiling control flow eager — needed for
# fullgraph + dynamic=False to succeed. VAE eager decode is
# ~1.0s; compiling it brings the stage to ~0.3s.
enable_torch_compile_vae=True,
torch_compile_kwargs=torch_compile_kwargs,
torch_compile_kwargs_vae=torch_compile_kwargs,
# Keep everything resident — no CPU offload for serving-style runs.
dit_cpu_offload=False,
text_encoder_cpu_offload=False,
vae_cpu_offload=False,
ltx2_vae_tiling=False,
)
generator = VideoGenerator.from_config({
"model_path": model_root,
"engine": {
"num_gpus": 1,
"compile": {
"enabled": True,
"text_encoder_enabled": True,
# Compile the VAE codec submodules (encoder / decoder) too. The
# `LTX2CausalVideoAutoencoder` declares `_compile_conditions` so
# `_compile_with_conditions` targets just those submodules and
# leaves the surrounding tiling control flow eager — needed for
# fullgraph + dynamic=False to succeed. VAE eager decode is
# ~1.0s; compiling it brings the stage to ~0.3s.
"vae_enabled": True,
**torch_compile_kwargs,
"vae_kwargs": torch_compile_kwargs,
},
# Keep everything resident — no CPU offload for serving-style runs.
"offload": {
"dit": False,
"text_encoder": False,
"vae": False,
},
},
"pipeline": {
# LTX-2.3 distilled uses the two-stage refine pipeline; the refine
# LoRA is intentionally empty for the distilled student.
"preset_overrides": {
"refine": {
"enabled": True,
"num_inference_steps": 3,
"guidance_scale": 1.0,
"add_noise": True,
},
},
"components": {"upsampler_weights": str(refine_upsampler_path)},
"model": {"ltx2": {"refine": {"lora_path": ""}}},
"experimental": {"pipeline_config": pipeline_config},
"vae_tiling": False,
},
})
common_kwargs = dict(
prompt=PROMPT,
negative_prompt="", # distilled is CFG-free; no negative needed
guidance_scale=1.0, # CFG=1 for distilled
height=1280,
width=832, # portrait runway aspect
num_frames=121,
fps=24, # ~5s clip
num_inference_steps=8, # distilled denoise steps
common_request = {
"prompt": PROMPT,
"negative_prompt": "", # distilled is CFG-free; no negative needed
"sampling": {
"guidance_scale": 1.0, # CFG=1 for distilled
"height": 1280,
"width": 832, # portrait runway aspect
"num_frames": 121,
"fps": 24, # ~5s clip
"num_inference_steps": 8, # distilled denoise steps
},
"output": {"save_video": True},
# i2v: anchor the input image at frame 0 with full strength.
# `ltx2_image_crf=0.0` skips an extra JPEG re-encode of an already
# JPEG conditioning image.
ltx2_images=[(I2V_IMAGE, 0, 1.0)],
ltx2_image_crf=0.0,
save_video=True,
)
"extensions": {
"ltx2_images": [(I2V_IMAGE, 0, 1.0)],
"ltx2_image_crf": 0.0,
},
}
warmup_runs = 2
measured_runs = 2
@@ -218,11 +235,11 @@ def main() -> None:
for w in range(warmup_runs):
t0 = time.perf_counter()
print(f"\n[warmup {w + 1}/{warmup_runs}] compiling + generating…")
generator.generate_video(
output_path=str(OUTPUT_DIR / f"_warmup_{w + 1}.mp4"),
seed=7,
**common_kwargs,
)
generator.generate({
**common_request,
"sampling": {**common_request["sampling"], "seed": 7},
"output": {**common_request["output"], "output_path": str(OUTPUT_DIR / f"_warmup_{w + 1}.mp4")},
})
dt = time.perf_counter() - t0
warmup_secs.append(dt)
print(f"[warmup {w + 1}/{warmup_runs}] wall={dt:.1f}s")
@@ -236,16 +253,16 @@ def main() -> None:
out_path = OUTPUT_DIR / f"output_ltx2_3_distilled_i2v_run_{m + 1}.mp4"
print(f"\n[measured {m + 1}/{measured_runs}] generating: {out_path}")
t0 = time.perf_counter()
result = generator.generate_video(
output_path=str(out_path),
seed=2002 + m,
**common_kwargs,
)
result = generator.generate({
**common_request,
"sampling": {**common_request["sampling"], "seed": 2002 + m},
"output": {**common_request["output"], "output_path": str(out_path)},
})
wall = time.perf_counter() - t0
e2e = (result.get("e2e_latency") if isinstance(result, dict) else None) or wall
e2e = (result.extra.get("e2e_latency") if isinstance(result, GenerationResult) else None) or wall
measured_secs.append(e2e)
print(f"[measured {m + 1}/{measured_runs}] e2e={e2e:.2f}s wall={wall:.2f}s")
if isinstance(result, dict):
if isinstance(result, GenerationResult):
_print_stage_breakdown(result, f"measured {m + 1}")
_collect_stage_times(result, stage_times, stage_order)
@@ -2,11 +2,9 @@
"""LTX-2.3 distilled image-to-video — typed API (``from_config`` / ``generate``).
Identical generation behavior to ``basic_ltx2_3_distilled_i2v.py``, but
expressed through the newer typed surface (``GeneratorConfig`` /
``GenerationRequest``) instead of the ``from_pretrained(**legacy_kwargs)``
bridge. The typed API is now the preferred entry point — the legacy
example still works but emits a ``DeprecationWarning`` for the LTX-2.3
specific knobs.
expressed with the typed dataclasses (``GeneratorConfig`` /
``GenerationRequest``) instead of nested dicts and a ``PipelineConfig``
object.
Quick start
-----------
@@ -42,28 +40,28 @@ Hardware notes
The ``_inductor.shape_padding = False`` line below also avoids a
``pad_mm`` landmine on the same generation of cards.
Typed-API mapping (legacy kwarg ↔ typed field)
----------------------------------------------
- ``num_gpus`` ↔ ``engine.num_gpus``
- ``enable_torch_compile`` ↔ ``engine.compile.enabled``
- ``enable_torch_compile_text_encoder`` ↔ ``engine.compile.text_encoder_enabled``
- ``enable_torch_compile_vae`` ↔ ``engine.compile.vae_enabled``
- ``torch_compile_kwargs`` ↔ ``engine.compile.backend/fullgraph/mode/dynamic``
- ``torch_compile_kwargs_vae`` ↔ empty ``compile.vae_kwargs`` (inherits master)
- ``dit_cpu_offload`` ↔ ``engine.offload.dit``
- ``text_encoder_cpu_offload`` ↔ ``engine.offload.text_encoder``
- ``vae_cpu_offload`` ↔ ``engine.offload.vae``
- ``ltx2_vae_tiling`` ↔ ``pipeline.vae_tiling``
- ``ltx2_refine_enabled`` ↔ ``pipeline.preset_overrides["refine"]["enabled"]``
- ``ltx2_refine_upsampler_path`` ↔ ``pipeline.components.upsampler_weights``
- ``ltx2_refine_lora_path`` ↔ ``pipeline.components.lora_path``
- ``ltx2_refine_num_inference_steps`` ↔ ``pipeline.preset_overrides["refine"]["num_inference_steps"]``
- ``ltx2_refine_guidance_scale`` ↔ ``pipeline.preset_overrides["refine"]["guidance_scale"]``
- ``ltx2_refine_add_noise`` ↔ ``pipeline.preset_overrides["refine"]["add_noise"]``
- ``pipeline_config=PipelineConfig.from_pretrained(model_root)`` ↔ (no-op — ``PipelineConfig.from_kwargs`` already resolves the model-specific class from ``model_path``)
- ``pipeline_config.dit_config.quant_config = None`` ↔ leave ``engine.quantization`` unset
- ``ltx2_images`` / ``ltx2_image_crf`` ↔ ``request.extensions`` (LTX-2 specific, no
first-class typed field yet)
Recipe setting ↔ typed field
----------------------------
- GPU count ↔ ``engine.num_gpus``
- DiT compile ↔ ``engine.compile.enabled``
- Text encoder compile ↔ ``engine.compile.text_encoder_enabled``
- VAE compile ↔ ``engine.compile.vae_enabled``
- ``torch.compile`` kwargs ↔ ``engine.compile.backend/fullgraph/mode/dynamic``
- VAE ``torch.compile`` kwargs ↔ empty ``engine.compile.vae_kwargs`` (inherits the master kwargs)
- DiT CPU offload ↔ ``engine.offload.dit``
- Text encoder CPU offload ↔ ``engine.offload.text_encoder``
- VAE CPU offload ↔ ``engine.offload.vae``
- VAE tiling ↔ ``pipeline.vae_tiling``
- Refine stage on ↔ ``pipeline.preset_overrides["refine"]["enabled"]``
- Refine upsampler ↔ ``pipeline.components.upsampler_weights``
- Refine LoRA ↔ ``pipeline.model.ltx2.refine.lora_path``
- Refine denoising steps ↔ ``pipeline.preset_overrides["refine"]["num_inference_steps"]``
- Refine guidance scale ↔ ``pipeline.preset_overrides["refine"]["guidance_scale"]``
- Refine noise injection ↔ ``pipeline.preset_overrides["refine"]["add_noise"]``
- Model-specific ``PipelineConfig`` ↔ nothing to set: ``PipelineConfig.from_source`` builds the
registry class of ``model_path``
- No DiT quantization ↔ leave ``engine.quantization`` unset
- Conditioning image and its CRF ↔ ``request.extensions["ltx2_images"]`` / ``["ltx2_image_crf"]`` (LTX-2 specific)
"""
from __future__ import annotations
@@ -177,7 +175,7 @@ def main() -> None:
# Keep DiT / text encoder / VAE resident on GPU — no CPU offload
# for serving-style runs. ``image_encoder`` and
# ``pin_cpu_memory`` are left at their schema defaults
# (matches the legacy example, which only set these three).
# (matches ``basic_ltx2_3_distilled_i2v.py``, which sets only these three).
offload=OffloadConfig(
dit=False,
text_encoder=False,
@@ -199,12 +197,12 @@ def main() -> None:
),
),
pipeline=PipelineSelection(
# ``PipelineConfig.from_kwargs`` resolves the model-specific
# ``PipelineConfig.from_source`` resolves the model-specific
# pipeline-config class from ``model_path`` automatically, so we
# don't need to set ``components.pipeline_config_path`` — the
# model-specific VAE precision / decoder defaults are picked up
# the same way the legacy example's
# ``PipelineConfig.from_pretrained(model_root)`` did them.
# the same way ``basic_ltx2_3_distilled_i2v.py``'s
# ``PipelineConfig.from_pretrained(model_root)`` picks them up.
components=ComponentConfig(upsampler_weights=str(refine_upsampler_path),
# Distilled has no refine LoRA — omit ``lora_path``.
),
@@ -19,15 +19,18 @@ os.environ["FASTVIDEO_ATTENTION_BACKEND"] = "FLASH_ATTN"
def main() -> None:
generator = VideoGenerator.from_pretrained(
"FastVideo/LTX2-Distilled-Diffusers",
num_gpus=4,
{
"engine": {
"num_gpus": 4,
},
},
)
output_path = "outputs_video/ltx2_basic/output_ltx2_distilled_t2v.mp4"
generator.generate_video(
prompt=PROMPT,
output_path=output_path,
save_video=True,
)
generator.generate({
"prompt": PROMPT,
"output": {"output_path": output_path, "save_video": True},
})
generator.shutdown()
@@ -8,6 +8,7 @@ from pathlib import Path
import torch
import torch._inductor.config
from fastvideo import VideoGenerator
from fastvideo.api import GenerationResult
from fastvideo.configs.pipelines.base import PipelineConfig
from fastvideo.layers.quantization.nvfp4_config import NVFP4Config
from fastvideo.utils import maybe_download_model
@@ -41,11 +42,11 @@ def load_validation_entries(path: Path) -> list[dict]:
def print_stage_breakdown(
result: dict,
result: GenerationResult,
run_idx: int,
num_runs: int,
) -> float | None:
logging_info = result.get("logging_info")
logging_info = result.logging_info
if logging_info is None:
print(f"[{run_idx}/{num_runs}] Stage breakdown unavailable: no logging_info")
return None
@@ -65,8 +66,8 @@ def print_stage_breakdown(
return total
def extract_sr_forward_latency(result: dict, ) -> tuple[float | None, list[tuple[str, float]], list[str]]:
logging_info = result.get("logging_info")
def extract_sr_forward_latency(result: GenerationResult, ) -> tuple[float | None, list[tuple[str, float]], list[str]]:
logging_info = result.logging_info
if logging_info is None:
return None, [], []
@@ -96,11 +97,11 @@ def extract_sr_forward_latency(result: dict, ) -> tuple[float | None, list[tuple
def collect_stage_times(
result: dict,
result: GenerationResult,
stage_times: dict[str, list[float]],
stage_order: OrderedDict[str, None],
) -> None:
logging_info = result.get("logging_info")
logging_info = result.logging_info
if logging_info is None:
return
stages = getattr(logging_info, "stages", None)
@@ -206,27 +207,40 @@ def main() -> None:
"dynamic": False,
}
generator = VideoGenerator.from_pretrained(
model_root,
num_gpus=1,
ltx2_refine_enabled=True,
ltx2_refine_upsampler_path=str(refine_upsampler_path),
refine_lora_path="", # keep refine LoRA disabled in this repo's typed adapter
ltx2_refine_lora_path="", # keep refine LoRA disabled for distilled model
ltx2_refine_num_inference_steps=2,
ltx2_refine_guidance_scale=1.0,
ltx2_refine_add_noise=True,
pipeline_config=pipeline_config,
enable_torch_compile=True,
enable_torch_compile_text_encoder=True,
enable_torch_compile_vae=True,
torch_compile_kwargs=torch_compile_kwargs,
torch_compile_kwargs_vae=torch_compile_kwargs,
dit_cpu_offload=False,
text_encoder_cpu_offload=False,
vae_cpu_offload=False,
ltx2_vae_tiling=False,
)
generator = VideoGenerator.from_config({
"model_path": model_root,
"engine": {
"num_gpus": 1,
"compile": {
"enabled": True,
"text_encoder_enabled": True,
"vae_enabled": True,
**torch_compile_kwargs,
"vae_kwargs": torch_compile_kwargs,
},
"offload": {
"dit": False,
"text_encoder": False,
"vae": False,
},
},
"pipeline": {
"preset_overrides": {
"refine": {
"enabled": True,
"num_inference_steps": 2,
"guidance_scale": 1.0,
"add_noise": True,
},
},
"components": {"upsampler_weights": str(refine_upsampler_path)},
"model": {"ltx2": {"refine": {"lora_path": ""}}}, # keep refine LoRA disabled for distilled model
"experimental": {
"pipeline_config": pipeline_config,
},
"vae_tiling": False,
},
})
run_times: list[float] = []
e2e_times: list[float] = []
@@ -247,25 +261,26 @@ def main() -> None:
torch.cuda.synchronize()
start = time.perf_counter()
result = generator.generate_video(
prompt=prompt,
output_path=str(output_path),
fps=24,
seed=10,
save_video=True,
guidance_scale=1.0,
height=benchmark_entry.get("height", 1088),
width=benchmark_entry.get("width", 1920),
num_frames=121,
num_inference_steps=5,
# image_path="examples/inference/basic/prompt1.png",
# ltx2_image_crf=0.0
)
result = generator.generate({
"prompt": prompt,
"sampling": {
"fps": 24,
"seed": 10,
"guidance_scale": 1.0,
"height": benchmark_entry.get("height", 1088),
"width": benchmark_entry.get("width", 1920),
"num_frames": 121,
"num_inference_steps": 5,
},
"output": {"output_path": str(output_path), "save_video": True},
# "inputs": {"image_path": "examples/inference/basic/prompt1.png"},
# "extensions": {"ltx2_image_crf": 0.0},
})
if os.environ.get("FASTVIDEO_STAGE_LOGGING") == "0":
torch.cuda.synchronize()
elapsed = result.get("generation_time") if isinstance(result, dict) else None
e2e_elapsed = result.get("e2e_latency") if isinstance(result, dict) else None
elapsed = result.generation_time if isinstance(result, GenerationResult) else None
e2e_elapsed = result.extra.get("e2e_latency") if isinstance(result, GenerationResult) else None
if elapsed is None:
elapsed = time.perf_counter() - start
if e2e_elapsed is None:
@@ -276,7 +291,7 @@ def main() -> None:
print(f"[{i + 1}/{num_runs}] Generation time: {elapsed:.2f}s")
print(f"[{i + 1}/{num_runs}] End-to-end latency: {e2e_elapsed:.2f}s")
if isinstance(result, dict):
if isinstance(result, GenerationResult):
stage_sum = print_stage_breakdown(result, i + 1, num_runs)
if stage_sum is not None:
non_stage_overhead = e2e_elapsed - stage_sum
+25 -18
View File
@@ -6,12 +6,18 @@ OUTPUT_PATH = "video_samples_lucy_edit"
def main():
generator = VideoGenerator.from_pretrained(
"decart-ai/Lucy-Edit-Dev",
num_gpus=1,
use_fsdp_inference=False,
dit_cpu_offload=True,
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False,
"offload": {
"dit": True,
"vae": False,
"text_encoder": True,
"pin_cpu_memory": True,
},
},
},
)
prompt = ("Change the apron and blouse to a classic clown costume: satin "
@@ -20,18 +26,19 @@ def main():
"foam nose; soft window light from left, eye-level medium shot.")
video_path = "https://d2drjpuinn46lb.cloudfront.net/painter_original_edit.mp4"
generator.generate_video(
prompt,
negative_prompt="",
video_path=video_path,
output_path=OUTPUT_PATH,
save_video=True,
height=480,
width=832,
num_frames=81,
fps=24,
guidance_scale=5.0,
)
generator.generate({
"prompt": prompt,
"negative_prompt": "",
"inputs": {"video_path": video_path},
"sampling": {
"height": 480,
"width": 832,
"num_frames": 81,
"fps": 24,
"guidance_scale": 5.0,
},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
if __name__ == "__main__":
+30 -21
View File
@@ -51,33 +51,42 @@ def main():
generator = VideoGenerator.from_pretrained(
config["model_path"],
# FastVideo will automatically handle distributed setup
num_gpus=1,
use_fsdp_inference=False, # set to True if GPU is out of memory
dit_cpu_offload=True, # DiT need to be offloaded for MoE
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
pin_cpu_memory=True,
# image_encoder_cpu_offload=False,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set to True if GPU is out of memory
"offload": {
"dit": True, # DiT need to be offloaded for MoE
"vae": False,
"text_encoder": True,
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
"pin_cpu_memory": True,
# "image_encoder": False,
},
},
},
)
num_frames = 597
actions = create_action_presets(num_frames, keyboard_dim=config["keyboard_dim"])
grid_sizes = torch.tensor([150, 44, 80])
generator.generate_video(
prompt="",
image_path=config["image_url"],
mouse_cond=actions["mouse"].unsqueeze(0),
keyboard_cond=actions["keyboard"].unsqueeze(0),
grid_sizes=grid_sizes,
num_frames=num_frames,
height=352,
width=640,
num_inference_steps=50,
output_path=OUTPUT_PATH,
save_video=True,
)
generator.generate({
"prompt": "",
"inputs": {
"image_path": config["image_url"],
"mouse_cond": actions["mouse"].unsqueeze(0),
"keyboard_cond": actions["keyboard"].unsqueeze(0),
"grid_sizes": grid_sizes,
},
"sampling": {
"num_frames": num_frames,
"height": 352,
"width": 640,
"num_inference_steps": 50,
},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
if __name__ == "__main__":
@@ -58,14 +58,20 @@ async def main():
generator = StreamingVideoGenerator.from_pretrained(
config["model_path"],
# FastVideo will automatically handle distributed setup
num_gpus=1,
use_fsdp_inference=False, # set to True if GPU is out of memory
dit_cpu_offload=True, # DiT need to be offloaded for MoE
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
pin_cpu_memory=True,
# image_encoder_cpu_offload=False,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False, # set to True if GPU is out of memory
"offload": {
"dit": True, # DiT need to be offloaded for MoE
"vae": False,
"text_encoder": True,
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
"pin_cpu_memory": True,
# "image_encoder": False,
},
},
},
)
max_blocks = 50
+25 -18
View File
@@ -9,26 +9,33 @@ OUTPUT_PATH = "video_samples_matrixgame3"
def main():
generator = VideoGenerator.from_pretrained(
MODEL_PATH,
num_gpus=1,
use_fsdp_inference=False,
dit_cpu_offload=False,
vae_cpu_offload=False,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
{
"engine": {
"num_gpus": 1,
"use_fsdp_inference": False,
"offload": {
"dit": False,
"vae": False,
"text_encoder": True,
"pin_cpu_memory": True,
},
},
},
)
generator.generate_video(
prompt=PROMPT,
image_path=IMAGE_URL,
height=720,
width=1280,
num_frames=57,
num_inference_steps=3,
guidance_scale=1.0,
seed=42,
output_path=OUTPUT_PATH,
save_video=True,
)
generator.generate({
"prompt": PROMPT,
"inputs": {"image_path": IMAGE_URL},
"sampling": {
"height": 720,
"width": 1280,
"num_frames": 57,
"num_inference_steps": 3,
"guidance_scale": 1.0,
"seed": 42,
},
"output": {"output_path": OUTPUT_PATH, "save_video": True},
})
if __name__ == "__main__":
@@ -16,7 +16,6 @@ from fastvideo.api import (
OffloadConfig,
OutputConfig,
ParallelismConfig,
PipelineSelection,
SamplingConfig,
)
@@ -82,17 +81,10 @@ def main() -> None:
output_dir = Path(args.output)
output_dir.mkdir(parents=True, exist_ok=True)
# Boot-time run configuration folded into FastVideoArgs (the same
# experimental-dict route basic_fasth3.py uses for the VSA knobs).
experimental: dict[str, object] = {}
if args.inference_torch_compile:
experimental["inference_torch_compile"] = True
execution_backend = args.execution_backend or ("ray" if os.environ.get("RAY_ADDRESS") else "mp")
generator = VideoGenerator.from_config(
GeneratorConfig(
model_path=args.model_path,
pipeline=PipelineSelection(experimental=experimental),
engine=EngineConfig(
num_gpus=args.num_gpus,
execution_backend=execution_backend,
@@ -110,6 +102,7 @@ def main() -> None:
enabled=args.torch_compile,
mode=args.compile_mode,
vae_enabled=args.compile_vae,
regional=True if args.inference_torch_compile else None,
),
),
))

Some files were not shown because too many files have changed in this diff Show More