From a63ccce73df98bbf6b0e0a34c136a5826704e679 Mon Sep 17 00:00:00 2001 From: KyleNeverGivesUp <160547796+KyleNeverGivesUp@users.noreply.github.com> Date: Thu, 20 Aug 2026 23:53:44 -0700 Subject: [PATCH] [docs]: add a maintained inference cookbook (#1290) Co-authored-by: SolitaryThinker --- .github/workflows/infra-docs.yml | 2 + docs/assets/cookbook-recipes.json | 52 ++++++++++++++++++++++++ docs/assets/cookbook.js | 60 ++++++++++++++++++++++++++++ docs/assets/custom.css | 40 +++++++++++++++++++ docs/cookbook/index.md | 44 ++++++++++++++++++++ docs/generate_examples.py | 37 +++++++++++++++++ docs/getting_started/installation.md | 5 ++- docs/getting_started/quick_start.md | 58 +++++---------------------- mkdocs.yml | 4 +- 9 files changed, 250 insertions(+), 52 deletions(-) create mode 100644 docs/assets/cookbook-recipes.json create mode 100644 docs/assets/cookbook.js create mode 100644 docs/cookbook/index.md diff --git a/.github/workflows/infra-docs.yml b/.github/workflows/infra-docs.yml index 4166d4799..69aba4c84 100644 --- a/.github/workflows/infra-docs.yml +++ b/.github/workflows/infra-docs.yml @@ -6,6 +6,7 @@ on: paths: - 'docs/**' - 'examples/**' + - 'scripts/inference/**' - 'mkdocs.yml' - 'requirements-mkdocs.in' - 'requirements-mkdocs.txt' @@ -16,6 +17,7 @@ on: paths: - 'docs/**' - 'examples/**' + - 'scripts/inference/**' - 'mkdocs.yml' - 'requirements-mkdocs.in' - 'requirements-mkdocs.txt' diff --git a/docs/assets/cookbook-recipes.json b/docs/assets/cookbook-recipes.json new file mode 100644 index 000000000..eadeee535 --- /dev/null +++ b/docs/assets/cookbook-recipes.json @@ -0,0 +1,52 @@ +{ + "recipes": [ + { + "id": "fastwan21-t2v", + "task": "Text to video", + "label": "FastWan2.1 1.3B (distilled + VSA)", + "model": "FastVideo/FastWan2.1-T2V-1.3B-Diffusers", + "source": "scripts/inference/inference_wan_VSA_DMD_1_3B.yaml", + "command": "FASTVIDEO_ATTENTION_BACKEND=VIDEO_SPARSE_ATTN fastvideo generate --config scripts/inference/inference_wan_VSA_DMD_1_3B.yaml" + }, + { + "id": "wan22-t2v", + "task": "Text to video", + "label": "Wan2.2 A14B", + "model": "Wan-AI/Wan2.2-T2V-A14B-Diffusers", + "source": "examples/inference/basic/basic_wan2_2.py", + "command": "python examples/inference/basic/basic_wan2_2.py" + }, + { + "id": "wan21-i2v", + "task": "Image to video", + "label": "Wan2.1 14B 480P", + "model": "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers", + "source": "scripts/inference/inference_wan_i2v.yaml", + "command": "fastvideo generate --config scripts/inference/inference_wan_i2v.yaml" + }, + { + "id": "turbowan22-i2v", + "task": "Image to video", + "label": "TurboWan2.2 A14B", + "model": "loayrashid/TurboWan2.2-I2V-A14B-Diffusers", + "source": "examples/inference/basic/basic_turbodiffusion_i2v.py", + "command": "python examples/inference/basic/basic_turbodiffusion_i2v.py" + }, + { + "id": "wan22-ti2v", + "task": "Text or image to video", + "label": "Wan2.2 TI2V 5B", + "model": "Wan-AI/Wan2.2-TI2V-5B-Diffusers", + "source": "examples/inference/basic/basic_wan2_2_ti2v.py", + "command": "python examples/inference/basic/basic_wan2_2_ti2v.py" + }, + { + "id": "matrix-game-2", + "task": "Interactive world", + "label": "Matrix Game 2.0", + "model": "FastVideo/Matrix-Game-2.0-Base-Distilled-Diffusers", + "source": "examples/inference/basic/basic_matrixgame2.py", + "command": "python examples/inference/basic/basic_matrixgame2.py" + } + ] +} diff --git a/docs/assets/cookbook.js b/docs/assets/cookbook.js new file mode 100644 index 000000000..1b985bc4e --- /dev/null +++ b/docs/assets/cookbook.js @@ -0,0 +1,60 @@ +(() => { + let recipesPromise; + + const loadRecipes = (url) => { + recipesPromise ||= fetch(url).then((response) => { + if (!response.ok) throw new Error(`HTTP ${response.status}`); + return response.json(); + }); + return recipesPromise; + }; + + const init = () => { + document.querySelectorAll("[data-cookbook]").forEach(async (root) => { + if (root.dataset.initialized) return; + root.dataset.initialized = "true"; + + const select = root.querySelector("[data-cookbook-recipe]"); + const model = root.querySelector("[data-cookbook-model]"); + const source = root.querySelector("[data-cookbook-source]"); + const command = root.querySelector("[data-cookbook-command]"); + const status = root.querySelector("[data-cookbook-status]"); + + try { + const { recipes } = await loadRecipes(root.dataset.recipes); + const byId = new Map(recipes.map((recipe) => [recipe.id, recipe])); + const groups = new Map(); + + select.replaceChildren(); + recipes.forEach((recipe) => { + if (!groups.has(recipe.task)) { + const group = document.createElement("optgroup"); + group.label = recipe.task; + groups.set(recipe.task, group); + select.append(group); + } + groups.get(recipe.task).append(new Option(recipe.label, recipe.id)); + }); + + const render = () => { + const recipe = byId.get(select.value); + model.textContent = recipe.model; + source.textContent = recipe.source; + source.href = `https://github.com/hao-ai-lab/FastVideo/blob/main/${recipe.source}`; + command.textContent = recipe.command; + status.textContent = `${recipe.label} selected.`; + }; + + select.addEventListener("change", render); + select.disabled = false; + render(); + } catch (error) { + status.textContent = "Recipes could not be loaded. Use the examples link below."; + console.error("Failed to load FastVideo cookbook recipes", error); + } + }); + }; + + if (window.document$) window.document$.subscribe(init); + else document.addEventListener("DOMContentLoaded", init); +})(); diff --git a/docs/assets/custom.css b/docs/assets/custom.css index a8278f62e..b87ef67c8 100644 --- a/docs/assets/custom.css +++ b/docs/assets/custom.css @@ -42,6 +42,46 @@ img { margin: 0 auto; } +.cookbook-picker { + padding: 1rem; + border: 0.05rem solid var(--md-default-fg-color--lightest); + border-radius: 0.2rem; +} + +.cookbook-picker select { + width: 100%; + padding: 0.6rem; + color: var(--md-default-fg-color); + background: var(--md-default-bg-color); + border: 0.05rem solid var(--md-default-fg-color--lighter); + border-radius: 0.2rem; +} + +.cookbook-picker dl { + display: grid; + grid-template-columns: max-content 1fr; + gap: 0.25rem 1rem; +} + +.cookbook-picker dt { + font-weight: 700; +} + +.cookbook-picker dd { + margin: 0; + min-width: 0; + overflow-wrap: anywhere; +} + +.cookbook-picker__status { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; +} + .md-typeset .copy-page-button.md-button { float: right; margin: 0 0 1rem 1rem; diff --git a/docs/cookbook/index.md b/docs/cookbook/index.md new file mode 100644 index 000000000..b5d408f6c --- /dev/null +++ b/docs/cookbook/index.md @@ -0,0 +1,44 @@ +# Inference Cookbook + +Choose a complete recipe maintained in the FastVideo repository. Each command +runs its checked-in source directly, so coupled model, GPU, offload, and +attention settings do not drift into unsupported combinations. + +The commands expect a local clone: + +```bash +git clone https://github.com/hao-ai-lab/FastVideo.git +cd FastVideo +``` + +
+ + +
+
Model
+
Loading…
+
Source
+
Browse maintained examples
+
+
Loading…
+

+ +
+ +## Customize a recipe + +Start from the checked-in source, then change only the settings your model +supports: + +- [Configuration](../inference/configuration.md) covers the Python and CLI + config surfaces. +- [Optimizations](../inference/optimizations.md) covers attention backends, + compilation, and memory tradeoffs. +- [Support matrix](../inference/support_matrix.md) lists supported models and + optimizations. diff --git a/docs/generate_examples.py b/docs/generate_examples.py index 6a57397a7..1e85d5d4a 100644 --- a/docs/generate_examples.py +++ b/docs/generate_examples.py @@ -2,6 +2,7 @@ # adapted from vllm: https://github.com/vllm-project/vllm/blob/v0.7.3/docs/source/generate_examples.py import itertools +import json import os import re from dataclasses import dataclass, field @@ -19,6 +20,40 @@ GENERATED_DOC_PREFIXES = ( "training/examples/", "distillation/examples/", ) +COOKBOOK_DATA = ROOT_DIR / "docs/assets/cookbook-recipes.json" +COOKBOOK_SOURCE_ROOTS = ( + ROOT_DIR / "examples/inference", + ROOT_DIR / "scripts/inference", +) + + +def validate_cookbook() -> None: + """Keep cookbook entries tied to checked-in runnable sources.""" + recipes = json.loads(COOKBOOK_DATA.read_text(encoding="utf-8")).get("recipes") + if not isinstance(recipes, list) or not recipes: + raise ValueError(f"{COOKBOOK_DATA}: recipes must be a non-empty list") + + seen: set[str] = set() + for recipe in recipes: + required = ("id", "task", "label", "model", "source", "command") + missing = {key for key in required if not recipe.get(key)} + if missing: + raise ValueError(f"Cookbook recipe is missing: {', '.join(sorted(missing))}") + if recipe["id"] in seen: + raise ValueError(f"Duplicate cookbook recipe id: {recipe['id']}") + seen.add(recipe["id"]) + + source = (ROOT_DIR / recipe["source"]).resolve() + if not any(source.is_relative_to(root.resolve()) for root in COOKBOOK_SOURCE_ROOTS): + raise ValueError(f"Cookbook source is outside an approved directory: {recipe['source']}") + if not source.is_file(): + raise ValueError(f"Cookbook source does not exist: {recipe['source']}") + + source_text = source.read_text(encoding="utf-8") + if recipe["model"] not in source_text: + raise ValueError(f"Cookbook model is not present in {recipe['source']}: {recipe['model']}") + if recipe["source"] not in recipe["command"]: + raise ValueError(f"Cookbook command does not invoke its source: {recipe['id']}") def fix_case(text: str) -> str: @@ -536,6 +571,7 @@ def on_pre_build(config, **kwargs): MkDocs hook to generate examples before building the documentation. This function is called automatically by MkDocs' native hook system. """ + validate_cookbook() print("Generating example documentation...") generate_examples(generate_main_index=True) print("Example documentation generated successfully!") @@ -549,6 +585,7 @@ def on_page_context(context, page, **kwargs): if __name__ == "__main__": + validate_cookbook() print("Generating example documentation...") generate_examples(generate_main_index=True) print("Example documentation generated successfully!") diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md index 47e06bd39..6d5c29639 100644 --- a/docs/getting_started/installation.md +++ b/docs/getting_started/installation.md @@ -65,6 +65,7 @@ uv pip install flash-attn --no-build-isolation -v ## Next Steps -- [Quick Start Guide](quick_start.md) - Get started with your first video generation +- [Quick Start](quick_start.md) - Generate your first video +- [Inference Cookbook](../cookbook/index.md) - Choose a maintained recipe - [Configuration](../inference/configuration.md) - Learn about configuration options -- [Examples](../inference/examples/examples_inference_index.md) - Explore example scripts and notebooks +- [Examples](../inference/examples/examples_inference_index.md) - Explore scripts and notebooks diff --git a/docs/getting_started/quick_start.md b/docs/getting_started/quick_start.md index 644f0e672..34701f3c8 100644 --- a/docs/getting_started/quick_start.md +++ b/docs/getting_started/quick_start.md @@ -23,61 +23,21 @@ Also optionally install flash-attn: uv pip install flash-attn --no-build-isolation -v ``` -## Basic Usage +## Choose a maintained recipe -### Text-to-Video Generation +The cookbook selects complete, checked-in recipes instead of mixing model, +parallelism, offload, and attention settings independently. -```python -from fastvideo import VideoGenerator +[Open the inference cookbook](../cookbook/index.md){ .md-button .md-button--primary } -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 - ) - - # 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 - ) - -if __name__ == '__main__': - main() -``` - -### Image-to-Video Generation - -```python -from fastvideo import VideoGenerator, SamplingParam - -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 - - # 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) - -if __name__ == '__main__': - main() -``` +!!! tip "Need more control?" + Start from a maintained recipe, then use the + [configuration](../inference/configuration.md) and + [optimization](../inference/optimizations.md) guides for supported changes. ## Next Steps +- [Inference Cookbook](../cookbook/index.md) - Choose a maintained recipe - [Installation Guide](installation.md) - Detailed installation instructions - [Configuration](../inference/configuration.md) - Learn about configuration options - [Examples](../inference/examples/examples_inference_index.md) - Explore more diff --git a/mkdocs.yml b/mkdocs.yml index caf72e898..e2180d61c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -78,7 +78,7 @@ plugins: minify_js: true minify_css: true cache_safe: true - js_files: [assets/copy-page.js] + js_files: [assets/copy-page.js, assets/cookbook.js] css_files: [assets/custom.css] - api-autonav: modules: ["fastvideo"] @@ -154,6 +154,7 @@ nav: - Apple Silicon FastWan: getting_started/installation/mps.md - Quick Start: getting_started/quick_start.md - V1 API: getting_started/v1_api.md + - Cookbook: cookbook/index.md - Inference: - Quick Start: inference/inference_quick_start.md - Configuration: inference/configuration.md @@ -238,3 +239,4 @@ extra_css: # Custom JavaScript extra_javascript: - assets/copy-page.js + - assets/cookbook.js