[refactor]: migrate examples/scripts to typed VideoGenerator API; rename api/compat.py to api/translation.py

Move all examples/inference and scripts off the legacy
VideoGenerator.from_pretrained(model, **kwargs) +
generate_video(prompt, **kwargs) surface onto the typed
from_config(GeneratorConfig(...)) + generate(GenerationRequest(...))
convention, including the Kandinsky-5 and DreamX-World examples added
on main after the original migration. git-mv api/compat.py to
api/translation.py ('compat' implied a temporary shim; the forward
translation is its honest permanent role) and repoint all importers,
tests, and doc links.
This commit is contained in:
SolitaryThinker
2026-07-11 03:26:24 -07:00
parent 19a51a1fe6
commit bbbb7ab021
87 changed files with 2493 additions and 1461 deletions
@@ -13,7 +13,8 @@ The base DiT uses Wan-AI/Wan2.2-TI2V-5B's VAE and google/t5gemma-9b-9b-ul2's
encoder at inference time; neither is bundled upstream.
This converter takes the raw MagiHuman base DiT and emits a Diffusers-style
directory so `VideoGenerator.from_pretrained(...)` can load it standalone:
directory so `VideoGenerator.from_config(GeneratorConfig(model_path=...))` can
load it standalone:
<output>/
model_index.json
@@ -683,21 +683,23 @@ def main():
print()
print(" 1. Test basic generation:")
print(" from fastvideo import VideoGenerator")
print(f" generator = VideoGenerator.from_pretrained('{output_dir}')")
print(" video = generator.generate_video(")
print(" from fastvideo.api import GeneratorConfig, GenerationRequest, SamplingConfig")
print(f" generator = VideoGenerator.from_config(GeneratorConfig(model_path='{output_dir}'))")
print(" result = generator.generate(GenerationRequest(")
print(" prompt='A cat playing piano',")
print(" num_inference_steps=50")
print(" )")
print(" sampling=SamplingConfig(num_inference_steps=50),")
print(" ))")
print()
if (output_dir / "lora" / "distilled").exists():
print(" 2. Test distilled generation (16 steps with LoRA):")
print(f" generator = VideoGenerator.from_pretrained('{output_dir}',")
print(f" lora_path='{output_dir}/lora/distilled',")
print(" lora_nickname='distilled')")
print(" video = generator.generate_video(")
print(" from fastvideo.api import ComponentConfig, PipelineSelection")
print(f" generator = VideoGenerator.from_config(GeneratorConfig(model_path='{output_dir}',")
print(f" pipeline=PipelineSelection(")
print(f" components=ComponentConfig(lora_path='{output_dir}/lora/distilled'),")
print(" experimental={'lora_nickname': 'distilled'})))")
print(" result = generator.generate(GenerationRequest(")
print(" prompt='A cat playing piano',")
print(" num_inference_steps=16,")
print(" guidance_scale=1.0)")
print(" sampling=SamplingConfig(num_inference_steps=16, guidance_scale=1.0)))")
print()
print()
@@ -18,7 +18,8 @@ Example (one-shot per variant):
--public
After upload, the local directory can be deleted — the HF repo is the
source of truth. `VideoGenerator.from_pretrained("FastVideo/...")` pulls
source of truth.
`VideoGenerator.from_config(GeneratorConfig(model_path="FastVideo/..."))` pulls
shards on demand.
"""
from __future__ import annotations
@@ -97,42 +97,53 @@ def generate_with_model(
flow_shift: Optional[float] = None,
embedded_guidance_scale: Optional[float] = None,
) -> str:
"""Produce a video with VideoGenerator.from_pretrained; returns video path."""
"""Produce a video with VideoGenerator.from_config; returns video path."""
try:
from fastvideo import VideoGenerator # lazy import
from fastvideo.api import (
ComponentConfig, EngineConfig, GenerationRequest, GeneratorConfig,
OffloadConfig, OutputConfig, PipelineSelection, SamplingConfig,
)
except Exception as exc:
raise RuntimeError(f"Failed to import fastvideo.VideoGenerator: {exc}") from exc
init_kwargs: Dict[str, Any] = {
"num_gpus": 1,
"dit_cpu_offload": True,
"vae_cpu_offload": True,
"text_encoder_cpu_offload": True,
"pin_cpu_memory": True,
}
pipeline_kwargs: Dict[str, Any] = {}
if lora_path:
init_kwargs["lora_path"] = lora_path
init_kwargs["lora_nickname"] = "extracted"
pipeline_kwargs["components"] = ComponentConfig(lora_path=lora_path)
pipeline_kwargs["experimental"] = {"lora_nickname": "extracted"}
generator = VideoGenerator.from_pretrained(model_path, **init_kwargs)
generator = VideoGenerator.from_config(GeneratorConfig(
model_path=model_path,
engine=EngineConfig(
num_gpus=1,
offload=OffloadConfig(dit=True, vae=True, text_encoder=True, pin_cpu_memory=True),
),
pipeline=PipelineSelection(**pipeline_kwargs),
))
gen_kwargs = {
"height": height,
"width": width,
"num_frames": num_frames,
"num_inference_steps": num_inference_steps,
"guidance_scale": guidance_scale,
"seed": seed,
"output_path": output_dir,
"output_video_name": output_name,
"save_video": True,
}
extensions: Dict[str, Any] = {}
if flow_shift is not None:
gen_kwargs["flow_shift"] = flow_shift
extensions["flow_shift"] = flow_shift
if embedded_guidance_scale is not None:
gen_kwargs["embedded_guidance_scale"] = embedded_guidance_scale
extensions["embedded_guidance_scale"] = embedded_guidance_scale
result = generator.generate_video(prompt, **gen_kwargs)
result = generator.generate(GenerationRequest(
prompt=prompt,
sampling=SamplingConfig(
height=height,
width=width,
num_frames=num_frames,
num_inference_steps=num_inference_steps,
guidance_scale=guidance_scale,
seed=seed,
),
output=OutputConfig(
output_path=output_dir,
output_video_name=output_name,
save_video=True,
),
extensions=extensions,
))
# best-effort cleanup of internal executors
try:
@@ -145,9 +156,9 @@ def generate_with_model(
expected = Path(output_dir) / f"{output_name}.mp4"
if expected.exists():
return str(expected)
# fallback: check result dict
if isinstance(result, dict) and "video_path" in result:
return str(result["video_path"])
# fallback: check result video path
if getattr(result, "video_path", None):
return str(result.video_path)
raise FileNotFoundError(f"Video not found at expected path: {expected}")