Files
hao-ai-lab-FastVideo/examples/inference/basic/basic_fasth3_simplified.py
T
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

145 lines
4.9 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Generate 5-second FastH3 videos with a supported Preview adapter."""
from __future__ import annotations
import argparse
import os
import statistics
import sys
import time
from pathlib import Path
from huggingface_hub import hf_hub_download
FASTVIDEO_ROOT = Path(__file__).resolve().parents[3]
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,
MiniMaxH3Config,
OffloadConfig,
OutputConfig,
ParallelismConfig,
PipelineSelection,
SamplingConfig,
)
# FastVideo exposes these three backend switches only through environment variables.
os.environ.update({
"FASTVIDEO_FA4": "1",
"FASTVIDEO_MINIMAX_H3_FUSIONS": "all",
"FASTVIDEO_VSA_SM100A": "0",
})
os.environ.pop("FASTVIDEO_INFERENCE_TORCH_COMPILE", None)
VARIANT_BACKENDS = {
"dense-datafree": "FLASH_ATTN",
"vsa-datafree": "VIDEO_SPARSE_ATTN_H3",
"vsa-synthetic-step1300": "VIDEO_SPARSE_ATTN_H3",
"vsa-synthetic-step1900": "VIDEO_SPARSE_ATTN_H3",
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("variant", choices=VARIANT_BACKENDS)
parser.add_argument("--prompt", required=True)
return parser.parse_args()
def main() -> None:
"""Run one compile warmup and three measured generations with one fixed recipe."""
args = parse_args()
attention_backend = VARIANT_BACKENDS[args.variant]
use_vsa = attention_backend == "VIDEO_SPARSE_ATTN_H3"
adapter_path = hf_hub_download(
repo_id="FastVideo/FastVideo-FastH3-4-step-Preview-v1-LoRA",
filename=f"{args.variant}/adapter_model.safetensors",
)
output_dir = FASTVIDEO_ROOT / "outputs/fasth3_lora_preview" / args.variant
output_dir.mkdir(parents=True, exist_ok=True)
generator = VideoGenerator.from_config(
GeneratorConfig(
model_path="MiniMaxAI/MiniMax-H3",
pipeline=PipelineSelection(
components=ComponentConfig(lora_path=adapter_path, lora_strength=1.0),
minimax_h3=MiniMaxH3Config(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, 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,
),
),
)
)
generation_count = 4
warmup_count = 1
measured_count = generation_count - warmup_count
measured_seconds: list[float] = []
print(f"Variant: {args.variant} ({attention_backend})")
print(f"Output directory: {output_dir}")
try:
for generation_index in range(generation_count):
measured = generation_index >= warmup_count
if measured:
measured_index = generation_index - warmup_count + 1
label = f"measured {measured_index}/{measured_count}"
output_path = output_dir / f"fasth3_all_run_{measured_index:02d}.mp4"
else:
warmup_index = generation_index + 1
label = f"warmup {warmup_index}/{warmup_count}"
output_path = output_dir / f"_fasth3_warmup_{warmup_index:02d}.mp4"
request = GenerationRequest(
prompt=args.prompt,
negative_prompt="",
sampling=SamplingConfig(
height=768,
width=1344,
num_frames=345, # <-- Change video length: 5sec: 124; 10sec: 243; 15sec: 345.
fps=24,
num_inference_steps=5,
guidance_scale=1.0,
batch_cfg=False,
seed=1000,
),
output=OutputConfig(
output_path=str(output_path),
save_video=True,
return_frames=False,
),
)
started = time.perf_counter()
result = generator.generate(request)
elapsed = time.perf_counter() - started
if measured:
measured_seconds.append(elapsed)
suffix = "" if measured else " (excluded from median)"
print(f"[{label}] {result.video_path or output_path}: {elapsed:.3f}s{suffix}")
finally:
generator.shutdown()
print(f"Median E2E wall time: {statistics.median(measured_seconds):.3f}s")
if __name__ == "__main__":
main()