+
Model
-
{config?.modelShort ?? "Unavailable"}
+
{config?.name ?? "Unavailable"}
-
Est. Generation Time
-
{config ? formatTime(config.timeSec) : "Unavailable"}
+
Resolution
+
{config ? `${config.width}×${config.height}` : "Unavailable"}
-
Peak VRAM
-
{config?.vram ?? "Unavailable"}
+
Denoising
+
{config ? `${config.numInferenceSteps} steps` : "Unavailable"}
{/* Command Block */}
{command && (
@@ -535,7 +430,7 @@ export default function FastVideoConfigSelector() {
) : (
- {`${hardware.label} (${vram} GB) is not suitable for ${tier === "quick" ? "Quick Generate" : "High Quality"} on this task.`}
+ No maintained profile is available for this task and model choice.
)}
diff --git a/consumer-guide/app/globals.css b/consumer-guide/app/globals.css
index beb4040a7..09c52ed18 100644
--- a/consumer-guide/app/globals.css
+++ b/consumer-guide/app/globals.css
@@ -75,8 +75,8 @@
}
@theme inline {
- --font-sans: 'Geist', 'Geist Fallback';
- --font-mono: 'Geist Mono', 'Geist Mono Fallback';
+ --font-sans: ui-sans-serif, system-ui, sans-serif;
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
diff --git a/consumer-guide/app/layout.tsx b/consumer-guide/app/layout.tsx
index 681bd0609..734dfa378 100644
--- a/consumer-guide/app/layout.tsx
+++ b/consumer-guide/app/layout.tsx
@@ -1,13 +1,9 @@
import type { Metadata } from 'next'
-import { Geist, Geist_Mono } from 'next/font/google'
import './globals.css'
-const _geist = Geist({ subsets: ["latin"] });
-const _geistMono = Geist_Mono({ subsets: ["latin"] });
-
export const metadata: Metadata = {
title: 'FastVideo Config Generator',
- description: 'Choose your hardware and task to generate a FastVideo configuration command',
+ description: 'Choose a maintained FastVideo recipe and generate an executable configuration',
}
export default function RootLayout({
diff --git a/consumer-guide/app/tuning/adv_page.tsx b/consumer-guide/app/tuning/adv_page.tsx
index 7b3080ba7..9c2c578ec 100644
--- a/consumer-guide/app/tuning/adv_page.tsx
+++ b/consumer-guide/app/tuning/adv_page.tsx
@@ -1,36 +1,67 @@
"use client"
-import { useState, useMemo, useEffect, type CSSProperties } from "react"
+import { useEffect, useId, useMemo, useState, type ElementType, type ReactNode } from "react"
import {
- Monitor, Cpu, Film, Sparkles, Zap, HardDrive, BarChart3,
- Copy, Check, Info, ChevronDown, ChevronUp, Settings2,
- Layers, Clock, Database, Gauge
+ BarChart3,
+ Check,
+ ChevronDown,
+ ChevronUp,
+ Copy,
+ Film,
+ HardDrive,
+ Image,
+ Info,
+ Layers,
+ Settings2,
+ Sparkles,
+ Zap,
} from "lucide-react"
import tuningData from "@/data/tuning.json"
-// Types
-interface Config {
- // Category A - Model & Hardware
- model_id: string
- workload_type: string
- gpu: string
- num_gpus: number
- // Category B - Video Spec
+interface Recipe {
height: number
width: number
- num_frames: number
+ numFrames: number
fps: number
- // Category C - Quality
- num_inference_steps: number
- guidance_scale: number
- embedded_cfg_scale: number
- flow_shift: number | null
+ numInferenceSteps: number
+ guidanceScale: number
+ guidanceScale2: number | null
+ embeddedCfgScale: number
+ boundaryRatio: number | null
+ dmdDenoisingSteps: number[] | null
+ attentionBackends: string[]
+ defaultAttentionBackend: string
+ vsaSparsity: number
+}
+
+interface ModelOption {
+ id: string
+ name: string
+ size: string
+ workload: string
+ keyboardDim?: number
+ usesMouse?: boolean
+ recipe: Recipe
+}
+
+interface WorkloadOption {
+ id: string
+ name: string
+ icon: string
+}
+
+interface AttentionBackend {
+ id: string
+ name: string
+ shortName: string
+ desc: string
+}
+
+interface Defaults {
+ model_id: string
+ workload_type: string
+ num_gpus: number
seed: number
- // Category D - Speed
- attention_backend: string
- VSA_sparsity: number
- dmd_denoising_steps: string
- // Category E - Memory
dit_cpu_offload: boolean
dit_layerwise_offload: boolean
vae_cpu_offload: boolean
@@ -40,189 +71,97 @@ interface Config {
use_fsdp_inference: boolean
}
-const SECTION_THEMES = {
- sky: {
- accent: "#3867f0",
- soft: "#e9eefc",
- tint: "#f8faff",
- border: "#c9d5fb",
- },
- emerald: {
- accent: "#089da8",
- soft: "#e7f7f8",
- tint: "#f4fbfb",
- border: "#9ddde2",
- },
- amber: {
- accent: "#f25a00",
- soft: "#fff0e8",
- tint: "#fff8f4",
- border: "#ffc6a6",
- },
- violet: {
- accent: "#00983f",
- soft: "#e9f8ef",
- tint: "#f6fcf8",
- border: "#9edeb7",
- },
- rose: {
- accent: "#a84ac2",
- soft: "#f6edf9",
- tint: "#fcf7fd",
- border: "#dfb6eb",
- },
- slate: {
- accent: "#475569",
- soft: "#f1f5f9",
- tint: "#f8fafc",
- border: "#cbd5e1",
- },
-}
-
-interface BenchmarkRun {
- model: string
- gpu: string
+interface Config extends Defaults {
height: number
width: number
- frames: number
- steps: number
- attention: string
- generationSeconds?: number
- runtimeSeconds?: number
+ num_frames: number
+ fps: number
+ num_inference_steps: number
+ guidance_scale: number
+ guidance_scale_2: number | null
+ embedded_cfg_scale: number
+ boundary_ratio: number | null
+ dmd_denoising_steps: number[] | null
+ attention_backend: string
+ VSA_sparsity: number
}
-interface ModelOption {
- id: string
- name: string
- size: string
- workload: string
- keyboardDim?: number
- dmdMode?: boolean
- dmdNote?: string
+const DEFAULTS = tuningData.defaults as Defaults
+const MODELS = tuningData.models as ModelOption[]
+const WORKLOADS = tuningData.workloads as WorkloadOption[]
+const ATTENTION_BACKENDS = tuningData.attentionBackends as AttentionBackend[]
+const MODEL_BY_ID = Object.fromEntries(MODELS.map((model) => [model.id, model])) as Record
+const ATTENTION_BY_ID = Object.fromEntries(ATTENTION_BACKENDS.map((backend) => [backend.id, backend])) as Record
+const DEFAULT_MODEL = MODEL_BY_ID[DEFAULTS.model_id] ?? MODELS[0]
+const SOURCE = tuningData.source
+
+const WORKLOAD_ICONS: Record = {
+ t2v: Film,
+ i2v: Image,
+ ti2v: Layers,
+ game: Zap,
}
-interface WorkloadOption {
- id: string
- name: string
- icon: string
+function withModel(base: Defaults | Config, model: ModelOption): Config {
+ const recipe = model.recipe
+ return {
+ ...base,
+ model_id: model.id,
+ workload_type: model.workload,
+ height: recipe.height,
+ width: recipe.width,
+ num_frames: recipe.numFrames,
+ fps: recipe.fps,
+ num_inference_steps: recipe.numInferenceSteps,
+ guidance_scale: recipe.guidanceScale,
+ guidance_scale_2: recipe.guidanceScale2,
+ embedded_cfg_scale: recipe.embeddedCfgScale,
+ boundary_ratio: recipe.boundaryRatio,
+ dmd_denoising_steps: recipe.dmdDenoisingSteps ? [...recipe.dmdDenoisingSteps] : null,
+ attention_backend: recipe.defaultAttentionBackend,
+ VSA_sparsity: recipe.vsaSparsity,
+ }
}
-interface GpuOption {
- id: string
- name: string
- vram: string
-}
-
-interface ResolutionPreset {
- height: number
- width: number
- label: string
-}
-
-interface FramePreset {
- frames: number
- label: string
-}
-
-interface AttentionBackend {
- id: string
- name: string
- shortName: string
- desc: string
- speed: string
-}
-
-const modelOptions = tuningData.models as ModelOption[]
-const workloadOptions = tuningData.workloads as WorkloadOption[]
-const gpuOptions = tuningData.gpus as GpuOption[]
-const resolutionPresets = tuningData.resolutionPresets as ResolutionPreset[]
-const framePresets = tuningData.framePresets as FramePreset[]
-const attentionBackends = tuningData.attentionBackends as AttentionBackend[]
-const BENCHMARK_RUNS = tuningData.runs as BenchmarkRun[]
-const MODEL_BY_ID = Object.fromEntries(modelOptions.map((model) => [model.id, model])) as Record
-const ATTENTION_BY_ID = Object.fromEntries(attentionBackends.map((backend) => [backend.id, backend])) as Record
-
-// Tooltip component
-function Tooltip({ content, side = "top", iconClassName }: { content: string; side?: "top" | "right" | "bottom"; iconClassName?: string }) {
- const [show, setShow] = useState(false)
- const tooltipClass =
- side === "right"
- ? "absolute left-full top-1/2 z-50 ml-2 w-72 -translate-y-1/2 rounded-lg border border-slate-200 bg-white p-3 text-xs text-slate-600 shadow-xl"
- : side === "bottom"
- ? "absolute top-full left-1/2 z-50 mt-2 w-64 -translate-x-1/2 rounded-lg border border-slate-200 bg-white p-3 text-xs text-slate-600 shadow-xl"
- : "absolute bottom-full left-1/2 z-50 mb-2 w-64 -translate-x-1/2 rounded-lg border border-slate-200 bg-white p-3 text-xs text-slate-600 shadow-xl"
- const arrowClass =
- side === "right"
- ? "absolute right-full top-1/2 -translate-y-1/2 border-4 border-transparent border-r-white"
- : side === "bottom"
- ? "absolute left-1/2 bottom-full -translate-x-1/2 border-4 border-transparent border-b-white"
- : "absolute left-1/2 top-full -translate-x-1/2 border-4 border-transparent border-t-white"
-
+function Help({ text }: { text: string }) {
return (
-
-
setShow(true)}
- onMouseLeave={() => setShow(false)}
- className={iconClassName ?? "ml-1.5 text-slate-500 transition-colors hover:text-[var(--section-color)]"}
- >
-
-
- {show && (
-
- )}
-
+
+
+
)
}
-// Collapsible section
function Section({
title,
icon: Icon,
- number,
children,
- defaultOpen = true,
- color = "sky"
}: {
title: string
- icon: React.ElementType
- number: string
- children: React.ReactNode
- defaultOpen?: boolean
- color?: "sky" | "emerald" | "amber" | "violet" | "rose" | "slate"
+ icon: ElementType
+ children: ReactNode
}) {
- const [open, setOpen] = useState(defaultOpen)
- const theme = SECTION_THEMES[color]
- const sectionStyle = {
- "--section-color": theme.accent,
- "--section-soft": theme.soft,
- "--section-tint": theme.tint,
- "--section-border": theme.border,
- } as CSSProperties
-
+ const [open, setOpen] = useState(true)
+ const contentId = useId()
return (
-
+
setOpen(!open)}
- className="flex w-full items-center justify-between p-5 transition-colors hover:bg-slate-50"
+ type="button"
+ className="flex w-full items-center justify-between p-5 text-left hover:bg-slate-50"
+ aria-expanded={open}
+ aria-controls={contentId}
+ onClick={() => setOpen((value) => !value)}
>
-
-
- {number}
-
-
-
{title}
-
- {open ? : }
+
+
+ {title}
+
+ {open ? : }
- {open && {children}
}
-
+ {open && {children}
}
+
)
}
-// Input components
function NumberInput({
label,
value,
@@ -230,37 +169,45 @@ function NumberInput({
min,
max,
step = 1,
- tooltip,
- unit
+ help,
+ disabled = false,
}: {
label: string
value: number
- onChange: (v: number) => void
+ onChange: (value: number) => void
min?: number
max?: number
step?: number
- tooltip?: string
- unit?: string
+ help?: string
+ disabled?: boolean
}) {
+ const id = useId()
return (
)
}
@@ -270,33 +217,28 @@ function SelectInput({
value,
onChange,
options,
- tooltip,
- disabled = false
+ help,
}: {
label: string
value: string
- onChange: (v: string) => void
- options: { id: string; name: string; desc?: string }[]
- tooltip?: string
- disabled?: boolean
+ onChange: (value: string) => void
+ options: { id: string; name: string }[]
+ help?: string
}) {
+ const id = useId()
return (
-
+
{label}
- {tooltip && }
+ {help && }
onChange(e.target.value)}
- disabled={disabled}
- className={`w-full rounded-lg border border-slate-300 px-3 py-2.5 text-sm text-slate-900 shadow-sm focus:border-[var(--section-color)] focus:outline-none focus:ring-2 focus:ring-[var(--section-color)]/20 ${
- disabled ? "cursor-not-allowed bg-slate-100 text-slate-500" : "bg-white"
- }`}
+ onChange={(event) => onChange(event.currentTarget.value)}
+ className="w-full rounded-lg border border-slate-300 bg-white px-3 py-2.5 text-sm text-slate-900"
>
- {options.map((opt) => (
- {opt.name}
- ))}
+ {options.map((option) => {option.name} )}
)
@@ -306,650 +248,432 @@ function Toggle({
label,
checked,
onChange,
- tooltip,
- disabled = false
+ help,
+ disabled = false,
}: {
label: string
checked: boolean
- onChange: (v: boolean) => void
- tooltip?: string
+ onChange: (value: boolean) => void
+ help?: string
disabled?: boolean
}) {
return (
-
-
- {label}
- {tooltip && }
-
+
+
+ {label}
+ {help && }
+
!disabled && onChange(!checked)}
- className={`relative h-6 w-11 rounded-full transition-colors ${checked ? 'bg-[var(--section-color)]' : 'bg-slate-200'}`}
+ onClick={() => onChange(!checked)}
+ className={`relative h-6 w-11 rounded-full transition-colors ${checked ? "bg-blue-600" : "bg-slate-300"}`}
>
-
+
-
+
)
}
export default function AdvancedTuningPage() {
+ const [config, setConfig] = useState(() => withModel(DEFAULTS, DEFAULT_MODEL))
const [copied, setCopied] = useState(false)
- const [config, setConfig] = useState(tuningData.defaults as Config)
- const videoLength = useMemo(() => (config.num_frames / config.fps).toFixed(1), [config.num_frames, config.fps])
- const totalPixels = useMemo(() => config.height * config.width * config.num_frames, [config.height, config.width, config.num_frames])
-
- // LATENT TOKENS — the sequence length the DiT actually processes. The VAE
- // compresses the video 4x in time and 8x8 in space; the DiT flattens that
- // latent grid into a token sequence. Attention cost scales with its square.
- const latentTokens = useMemo(() => {
- const t = Math.floor((config.num_frames - 1) / 4) + 1
- const h = Math.floor(config.height / 8)
- const w = Math.floor(config.width / 8)
- return t * h * w
- }, [config.num_frames, config.height, config.width])
-
- // EST. TIME — real measured generation time, looked up from data/tuning.json
- // by model + GPU + attention backend. There is no formula: the tuning page allows
- // arbitrary configs, but only the benchmarked points have a real time. If the user
- // tunes resolution / frames / steps away from the benchmarked run, the value is
- // flagged as no longer matching their config.
- const benchmark = useMemo(() => {
- const run = BENCHMARK_RUNS.find(
- (r) =>
- r.model === config.model_id &&
- MODEL_BY_ID[r.model]?.workload === config.workload_type &&
- r.gpu === config.gpu &&
- r.attention === config.attention_backend,
- )
- if (!run) return null
- const deviated =
- config.height !== run.height ||
- config.width !== run.width ||
- config.num_frames !== run.frames ||
- config.num_inference_steps !== run.steps
- return { run, deviated }
- }, [
- config.model_id,
- config.workload_type,
- config.gpu,
- config.attention_backend,
- config.height,
- config.width,
- config.num_frames,
- config.num_inference_steps,
- ])
-
- // When the model or GPU changes, snap the time-affecting parameters to the
- // values that model + GPU was actually benchmarked at, so the page defaults to
- // a real measured configuration. No-op if that model + GPU has no benchmark.
- useEffect(() => {
- const run =
- BENCHMARK_RUNS.find(
- (r) =>
- r.model === config.model_id &&
- MODEL_BY_ID[r.model]?.workload === config.workload_type &&
- r.gpu === config.gpu &&
- r.attention === config.attention_backend,
- ) ??
- BENCHMARK_RUNS.find(
- (r) =>
- r.model === config.model_id &&
- MODEL_BY_ID[r.model]?.workload === config.workload_type &&
- r.gpu === config.gpu,
- )
- if (!run) return
- setConfig((prev) => ({
- ...prev,
- height: run.height,
- width: run.width,
- num_frames: run.frames,
- num_inference_steps: run.steps,
- attention_backend: run.attention,
- }))
- }, [config.model_id, config.workload_type, config.gpu])
-
- // The Model dropdown offers successful benchmark profiles for the currently
- // selected Workload Type + GPU. If the same model has multiple successful
- // attention backends, each benchmarked backend is listed separately.
- const availableModelRuns = useMemo(
- () =>
- BENCHMARK_RUNS.filter(
- (run) =>
- MODEL_BY_ID[run.model]?.workload === config.workload_type &&
- run.gpu === config.gpu,
- ),
- [config.workload_type, config.gpu],
+ const selectedModel = MODEL_BY_ID[config.model_id] ?? DEFAULT_MODEL
+ const availableModels = useMemo(
+ () => MODELS.filter((model) => model.workload === config.workload_type),
+ [config.workload_type],
)
-
- // Keep the selected model valid when Workload Type / GPU changes.
- useEffect(() => {
- if (availableModelRuns.length === 0) return
- if (!availableModelRuns.some((run) => run.model === config.model_id && run.attention === config.attention_backend)) {
- setConfig((prev) => ({
- ...prev,
- model_id: availableModelRuns[0].model,
- attention_backend: availableModelRuns[0].attention,
- }))
- }
- }, [availableModelRuns, config.model_id, config.attention_backend])
-
- const hasAvailableModelRuns = availableModelRuns.length > 0
- const selectedModelRun = `${config.model_id}::${config.attention_backend}`
-
- const command = useMemo(() => {
- const isGame = config.workload_type === "game"
- const isI2VLocal = config.workload_type === "i2v" || config.workload_type === "ti2v"
-
- // Engine kwargs: forwarded directly into VideoGenerator.from_pretrained(...).
- // Only the subset listed in fastvideo/entrypoints/video_generator.py
- // (_FROM_PRETRAINED_CONVENIENCE_KWARGS) is supported there.
- const engineKwargs: string[] = [` num_gpus=${config.num_gpus},`]
- if (config.dit_cpu_offload) engineKwargs.push(` dit_cpu_offload=True,`)
- if (config.dit_layerwise_offload) engineKwargs.push(` dit_layerwise_offload=True,`)
- if (config.vae_cpu_offload) engineKwargs.push(` vae_cpu_offload=True,`)
- if (config.text_encoder_cpu_offload) engineKwargs.push(` text_encoder_cpu_offload=True,`)
- if (config.image_encoder_cpu_offload && isI2VLocal) {
- engineKwargs.push(` image_encoder_cpu_offload=True,`)
- }
- if (config.pin_cpu_memory) engineKwargs.push(` pin_cpu_memory=True,`)
- if (config.use_fsdp_inference && config.num_gpus > 1) {
- engineKwargs.push(` use_fsdp_inference=True,`)
- }
-
- if (isGame) {
- const keyboardDim = MODEL_BY_ID[config.model_id]?.keyboardDim ?? 4
- return [
- "# Install once:",
- "# pip install fastvideo",
- "",
- "# Save this file as run.py and run:",
- "# python run.py",
- "import os",
- "import torch",
- "from fastvideo import VideoGenerator",
- "from fastvideo.models.dits.matrixgame.utils import create_action_presets",
- "",
- `os.environ["FASTVIDEO_ATTENTION_BACKEND"] = "${config.attention_backend}"`,
- "",
- "generator = VideoGenerator.from_pretrained(",
- ` "${config.model_id}",`,
- ...engineKwargs,
- ")",
- "",
- `num_frames = ${config.num_frames}`,
- `actions = create_action_presets(num_frames, keyboard_dim=${keyboardDim})`,
- `grid_sizes = torch.tensor([${Math.floor((config.num_frames - 1) / 4) + 1}, ${Math.floor(config.height / 8)}, ${Math.floor(config.width / 8)}])`,
- "",
- "generator.generate_video(",
- ' prompt="",',
- ' image_path="./input.png",',
- ' mouse_cond=actions["mouse"].unsqueeze(0),',
- ' keyboard_cond=actions["keyboard"].unsqueeze(0),',
- " grid_sizes=grid_sizes,",
- ` height=${config.height},`,
- ` width=${config.width},`,
- ` fps=${config.fps},`,
- " seed=42,",
- ` num_frames=${config.num_frames},`,
- ` num_inference_steps=${config.num_inference_steps},`,
- ` guidance_scale=${config.guidance_scale},`,
- ' output_path="outputs/",',
- " save_video=True,",
- ")",
- ].join("\n")
- }
-
- // SamplingParam fields: real attribute names from fastvideo/api/sampling_param.py.
- const paramAssigns: string[] = [
- `param.height = ${config.height}`,
- `param.width = ${config.width}`,
- `param.num_frames = ${config.num_frames}`,
- `param.fps = ${config.fps}`,
- `param.num_inference_steps = ${config.num_inference_steps}`,
- `param.guidance_scale = ${config.guidance_scale}`,
- ]
- if (config.seed !== -1) paramAssigns.push(`param.seed = ${config.seed}`)
- if (isI2VLocal) paramAssigns.push(`param.image_path = "./input.png"`)
-
- // Pipeline-config-level overrides (embedded_cfg_scale, flow_shift, VSA_sparsity)
- // are not part of from_pretrained convenience kwargs. They live in the model's
- // pipeline config JSON (see fastvideo/configs/*.json). Surface as comment so users
- // know they need a custom config file to apply them.
- const pipelineNotes: string[] = []
- if (config.embedded_cfg_scale !== 6.0) {
- pipelineNotes.push(`# embedded_cfg_scale: ${config.embedded_cfg_scale}`)
- }
- if (config.flow_shift !== null) {
- pipelineNotes.push(`# flow_shift: ${config.flow_shift}`)
- }
- if (config.attention_backend === "VIDEO_SPARSE_ATTN" && config.VSA_sparsity > 0) {
- pipelineNotes.push(`# VSA_sparsity: ${config.VSA_sparsity}`)
- }
-
- const lines: string[] = [
- "# Install once:",
- "# pip install fastvideo",
- "",
- "# Save this file as run.py and run:",
- "# python run.py",
- "import os",
- "from fastvideo import VideoGenerator, SamplingParam",
- "",
- `os.environ["FASTVIDEO_ATTENTION_BACKEND"] = "${config.attention_backend}"`,
- "",
- "generator = VideoGenerator.from_pretrained(",
- ` "${config.model_id}",`,
- ...engineKwargs,
- ")",
- "",
- `param = SamplingParam.from_pretrained("${config.model_id}")`,
- ...paramAssigns,
- "",
- ]
-
- if (pipelineNotes.length) {
- lines.push(
- "# Pipeline-config-level overrides (require a custom config file):",
- ...pipelineNotes,
- "",
- )
- }
-
- lines.push(
- "generator.generate_video(",
- ' "your prompt here",',
- " sampling_param=param,",
- ' output_path="outputs/",',
- " save_video=True,",
- ")",
- )
-
- return lines.join("\n")
- }, [config])
-
- const copyCommand = () => {
- navigator.clipboard.writeText(command)
- setCopied(true)
- setTimeout(() => setCopied(false), 2000)
- }
+ const availableBackends = selectedModel.recipe.attentionBackends
+ .map((id) => ATTENTION_BY_ID[id])
+ .filter((backend): backend is AttentionBackend => Boolean(backend))
const updateConfig = (key: K, value: Config[K]) => {
- setConfig((prev) => ({ ...prev, [key]: value }))
+ setConfig((current) => ({ ...current, [key]: value }))
}
- const selectedModel = MODEL_BY_ID[config.model_id]
- const isDmdMode = Boolean(selectedModel?.dmdMode)
- const isI2V = config.workload_type === "i2v" || config.workload_type === "ti2v"
+ const selectWorkload = (workload: string) => {
+ const model = MODELS.find((candidate) => candidate.workload === workload)
+ if (model) setConfig((current) => withModel(current, model))
+ }
+
+ const selectModel = (modelId: string) => {
+ const model = MODEL_BY_ID[modelId]
+ if (model) setConfig((current) => withModel(current, model))
+ }
+
+ const recipeChanged = useMemo(() => {
+ const recipe = selectedModel.recipe
+ return config.height !== recipe.height ||
+ config.width !== recipe.width ||
+ config.num_frames !== recipe.numFrames ||
+ config.fps !== recipe.fps ||
+ config.num_inference_steps !== recipe.numInferenceSteps ||
+ config.guidance_scale !== recipe.guidanceScale ||
+ config.guidance_scale_2 !== recipe.guidanceScale2 ||
+ config.embedded_cfg_scale !== recipe.embeddedCfgScale ||
+ config.boundary_ratio !== recipe.boundaryRatio ||
+ config.attention_backend !== recipe.defaultAttentionBackend ||
+ config.VSA_sparsity !== recipe.vsaSparsity
+ }, [config, selectedModel])
+
+ const videoLength = (config.num_frames / config.fps).toFixed(1)
+ const totalPixels = config.height * config.width * config.num_frames
+ const latentTokens = (Math.floor((config.num_frames - 1) / 4) + 1) *
+ Math.floor(config.height / 8) * Math.floor(config.width / 8)
useEffect(() => {
- if (typeof window === "undefined" || window.parent === window) return
+ if (window.parent === window) return
+ const root = document.getElementById("config-generator-root")
+ if (!root) return
let frame = 0
- const timers: number[] = []
-
const sendHeight = () => {
window.cancelAnimationFrame(frame)
frame = window.requestAnimationFrame(() => {
- const height = Math.max(
- document.body.scrollHeight,
- document.body.offsetHeight,
- document.documentElement.scrollHeight,
- document.documentElement.offsetHeight,
+ window.parent.postMessage(
+ { type: "fastvideo-config-generator:resize", height: Math.ceil(root.getBoundingClientRect().height) },
+ window.location.origin,
)
- window.parent.postMessage({ type: "adv-tuning-guide-height", height }, "*")
})
}
-
+ const observer = new ResizeObserver(sendHeight)
+ observer.observe(root)
sendHeight()
-
- const ro = new ResizeObserver(sendHeight)
- ro.observe(document.documentElement)
- ro.observe(document.body)
-
- const mo = new MutationObserver(sendHeight)
- mo.observe(document.documentElement, {
- attributes: true,
- childList: true,
- characterData: true,
- subtree: true,
- })
-
- window.addEventListener("load", sendHeight)
- window.addEventListener("resize", sendHeight)
-
- for (const delay of [100, 300, 1000]) {
- timers.push(window.setTimeout(sendHeight, delay))
- }
-
return () => {
window.cancelAnimationFrame(frame)
- timers.forEach((timer) => window.clearTimeout(timer))
- window.removeEventListener("load", sendHeight)
- window.removeEventListener("resize", sendHeight)
- mo.disconnect()
- ro.disconnect()
+ observer.disconnect()
}
}, [])
+ const command = useMemo(() => {
+ const isGame = config.workload_type === "game"
+ const needsImage = isGame || config.workload_type === "i2v" || config.workload_type === "ti2v"
+ const workloadType = config.workload_type === "ti2v" || isGame ? "i2v" : config.workload_type
+ const overrideYaml = [
+ " preset_overrides:",
+ ` embedded_cfg_scale: ${config.embedded_cfg_scale}`,
+ ...(config.dmd_denoising_steps === null
+ ? []
+ : [` dmd_denoising_steps: [${config.dmd_denoising_steps.join(", ")}]`]),
+ ]
+
+ if (isGame) {
+ const pythonBoolean = (value: boolean) => value ? "True" : "False"
+ const pythonOverrides = [
+ ...(config.dmd_denoising_steps === null
+ ? []
+ : [` "dmd_denoising_steps": [${config.dmd_denoising_steps.join(", ")}],`]),
+ ]
+ const inputLines = [
+ ' "image_path": "./input.png",',
+ selectedModel.usesMouse === false ? null : ' "mouse_cond": actions["mouse"].unsqueeze(0),',
+ ' "keyboard_cond": actions["keyboard"].unsqueeze(0),',
+ ' "grid_sizes": grid_sizes,',
+ ].filter((line): line is string => Boolean(line))
+ const keyboardDim = selectedModel.keyboardDim ?? 4
+ return [
+ "# Save as run.py, then run: python run.py",
+ "import os",
+ "import torch",
+ "from fastvideo import VideoGenerator",
+ "from fastvideo.models.dits.matrixgame2.utils import create_action_presets",
+ "",
+ `os.environ["FASTVIDEO_ATTENTION_BACKEND"] = "${config.attention_backend}"`,
+ "generator = VideoGenerator.from_config({",
+ ` "model_path": "${config.model_id}",`,
+ " \"engine\": {",
+ ` "num_gpus": ${config.num_gpus},`,
+ ` "use_fsdp_inference": ${pythonBoolean(config.use_fsdp_inference)},`,
+ " \"offload\": {",
+ ` "dit": ${pythonBoolean(config.dit_cpu_offload)},`,
+ ` "dit_layerwise": ${pythonBoolean(config.dit_layerwise_offload)},`,
+ ` "text_encoder": ${pythonBoolean(config.text_encoder_cpu_offload)},`,
+ ` "image_encoder": ${pythonBoolean(config.image_encoder_cpu_offload)},`,
+ ` "vae": ${pythonBoolean(config.vae_cpu_offload)},`,
+ ` "pin_cpu_memory": ${pythonBoolean(config.pin_cpu_memory)},`,
+ " },",
+ " },",
+ " \"pipeline\": {",
+ " \"workload_type\": \"i2v\",",
+ " \"preset_overrides\": {",
+ ...pythonOverrides,
+ " },",
+ " },",
+ "})",
+ "",
+ `num_frames = ${config.num_frames}`,
+ `actions = create_action_presets(num_frames, keyboard_dim=${keyboardDim}, seed=${config.seed < 0 ? "None" : config.seed})`,
+ `grid_sizes = torch.tensor([${Math.floor((config.num_frames - 1) / 4) + 1}, ${Math.floor(config.height / 8)}, ${Math.floor(config.width / 8)}])`,
+ "generator.generate({",
+ ' "prompt": "",',
+ " \"inputs\": {",
+ ...inputLines,
+ " },",
+ " \"sampling\": {",
+ ` "height": ${config.height},`,
+ ` "width": ${config.width},`,
+ ` "num_frames": ${config.num_frames},`,
+ ` "fps": ${config.fps},`,
+ ` "num_inference_steps": ${config.num_inference_steps},`,
+ ...(config.dmd_denoising_steps === null ? [` "guidance_scale": ${config.guidance_scale},`] : []),
+ ...(config.boundary_ratio === null ? [] : [` "boundary_ratio": ${config.boundary_ratio},`]),
+ ` "seed": ${config.seed},`,
+ " },",
+ ' "output": {"output_path": "outputs/", "save_video": True},',
+ "})",
+ ].join("\n")
+ }
+
+ const experimentalYaml = config.attention_backend === "VIDEO_SPARSE_ATTN"
+ ? [" experimental:", ` VSA_sparsity: ${config.VSA_sparsity}`]
+ : []
+ const inputYaml = needsImage ? [" inputs:", " image_path: ./input.png"] : []
+
+ return [
+ "cat > fastvideo-generate.yaml <<'YAML'",
+ "generator:",
+ ` model_path: ${config.model_id}`,
+ " engine:",
+ ` num_gpus: ${config.num_gpus}`,
+ ` use_fsdp_inference: ${config.use_fsdp_inference}`,
+ " offload:",
+ ` dit: ${config.dit_cpu_offload}`,
+ ` dit_layerwise: ${config.dit_layerwise_offload}`,
+ ` text_encoder: ${config.text_encoder_cpu_offload}`,
+ ` image_encoder: ${config.image_encoder_cpu_offload}`,
+ ` vae: ${config.vae_cpu_offload}`,
+ ` pin_cpu_memory: ${config.pin_cpu_memory}`,
+ " pipeline:",
+ ` workload_type: ${workloadType}`,
+ ...overrideYaml,
+ ...experimentalYaml,
+ "",
+ "request:",
+ ' prompt: "your prompt here"',
+ ...inputYaml,
+ " sampling:",
+ ` height: ${config.height}`,
+ ` width: ${config.width}`,
+ ` num_frames: ${config.num_frames}`,
+ ` fps: ${config.fps}`,
+ ` num_inference_steps: ${config.num_inference_steps}`,
+ ...(config.dmd_denoising_steps === null ? [` guidance_scale: ${config.guidance_scale}`] : []),
+ ...(config.guidance_scale_2 === null ? [] : [` guidance_scale_2: ${config.guidance_scale_2}`]),
+ ...(config.boundary_ratio === null ? [] : [` boundary_ratio: ${config.boundary_ratio}`]),
+ ` seed: ${config.seed}`,
+ " output:",
+ " output_path: outputs/",
+ " save_video: true",
+ "YAML",
+ "",
+ `FASTVIDEO_ATTENTION_BACKEND=${config.attention_backend} \\`,
+ " fastvideo generate --config fastvideo-generate.yaml",
+ ].join("\n")
+ }, [config, selectedModel])
+
+ const copyCommand = async () => {
+ await navigator.clipboard.writeText(command)
+ setCopied(true)
+ window.setTimeout(() => setCopied(false), 2000)
+ }
+
+ const isImageWorkload = config.workload_type === "i2v" || config.workload_type === "ti2v" || config.workload_type === "game"
+
return (
-
- {/*
*/}
-
-
- {/* Stats Bar */}
-
-
-
-
-
- Video Length
-
-
-
{hasAvailableModelRuns ? `${videoLength}s` : "N/A"}
-
-
-
-
- Total Pixels
-
-
-
{hasAvailableModelRuns ? `${(totalPixels / 1000000).toFixed(1)}M` : "N/A"}
-
-
-
-
- Latent Tokens
-
-
-
{hasAvailableModelRuns ? `${(latentTokens / 1000).toFixed(0)}K` : "N/A"}
-
-
-
-
- Est. Time
-
-
- {benchmark ? (
- <>
-
- {benchmark.run.generationSeconds != null ? `${Math.round(benchmark.run.generationSeconds)}s` : "N/A"}
-
- {benchmark.run.generationSeconds != null && (
-
- {`${benchmark.deviated ? "⚠ " : ""}measured @ ${benchmark.run.steps} steps · ${benchmark.run.width}×${benchmark.run.height} · ${ATTENTION_BY_ID[benchmark.run.attention]?.shortName ?? benchmark.run.attention}`}
-
- )}
- >
- ) : !hasAvailableModelRuns ? (
- <>
-
N/A
- >
- ) : (
- <>
-
—
-
not benchmarked
- >
- )}
-
-
+
+
+
+
+
+
Video length
{videoLength}s
+
Total pixels
{(totalPixels / 1_000_000).toFixed(1)}M
+
Latent tokens
{latentTokens.toLocaleString()}
-
- {/* Left Column - Parameters */}
-
-
-
-
+
+
+
+
+
+
Workload
+
+ {WORKLOADS.map((workload) => {
+ const Icon = WORKLOAD_ICONS[workload.id] ?? Film
+ return (
+ selectWorkload(workload.id)}
+ className={`rounded-lg border p-3 text-left ${config.workload_type === workload.id ? "border-blue-600 bg-blue-50" : "border-slate-300 bg-white"}`}
+ >
+
+ {workload.name}
+
+ )
+ })}
+
+
+
{
- const [model, attention] = v.split("::")
- setConfig((prev) => ({ ...prev, model_id: model, attention_backend: attention }))
- }}
- options={
- hasAvailableModelRuns
- ? availableModelRuns.map((run) => {
- const model = MODEL_BY_ID[run.model]
- const backend = ATTENTION_BY_ID[run.attention]?.shortName ?? run.attention
- return {
- id: `${run.model}::${run.attention}`,
- name: `${model.name} (${model.size}, ${backend})`,
- }
- })
- : [{ id: selectedModelRun, name: "Not suitable" }]
- }
- disabled={!hasAvailableModelRuns}
- tooltip="Only models with a passing benchmark for the selected Workload Type + GPU are listed."
+ value={config.model_id}
+ onChange={selectModel}
+ options={availableModels.map((model) => ({ id: model.id, name: `${model.name} (${model.size})` }))}
+ help="Models registered for the selected workload."
+ />
+ setConfig((current) => ({
+ ...current,
+ num_gpus: value,
+ use_fsdp_inference: value > 1 && current.use_fsdp_inference,
+ }))}
+ min={1}
+ max={8}
/>
-
updateConfig("workload_type", v)}
- options={workloadOptions.map(w => ({ id: w.id, name: w.name }))}
- tooltip="Task category. Determines which pipeline stages run."
- />
- updateConfig("gpu", v)}
- options={gpuOptions.map(g => ({ id: g.id, name: `${g.name} (${g.vram})` }))}
- tooltip="GPU model. Used for benchmark tracking and hardware-aware configuration."
- />
+
+ Recipe source: FastVideo {SOURCE.fastvideoCommit.slice(0, 8)} . Hardware fit is not inferred.
+
+
+
+
+
+
+ updateConfig("height", value)} min={256} max={2160} step={8} />
+ updateConfig("width", value)} min={256} max={3840} step={8} />
updateConfig("num_gpus", v)}
- min={1}
- max={8}
- tooltip="Values > 1 require use_fsdp_inference = true."
+ label="Frames"
+ value={config.num_frames}
+ onChange={(value) => updateConfig("num_frames", value)}
+ min={config.workload_type === "game" ? 9 : 1}
+ max={config.workload_type === "game" ? 1197 : 1200}
+ step={config.workload_type === "game" ? 12 : 1}
+ help={config.workload_type === "game" ? "Matrix Game requires 9 + 12k frames." : undefined}
/>
+ updateConfig("fps", value)} min={1} max={60} />
+ {recipeChanged && Custom values differ from the maintained recipe and may be unsupported.
}
-
-
-
-
Resolution Preset
-
- {resolutionPresets.map((preset) => (
- {
- updateConfig("height", preset.height)
- updateConfig("width", preset.width)
- }}
- className={`rounded-lg border px-3 py-2 text-sm transition-all ${
- config.height === preset.height && config.width === preset.width
- ? "border-[var(--section-color)] bg-[var(--section-color)] text-white"
- : "border-[var(--section-color)] bg-white text-[var(--section-color)] hover:bg-[var(--section-soft)]"
- }`}
- >
- {preset.label}
-
- ))}
-
-
-
- updateConfig("height", v)} min={256} max={2160} step={8} unit="px" tooltip="Frame height in pixels. Must be divisible by 8 (VAE requirement)." />
- updateConfig("width", v)} min={256} max={3840} step={8} unit="px" tooltip="Frame width in pixels. Must be divisible by 8." />
-
-
-
Frame Count Preset
-
- {framePresets.map((preset) => (
- updateConfig("num_frames", preset.frames)}
- className={`rounded-lg border px-3 py-2 text-sm transition-all ${
- config.num_frames === preset.frames
- ? "border-[var(--section-color)] bg-[var(--section-color)] text-white"
- : "border-[var(--section-color)] bg-white text-[var(--section-color)] hover:bg-[var(--section-soft)]"
- }`}
- >
- {preset.label}
-
- ))}
-
-
-
- updateConfig("num_frames", v)} min={1} max={240} tooltip="Total number of frames to generate." />
- updateConfig("fps", v)} min={1} max={60} tooltip="Frames per second for the output video. Does NOT affect generation time." />
-
-
-
-
-
-
-
-
- Inference Steps
-
-
-
- updateConfig("num_inference_steps", Number(e.target.value))} className="h-2 flex-1 cursor-pointer appearance-none rounded-full bg-[var(--section-soft)] accent-[var(--section-color)] [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-[var(--section-color)]" />
- {config.num_inference_steps}
-
-
- Draft (10) Balanced (30) High (80)
-
-
-
-
- Guidance Scale
-
-
-
- updateConfig("guidance_scale", Number(e.target.value))} className="h-2 flex-1 cursor-pointer appearance-none rounded-full bg-[var(--section-soft)] accent-[var(--section-color)] [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-[var(--section-color)]" />
- {config.guidance_scale}
-
-
- Loose (3) Balanced (5) Strict (8+)
-
-
-
- updateConfig("embedded_cfg_scale", v)} min={1} max={10} step={0.5} tooltip="Model-level CFG signal passed into the DiT. Default: 6.0. Rarely change." />
- updateConfig("seed", v)} min={-1} tooltip="Random seed. Set to -1 for random. Fix a seed for reproducibility." />
-
-
-
-
-
-
-
-
Attention Backend
-
- {attentionBackends.map((backend) => (
-
updateConfig("attention_backend", backend.id)}
- className={`rounded-lg border p-3 text-left transition-all ${
- config.attention_backend === backend.id
- ? "border-[var(--section-color)] bg-[var(--section-soft)]"
- : "border-slate-200 bg-white hover:border-[var(--section-color)]"
- }`}
- >
-
- {backend.name}
- {backend.speed}
-
- {backend.desc}
-
- ))}
-
-
- {config.attention_backend === "VIDEO_SPARSE_ATTN" && (
-
-
- VSA Sparsity
-
-
-
- updateConfig("VSA_sparsity", Number(e.target.value))} className="h-2 flex-1 cursor-pointer appearance-none rounded-full bg-[var(--section-soft)] accent-[var(--section-color)] [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-[var(--section-color)]" />
- {config.VSA_sparsity}
-
-
- )}
- {isDmdMode && (
-
-
-
- DMD Mode Available
-
-
{selectedModel?.dmdNote}
-
dmd_steps: {config.dmd_denoising_steps}
-
+
+
+ updateConfig("num_inference_steps", value)}
+ min={1}
+ max={100}
+ disabled={config.dmd_denoising_steps !== null}
+ help={config.dmd_denoising_steps ? "This model uses the fixed schedule shown below." : "Number of denoising iterations."}
+ />
+ {config.workload_type !== "game" && (
+ updateConfig("guidance_scale", value)}
+ min={0.5}
+ max={20}
+ step={0.5}
+ disabled={config.dmd_denoising_steps !== null}
+ help={config.dmd_denoising_steps ? "DMD uses the embedded CFG scale instead." : "Values above 1 may add an unconditional forward pass."}
+ />
)}
+ {config.guidance_scale_2 !== null && updateConfig("guidance_scale_2", value)} min={0.5} max={20} step={0.5} />}
+ {config.workload_type !== "game" && updateConfig("embedded_cfg_scale", value)} min={0} max={20} step={0.5} />}
+ {config.boundary_ratio !== null && updateConfig("boundary_ratio", value)} min={0} max={1} step={0.025} />}
+ updateConfig("seed", value)} min={0} />
+ {config.dmd_denoising_steps && (
+
+ Fixed denoising schedule: {config.dmd_denoising_steps.join(", ")}
+
+ )}
-
-
-
-
- Tip: For 24GB VRAM with 14B models, enable dit_cpu_offload + vae_cpu_offload + pin_cpu_memory.
-
-
-
updateConfig("dit_cpu_offload", v)} tooltip="Offload entire DiT to RAM. Saves ~4GB, ~2-3x slower." />
- updateConfig("dit_layerwise_offload", v)} tooltip="Layer-by-layer offload. Maximum VRAM saving (~8GB), ~4x slower." />
- updateConfig("vae_cpu_offload", v)} tooltip="Offload VAE decoder. Saves ~2GB. Minimal time penalty." />
- updateConfig("text_encoder_cpu_offload", v)} tooltip="Offload text encoder. Saves ~1-2GB. Runs once at start." />
- updateConfig("image_encoder_cpu_offload", v)} tooltip="I2V/TI2V only. Saves ~1-2GB." disabled={!isI2V} />
- updateConfig("pin_cpu_memory", v)} tooltip="Pins RAM pages for faster PCIe DMA transfers. Always enable with any offload flag." />
- updateConfig("use_fsdp_inference", v)} tooltip="Shards model weights across multiple GPUs via FSDP." disabled={config.num_gpus <= 1} />
+
+
+ {availableBackends.map((backend) => (
+
updateConfig("attention_backend", backend.id)}
+ className={`rounded-lg border p-3 text-left ${config.attention_backend === backend.id ? "border-blue-600 bg-blue-50" : "border-slate-300 bg-white"}`}
+ >
+ {backend.name}
+ {backend.desc}
+
+ ))}
+ {config.attention_backend === "VIDEO_SPARSE_ATTN" && (
+
+ updateConfig("VSA_sparsity", value)} min={0} max={1} step={0.1} />
+
+ )}
+
+
+
+ setConfig((current) => ({ ...current, dit_cpu_offload: value, dit_layerwise_offload: value ? false : current.dit_layerwise_offload }))}
+ />
+ setConfig((current) => ({ ...current, dit_layerwise_offload: value, dit_cpu_offload: value ? false : current.dit_cpu_offload, use_fsdp_inference: value ? false : current.use_fsdp_inference }))}
+ help="Mutually exclusive with whole-DiT offload and FSDP."
+ />
+ updateConfig("vae_cpu_offload", value)} />
+ updateConfig("text_encoder_cpu_offload", value)} />
+ updateConfig("image_encoder_cpu_offload", value)} disabled={!isImageWorkload} />
+ updateConfig("pin_cpu_memory", value)} />
+ updateConfig("use_fsdp_inference", value)} disabled={config.num_gpus <= 1 || config.dit_layerwise_offload} />
- {/* Right Column - Output */}
-
-
-
-
-
-
-
-
-
Generated Command
-
- {hasAvailableModelRuns && (
-
- {copied ? : }
- {copied ? "Copied" : "Copy"}
-
- )}
-
-
- {hasAvailableModelRuns ? (
-
{command}
- ) : (
-
- )}
-
-
-
-
-
Quick Reference
-
-
Resolution {hasAvailableModelRuns ? `${config.width}x${config.height}` : "N/A"}
-
Frames {hasAvailableModelRuns ? config.num_frames : "N/A"}
-
Steps {hasAvailableModelRuns ? config.num_inference_steps : "N/A"}
-
Backend {hasAvailableModelRuns ? config.attention_backend : "N/A"}
-
- Offloading
- {hasAvailableModelRuns ? ([config.dit_cpu_offload && "DiT", config.vae_cpu_offload && "VAE"].filter(Boolean).join("+") || "None") : "N/A"}
-
+
+
+
+
+
+ Generated config
+
+ {copied ? : }
+ {copied ? "Copied" : "Copy"}
+
+
{command}
-
+
+
+
+
+ Recipe summary
+
+
+
Model {selectedModel.name}
+
Resolution {config.width}×{config.height}
+
Frames {config.num_frames}
+
Backend {config.attention_backend}
+
+
+
-
+
)
}
diff --git a/consumer-guide/data/quickstart.json b/consumer-guide/data/quickstart.json
index 0f28c30ff..fe1acc0d9 100644
--- a/consumer-guide/data/quickstart.json
+++ b/consumer-guide/data/quickstart.json
@@ -2,380 +2,124 @@
"defaults": {
"task": "t2v",
"tier": "quick",
- "gpu": "5090",
- "ramGb": 64,
"numGpus": 1
},
- "ramOptionsGb": [128, 64, 32],
"gpuCountOptions": [1, 2, 4],
- "hardware": [
- {
- "id": "4090",
- "label": "RTX 4090",
- "defaultVramGb": 24,
- "vramOptionsGb": [24]
- },
- {
- "id": "5090",
- "label": "RTX 5090",
- "defaultVramGb": 32,
- "vramOptionsGb": [32]
- }
- ],
"tasks": [
- {
- "id": "t2v",
- "label": "Text to Video",
- "sub": "T2V",
- "icon": "Film"
- },
- {
- "id": "i2v",
- "label": "Image to Video",
- "sub": "I2V",
- "icon": "Image"
- },
- {
- "id": "ti2v",
- "label": "Text + Image",
- "sub": "TI2V",
- "icon": "Sparkles"
- },
- {
- "id": "game",
- "label": "Game",
- "sub": "Matrix Game",
- "icon": "Gamepad2"
- }
+ { "id": "t2v", "label": "Text to Video", "sub": "T2V", "icon": "Film" },
+ { "id": "i2v", "label": "Image to Video", "sub": "I2V", "icon": "Image" },
+ { "id": "ti2v", "label": "Text + Image", "sub": "TI2V", "icon": "Sparkles" },
+ { "id": "game", "label": "Game", "sub": "Matrix Game", "icon": "Gamepad2" }
],
"tiers": [
{
"id": "quick",
- "name": "Quick Generate",
- "desc": "FastVideo model with a benchmarked fast profile.",
- "badge": "Recommended"
+ "name": "Recommended",
+ "desc": "The maintained FastVideo example for this task.",
+ "badge": "Start here"
},
{
"id": "best",
- "name": "High Quality",
- "desc": "FastVideo model with a benchmarked quality-oriented profile."
+ "name": "Alternative",
+ "desc": "Another maintained model or task variant."
}
],
"profiles": [
{
- "status": "available",
- "gpu": "5090",
- "vramGb": 32,
"task": "t2v",
"tier": "quick",
"model": "FastVideo/FastWan2.1-T2V-1.3B-Diffusers",
- "modelShort": "FastWan2.1-T2V-1.3B",
- "attn": "VIDEO_SPARSE_ATTN",
- "h": 480,
- "w": 832,
- "frames": 81,
- "steps": 40,
- "cfg": 5,
- "fps": 16,
- "vramMin": 18,
- "ramMin": 64,
- "timeSec": 22.91,
- "firstRunSec": 139.44,
- "vram": "~16.0 GB",
- "offload": true,
- "ditCpuOffload": true,
- "ditLayerwiseOffload": true,
- "vaeCpuOffload": true,
+ "ditCpuOffload": false,
+ "ditLayerwiseOffload": false,
+ "vaeCpuOffload": false,
"textEncoderCpuOffload": true,
- "pinCpuMemory": true
+ "imageEncoderCpuOffload": false,
+ "pinCpuMemory": true,
+ "useFsdpInference": false
},
{
- "status": "available",
- "gpu": "5090",
- "vramGb": 32,
"task": "t2v",
"tier": "best",
- "model": "FastVideo/FastHunyuan-diffusers",
- "modelShort": "FastHunyuan",
- "attn": "FLASH_ATTN",
- "h": 720,
- "w": 1280,
- "frames": 81,
- "steps": 50,
- "cfg": 1,
- "fps": 24,
- "vramMin": 32,
- "ramMin": 64,
- "timeSec": 1389.71,
- "firstRunSec": 1492.36,
- "vram": "~27.1 GB",
- "offload": true,
+ "model": "Wan-AI/Wan2.1-T2V-14B-Diffusers",
"ditCpuOffload": true,
- "ditLayerwiseOffload": true,
- "vaeCpuOffload": true,
- "textEncoderCpuOffload": true,
- "pinCpuMemory": true
- },
- {
- "status": "available",
- "gpu": "5090",
- "vramGb": 32,
- "task": "ti2v",
- "tier": "quick",
- "model": "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers",
- "modelShort": "FastWan2.2-TI2V-5B",
- "attn": "VIDEO_SPARSE_ATTN",
- "h": 480,
- "w": 832,
- "frames": 81,
- "steps": 40,
- "cfg": 5,
- "fps": 16,
- "vramMin": 18,
- "ramMin": 64,
- "timeSec": 17.39,
- "firstRunSec": 116.57,
- "vram": "~17.2 GB",
- "offload": true,
- "ditCpuOffload": true,
- "ditLayerwiseOffload": true,
+ "ditLayerwiseOffload": false,
+ "vaeCpuOffload": false,
"textEncoderCpuOffload": true,
+ "imageEncoderCpuOffload": false,
"pinCpuMemory": true,
- "extra": "--image ./input.png"
+ "useFsdpInference": false
},
{
- "status": "available",
- "gpu": "5090",
- "vramGb": 32,
- "task": "game",
- "tier": "quick",
- "model": "FastVideo/Matrix-Game-2.0-Base-Diffusers",
- "modelShort": "Matrix-Game-2.0-Base",
- "attn": "FLASH_ATTN",
- "h": 352,
- "w": 640,
- "frames": 81,
- "steps": 3,
- "cfg": 5,
- "fps": 16,
- "vramMin": 12,
- "ramMin": 32,
- "timeSec": 12.29,
- "firstRunSec": 247.2,
- "vram": "~10.4 GB",
- "offload": true,
- "ditCpuOffload": true,
- "ditLayerwiseOffload": true,
- "textEncoderCpuOffload": true,
- "pinCpuMemory": true,
- "keyboardDim": 4,
- "extra": "--image ./input.png --action forward"
- },
- {
- "status": "available",
- "gpu": "5090",
- "vramGb": 32,
- "task": "game",
- "tier": "best",
- "model": "FastVideo/Matrix-Game-2.0-GTA-Diffusers",
- "modelShort": "Matrix-Game-2.0-GTA",
- "attn": "FLASH_ATTN",
- "h": 352,
- "w": 640,
- "frames": 81,
- "steps": 3,
- "cfg": 5,
- "fps": 16,
- "vramMin": 12,
- "ramMin": 32,
- "timeSec": 13.03,
- "firstRunSec": 212.37,
- "vram": "~10.4 GB",
- "offload": true,
- "ditCpuOffload": true,
- "ditLayerwiseOffload": true,
- "textEncoderCpuOffload": true,
- "pinCpuMemory": true,
- "keyboardDim": 2,
- "extra": "--image ./input.png --action forward"
- },
- {
- "status": "available",
- "gpu": "5090",
- "vramGb": 32,
- "task": "ti2v",
- "tier": "best",
- "model": "Wan-AI/Wan2.2-TI2V-5B-Diffusers",
- "modelShort": "Wan2.2-TI2V-5B",
- "attn": "FLASH_ATTN",
- "h": 480,
- "w": 832,
- "frames": 81,
- "steps": 40,
- "cfg": 5,
- "fps": 16,
- "vramMin": 18,
- "ramMin": 64,
- "timeSec": 71.01,
- "firstRunSec": 223.29,
- "vram": "~15.5 GB",
- "offload": true,
- "ditCpuOffload": true,
- "ditLayerwiseOffload": true,
- "textEncoderCpuOffload": true,
- "pinCpuMemory": true,
- "extra": "--image ./input.png"
- },
- {
- "status": "available",
- "gpu": "5090",
- "vramGb": 32,
- "task": "i2v",
- "tier": "best",
- "model": "Wan-AI/Wan2.1-I2V-14B-720P-Diffusers",
- "modelShort": "Wan2.1-I2V-720P",
- "attn": "FLASH_ATTN",
- "h": 720,
- "w": 1280,
- "frames": 81,
- "steps": 40,
- "cfg": 5,
- "fps": 16,
- "vramMin": 32,
- "ramMin": 64,
- "timeSec": 2832.76,
- "firstRunSec": 3026.41,
- "vram": "~28.9 GB",
- "offload": true,
- "ditCpuOffload": true,
- "vaeCpuOffload": true,
- "textEncoderCpuOffload": true,
- "pinCpuMemory": true,
- "useFsdpInference": true,
- "extra": "--image ./input.png"
- },
- {
- "status": "available",
- "gpu": "5090",
- "vramGb": 32,
"task": "i2v",
"tier": "quick",
"model": "loayrashid/TurboWan2.2-I2V-A14B-Diffusers",
- "modelShort": "TurboWan2.2-I2V-A14B",
- "attn": "SLA_ATTN",
- "h": 480,
- "w": 832,
- "frames": 81,
- "steps": 4,
- "cfg": 1,
- "fps": 16,
- "vramMin": 32,
- "ramMin": 64,
- "timeSec": 364,
- "vram": "~10.6 GB",
- "offload": true,
"ditCpuOffload": true,
+ "ditLayerwiseOffload": false,
+ "vaeCpuOffload": false,
"textEncoderCpuOffload": true,
+ "imageEncoderCpuOffload": true,
"pinCpuMemory": true,
- "useFsdpInference": true,
- "extra": "--image ./input.png"
+ "useFsdpInference": false
},
{
- "status": "available",
- "gpu": "4090",
- "vramGb": 24,
- "task": "t2v",
- "tier": "quick",
- "model": "FastVideo/FastWan2.1-T2V-1.3B-Diffusers",
- "modelShort": "FastWan2.1-T2V-1.3B",
- "attn": "VIDEO_SPARSE_ATTN",
- "h": 480,
- "w": 832,
- "frames": 81,
- "steps": 3,
- "cfg": 6,
- "fps": 16,
- "vramMin": 12,
- "ramMin": 32,
- "timeSec": 32.42,
- "vram": "~11.0 GB",
- "offload": true,
- "ditLayerwiseOffload": true,
- "textEncoderCpuOffload": true
+ "task": "i2v",
+ "tier": "best",
+ "model": "Wan-AI/Wan2.1-I2V-14B-720P-Diffusers",
+ "ditCpuOffload": true,
+ "ditLayerwiseOffload": false,
+ "vaeCpuOffload": false,
+ "textEncoderCpuOffload": true,
+ "imageEncoderCpuOffload": true,
+ "pinCpuMemory": true,
+ "useFsdpInference": false
},
{
- "status": "available",
- "gpu": "4090",
- "vramGb": 24,
"task": "ti2v",
"tier": "quick",
"model": "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers",
- "modelShort": "FastWan2.2-TI2V-5B",
- "attn": "FLASH_ATTN",
- "h": 480,
- "w": 832,
- "frames": 81,
- "steps": 3,
- "cfg": 6,
- "fps": 16,
- "vramMin": 12,
- "ramMin": 64,
- "timeSec": 17.81,
- "vram": "~10.8 GB",
- "offload": true,
- "ditLayerwiseOffload": true,
+ "ditCpuOffload": true,
+ "ditLayerwiseOffload": false,
+ "vaeCpuOffload": false,
"textEncoderCpuOffload": true,
- "extra": "--image ./input.png"
+ "imageEncoderCpuOffload": true,
+ "pinCpuMemory": true,
+ "useFsdpInference": false
+ },
+ {
+ "task": "ti2v",
+ "tier": "best",
+ "model": "Wan-AI/Wan2.2-TI2V-5B-Diffusers",
+ "ditCpuOffload": true,
+ "ditLayerwiseOffload": false,
+ "vaeCpuOffload": false,
+ "textEncoderCpuOffload": true,
+ "imageEncoderCpuOffload": true,
+ "pinCpuMemory": true,
+ "useFsdpInference": false
},
{
- "status": "available",
- "gpu": "4090",
- "vramGb": 24,
"task": "game",
"tier": "quick",
- "model": "FastVideo/Matrix-Game-2.0-Base-Diffusers",
- "modelShort": "Matrix-Game-2.0-Base",
- "attn": "FLASH_ATTN",
- "h": 480,
- "w": 832,
- "frames": 81,
- "steps": 3,
- "cfg": 6,
- "fps": 16,
- "vramMin": 12,
- "ramMin": 32,
- "timeSec": 23.81,
- "vram": "~11.2 GB",
- "offload": true,
- "ditLayerwiseOffload": true,
+ "model": "FastVideo/Matrix-Game-2.0-Base-Distilled-Diffusers",
+ "ditCpuOffload": true,
+ "ditLayerwiseOffload": false,
+ "vaeCpuOffload": false,
"textEncoderCpuOffload": true,
- "keyboardDim": 4,
- "extra": "--image ./input.png --action forward"
+ "imageEncoderCpuOffload": true,
+ "pinCpuMemory": true,
+ "useFsdpInference": false
},
{
- "status": "available",
- "gpu": "4090",
- "vramGb": 24,
"task": "game",
"tier": "best",
- "model": "FastVideo/Matrix-Game-2.0-GTA-Diffusers",
- "modelShort": "Matrix-Game-2.0-GTA",
- "attn": "FLASH_ATTN",
- "h": 480,
- "w": 832,
- "frames": 81,
- "steps": 3,
- "cfg": 6,
- "fps": 16,
- "vramMin": 12,
- "ramMin": 32,
- "timeSec": 24.24,
- "vram": "~11.2 GB",
- "offload": true,
- "ditLayerwiseOffload": true,
+ "model": "FastVideo/Matrix-Game-2.0-GTA-Distilled-Diffusers",
+ "ditCpuOffload": true,
+ "ditLayerwiseOffload": false,
+ "vaeCpuOffload": false,
"textEncoderCpuOffload": true,
- "keyboardDim": 2,
- "extra": "--image ./input.png --action forward"
+ "imageEncoderCpuOffload": true,
+ "pinCpuMemory": true,
+ "useFsdpInference": false
}
]
}
diff --git a/consumer-guide/data/tuning.json b/consumer-guide/data/tuning.json
index ed2652ab2..b2bd30d73 100644
--- a/consumer-guide/data/tuning.json
+++ b/consumer-guide/data/tuning.json
@@ -1,103 +1,170 @@
{
- "_comment": "Single data source for the Advanced Tuning page (app/tuning/adv_page.tsx). Each benchmark run is one real measured configuration. The page filters by workload + GPU and never estimates benchmark values.",
+ "source": {
+ "fastvideoCommit": "a2bfc7cdb2a1de2b138258412e42d183444ffe09",
+ "description": "Defaults transcribed from FastVideo's registered inference presets, pipeline configs, maintained examples, and support matrix at this commit.",
+ "paths": [
+ "fastvideo/registry.py",
+ "fastvideo/pipelines/basic/*/presets.py",
+ "fastvideo/configs/pipelines/*.py",
+ "examples/inference/basic/*.py",
+ "docs/inference/support_matrix.md"
+ ]
+ },
"defaults": {
"model_id": "FastVideo/FastWan2.1-T2V-1.3B-Diffusers",
"workload_type": "t2v",
- "gpu": "rtx5090",
"num_gpus": 1,
- "height": 480,
- "width": 832,
- "num_frames": 81,
- "fps": 24,
- "num_inference_steps": 40,
- "guidance_scale": 5,
- "embedded_cfg_scale": 6,
- "flow_shift": null,
- "seed": -1,
- "attention_backend": "VIDEO_SPARSE_ATTN",
- "VSA_sparsity": 0,
- "dmd_denoising_steps": "1000,757,522",
+ "seed": 1024,
"dit_cpu_offload": false,
"dit_layerwise_offload": false,
"vae_cpu_offload": false,
- "text_encoder_cpu_offload": false,
+ "text_encoder_cpu_offload": true,
"image_encoder_cpu_offload": false,
- "pin_cpu_memory": false,
+ "pin_cpu_memory": true,
"use_fsdp_inference": false
},
"workloads": [
{ "id": "t2v", "name": "Text to Video", "icon": "Film" },
{ "id": "i2v", "name": "Image to Video", "icon": "Sparkles" },
- { "id": "ti2v", "name": "Text+Image to Video", "icon": "Layers" },
+ { "id": "ti2v", "name": "Text + Image to Video", "icon": "Layers" },
{ "id": "game", "name": "Game", "icon": "Zap" }
],
- "gpus": [
- { "id": "rtx4090", "name": "RTX 4090", "vram": "24GB" },
- { "id": "rtx5090", "name": "RTX 5090", "vram": "32GB" }
- ],
- "resolutionPresets": [
- { "height": 480, "width": 832, "label": "480p (832x480)" },
- { "height": 720, "width": 1280, "label": "720p (1280x720)" },
- { "height": 1080, "width": 1920, "label": "1080p (1920x1080)" }
- ],
- "framePresets": [
- { "frames": 49, "label": "49 frames (~2s @24fps)" },
- { "frames": 81, "label": "81 frames (~3.4s @24fps)" },
- { "frames": 121, "label": "121 frames (~5s @24fps)" }
- ],
"attentionBackends": [
- { "id": "FLASH_ATTN", "name": "Flash Attention", "shortName": "Flash", "desc": "Best quality-speed trade-off", "speed": "Baseline" },
- { "id": "VIDEO_SPARSE_ATTN", "name": "Video Sparse Attention", "shortName": "VSA", "desc": "~30% faster, minimal quality loss", "speed": "Fast" },
- { "id": "SAGE_ATTN", "name": "Sage Attention", "shortName": "SAGE", "desc": "~40% faster, visible at high motion", "speed": "Faster" },
- { "id": "SLA_ATTN", "name": "Sparse-Linear Attention", "shortName": "SLA", "desc": "Requires SLA-tuned model checkpoint", "speed": "Fast" }
+ { "id": "FLASH_ATTN", "name": "Flash Attention", "shortName": "Flash", "desc": "Default dense attention backend." },
+ { "id": "VIDEO_SPARSE_ATTN", "name": "Video Sparse Attention", "shortName": "VSA", "desc": "Use only with models marked VSA-compatible." },
+ { "id": "SAGE_ATTN", "name": "Sage Attention", "shortName": "SAGE", "desc": "Quantized attention for models marked Sage-compatible." },
+ { "id": "SLA_ATTN", "name": "Sparse-Linear Attention", "shortName": "SLA", "desc": "Required by TurboDiffusion checkpoints." }
],
"models": [
- { "id": "FastVideo/FastWan2.1-T2V-1.3B-Diffusers", "name": "FastWan2.1 T2V", "size": "1.3B", "workload": "t2v", "dmdMode": true, "dmdNote": "FastWan DMD uses 3-step distillation for faster generation." },
- { "id": "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers", "name": "FastWan2.2 TI2V FullAttn", "size": "5B", "workload": "ti2v", "dmdMode": true, "dmdNote": "FastWan DMD uses 3-step distillation for faster generation." },
- { "id": "Wan-AI/Wan2.2-TI2V-5B-Diffusers", "name": "Wan2.2 TI2V", "size": "5B", "workload": "ti2v" },
- { "id": "Wan-AI/Wan2.2-T2V-A14B-Diffusers", "name": "Wan2.2 T2V", "size": "A14B", "workload": "t2v" },
- { "id": "Wan-AI/Wan2.2-I2V-A14B-Diffusers", "name": "Wan2.2 I2V", "size": "A14B", "workload": "i2v" },
- { "id": "hunyuanvideo-community/HunyuanVideo", "name": "HunyuanVideo", "size": "13B", "workload": "t2v" },
- { "id": "FastVideo/FastHunyuan-diffusers", "name": "FastHunyuan", "size": "13B", "workload": "t2v" },
- { "id": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers", "name": "Wan2.1 T2V", "size": "1.3B", "workload": "t2v" },
- { "id": "Wan-AI/Wan2.1-T2V-14B-Diffusers", "name": "Wan2.1 T2V", "size": "14B", "workload": "t2v" },
- { "id": "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers", "name": "Wan2.1 I2V 480P", "size": "14B", "workload": "i2v" },
- { "id": "Wan-AI/Wan2.1-I2V-14B-720P-Diffusers", "name": "Wan2.1 I2V 720P", "size": "14B", "workload": "i2v" },
- { "id": "loayrashid/TurboWan2.1-T2V-1.3B-Diffusers", "name": "TurboWan2.1 T2V", "size": "1.3B", "workload": "t2v" },
- { "id": "loayrashid/TurboWan2.1-T2V-14B-Diffusers", "name": "TurboWan2.1 T2V", "size": "14B", "workload": "t2v" },
- { "id": "loayrashid/TurboWan2.2-I2V-A14B-Diffusers", "name": "TurboWan2.2 I2V", "size": "A14B", "workload": "i2v" },
- { "id": "FastVideo/LongCat-Video-T2V-Diffusers", "name": "LongCat T2V", "size": "13.6B", "workload": "t2v" },
- { "id": "FastVideo/Matrix-Game-2.0-Base-Diffusers", "name": "Matrix Game 2.0 Base", "size": "1.3B", "workload": "game", "keyboardDim": 4 },
- { "id": "FastVideo/Matrix-Game-2.0-GTA-Diffusers", "name": "Matrix Game 2.0 GTA", "size": "1.3B", "workload": "game", "keyboardDim": 2 },
- { "id": "FastVideo/Matrix-Game-2.0-TempleRun-Diffusers", "name": "Matrix Game 2.0 TempleRun", "size": "1.3B", "workload": "game", "keyboardDim": 7 }
- ],
- "runs": [
- { "model": "FastVideo/FastWan2.1-T2V-1.3B-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 40, "attention": "VIDEO_SPARSE_ATTN", "generationSeconds": 22.91, "runtimeSeconds": 139.44 },
- { "model": "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 40, "attention": "VIDEO_SPARSE_ATTN", "generationSeconds": 17.39, "runtimeSeconds": 116.57 },
- { "model": "Wan-AI/Wan2.2-TI2V-5B-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 40, "attention": "FLASH_ATTN", "generationSeconds": 71.01, "runtimeSeconds": 223.29 },
- { "model": "Wan-AI/Wan2.2-T2V-A14B-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 40, "attention": "FLASH_ATTN", "generationSeconds": 782.09, "runtimeSeconds": 1144.85 },
- { "model": "Wan-AI/Wan2.2-I2V-A14B-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 40, "attention": "FLASH_ATTN", "generationSeconds": 827.48, "runtimeSeconds": 1169.64 },
- { "model": "hunyuanvideo-community/HunyuanVideo", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 40, "attention": "FLASH_ATTN", "generationSeconds": 575.39, "runtimeSeconds": 713.74 },
- { "model": "FastVideo/FastHunyuan-diffusers", "gpu": "rtx5090", "height": 720, "width": 1280, "frames": 81, "steps": 50, "attention": "FLASH_ATTN", "generationSeconds": 1389.71, "runtimeSeconds": 1492.36 },
- { "model": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 40, "attention": "FLASH_ATTN", "generationSeconds": 155.86, "runtimeSeconds": 227.07 },
- { "model": "Wan-AI/Wan2.1-T2V-14B-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 40, "attention": "FLASH_ATTN", "generationSeconds": 780.44, "runtimeSeconds": 954.84 },
- { "model": "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 40, "attention": "FLASH_ATTN", "generationSeconds": 842.11, "runtimeSeconds": 1058.56 },
- { "model": "Wan-AI/Wan2.1-I2V-14B-720P-Diffusers", "gpu": "rtx5090", "height": 720, "width": 1280, "frames": 81, "steps": 40, "attention": "FLASH_ATTN", "generationSeconds": 2832.76, "runtimeSeconds": 3026.41 },
- { "model": "loayrashid/TurboWan2.1-T2V-1.3B-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 4, "attention": "SLA_ATTN", "generationSeconds": 16.16, "runtimeSeconds": 92.77 },
- { "model": "loayrashid/TurboWan2.1-T2V-14B-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 4, "attention": "SLA_ATTN", "generationSeconds": 60.35, "runtimeSeconds": 604.26 },
- { "model": "loayrashid/TurboWan2.2-I2V-A14B-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 81, "steps": 4, "attention": "SLA_ATTN", "runtimeSeconds": 364.0 },
- { "model": "FastVideo/LongCat-Video-T2V-Diffusers", "gpu": "rtx5090", "height": 480, "width": 832, "frames": 93, "steps": 40, "attention": "FLASH_ATTN", "generationSeconds": 915.89, "runtimeSeconds": 1061.24 },
- { "model": "FastVideo/Matrix-Game-2.0-Base-Diffusers", "gpu": "rtx5090", "height": 352, "width": 640, "frames": 81, "steps": 3, "attention": "FLASH_ATTN", "generationSeconds": 12.29, "runtimeSeconds": 247.2 },
- { "model": "FastVideo/Matrix-Game-2.0-GTA-Diffusers", "gpu": "rtx5090", "height": 352, "width": 640, "frames": 81, "steps": 3, "attention": "FLASH_ATTN", "generationSeconds": 13.03, "runtimeSeconds": 212.37 },
- { "model": "FastVideo/Matrix-Game-2.0-TempleRun-Diffusers", "gpu": "rtx5090", "height": 352, "width": 640, "frames": 81, "steps": 3, "attention": "FLASH_ATTN", "generationSeconds": 12.85, "runtimeSeconds": 176.79 },
- { "model": "FastVideo/FastWan2.1-T2V-1.3B-Diffusers", "gpu": "rtx4090", "height": 480, "width": 832, "frames": 81, "steps": 3, "attention": "VIDEO_SPARSE_ATTN", "generationSeconds": 32.42 },
- { "model": "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers", "gpu": "rtx4090", "height": 480, "width": 832, "frames": 81, "steps": 3, "attention": "VIDEO_SPARSE_ATTN", "generationSeconds": 22.78 },
- { "model": "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers", "gpu": "rtx4090", "height": 480, "width": 832, "frames": 81, "steps": 3, "attention": "FLASH_ATTN", "generationSeconds": 17.81 },
- { "model": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers", "gpu": "rtx4090", "height": 480, "width": 832, "frames": 81, "steps": 3, "attention": "FLASH_ATTN", "generationSeconds": 28.62 },
- { "model": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers", "gpu": "rtx4090", "height": 480, "width": 832, "frames": 81, "steps": 3, "attention": "SAGE_ATTN", "generationSeconds": 24.62 },
- { "model": "loayrashid/TurboWan2.1-T2V-1.3B-Diffusers", "gpu": "rtx4090", "height": 480, "width": 832, "frames": 81, "steps": 3, "attention": "SLA_ATTN", "generationSeconds": 20.13 },
- { "model": "FastVideo/Matrix-Game-2.0-Base-Diffusers", "gpu": "rtx4090", "height": 480, "width": 832, "frames": 81, "steps": 3, "attention": "FLASH_ATTN", "generationSeconds": 23.81 },
- { "model": "FastVideo/Matrix-Game-2.0-GTA-Diffusers", "gpu": "rtx4090", "height": 480, "width": 832, "frames": 81, "steps": 3, "attention": "FLASH_ATTN", "generationSeconds": 24.24 },
- { "model": "FastVideo/Matrix-Game-2.0-TempleRun-Diffusers", "gpu": "rtx4090", "height": 480, "width": 832, "frames": 81, "steps": 3, "attention": "FLASH_ATTN", "generationSeconds": 23.38 }
+ {
+ "id": "FastVideo/FastWan2.1-T2V-1.3B-Diffusers",
+ "name": "FastWan2.1 T2V",
+ "size": "1.3B",
+ "workload": "t2v",
+ "recipe": { "height": 448, "width": 832, "numFrames": 61, "fps": 16, "numInferenceSteps": 3, "guidanceScale": 3, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 8, "boundaryRatio": null, "dmdDenoisingSteps": [1000, 757, 522], "attentionBackends": ["VIDEO_SPARSE_ATTN", "FLASH_ATTN"], "defaultAttentionBackend": "VIDEO_SPARSE_ATTN", "vsaSparsity": 0.8 }
+ },
+ {
+ "id": "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers",
+ "name": "FastWan2.2 TI2V FullAttn",
+ "size": "5B",
+ "workload": "ti2v",
+ "recipe": { "height": 704, "width": 1280, "numFrames": 121, "fps": 24, "numInferenceSteps": 3, "guidanceScale": 5, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 5, "boundaryRatio": null, "dmdDenoisingSteps": [1000, 757, 522], "attentionBackends": ["FLASH_ATTN", "VIDEO_SPARSE_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "Wan-AI/Wan2.2-TI2V-5B-Diffusers",
+ "name": "Wan2.2 TI2V",
+ "size": "5B",
+ "workload": "ti2v",
+ "recipe": { "height": 704, "width": 1280, "numFrames": 121, "fps": 24, "numInferenceSteps": 50, "guidanceScale": 5, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 5, "boundaryRatio": null, "dmdDenoisingSteps": null, "attentionBackends": ["FLASH_ATTN", "SAGE_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "Wan-AI/Wan2.2-T2V-A14B-Diffusers",
+ "name": "Wan2.2 T2V",
+ "size": "A14B",
+ "workload": "t2v",
+ "recipe": { "height": 720, "width": 1280, "numFrames": 81, "fps": 16, "numInferenceSteps": 40, "guidanceScale": 4, "guidanceScale2": 3, "embeddedCfgScale": 6, "flowShift": 12, "boundaryRatio": 0.875, "dmdDenoisingSteps": null, "attentionBackends": ["FLASH_ATTN", "SAGE_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "Wan-AI/Wan2.2-I2V-A14B-Diffusers",
+ "name": "Wan2.2 I2V",
+ "size": "A14B",
+ "workload": "i2v",
+ "recipe": { "height": 832, "width": 480, "numFrames": 81, "fps": 16, "numInferenceSteps": 40, "guidanceScale": 3.5, "guidanceScale2": 3.5, "embeddedCfgScale": 6, "flowShift": 5, "boundaryRatio": 0.9, "dmdDenoisingSteps": null, "attentionBackends": ["FLASH_ATTN", "SAGE_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "hunyuanvideo-community/HunyuanVideo",
+ "name": "HunyuanVideo",
+ "size": "13B",
+ "workload": "t2v",
+ "recipe": { "height": 720, "width": 1280, "numFrames": 125, "fps": 24, "numInferenceSteps": 50, "guidanceScale": 1, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 7, "boundaryRatio": null, "dmdDenoisingSteps": null, "attentionBackends": ["FLASH_ATTN", "SAGE_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "FastVideo/FastHunyuan-diffusers",
+ "name": "FastHunyuan",
+ "size": "13B",
+ "workload": "t2v",
+ "recipe": { "height": 720, "width": 1280, "numFrames": 125, "fps": 24, "numInferenceSteps": 6, "guidanceScale": 1, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 17, "boundaryRatio": null, "dmdDenoisingSteps": null, "attentionBackends": ["FLASH_ATTN", "SAGE_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
+ "name": "Wan2.1 T2V",
+ "size": "1.3B",
+ "workload": "t2v",
+ "recipe": { "height": 480, "width": 832, "numFrames": 81, "fps": 16, "numInferenceSteps": 50, "guidanceScale": 3, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 3, "boundaryRatio": null, "dmdDenoisingSteps": null, "attentionBackends": ["FLASH_ATTN", "SAGE_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "Wan-AI/Wan2.1-T2V-14B-Diffusers",
+ "name": "Wan2.1 T2V",
+ "size": "14B",
+ "workload": "t2v",
+ "recipe": { "height": 720, "width": 1280, "numFrames": 81, "fps": 16, "numInferenceSteps": 50, "guidanceScale": 5, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 5, "boundaryRatio": null, "dmdDenoisingSteps": null, "attentionBackends": ["FLASH_ATTN", "SAGE_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers",
+ "name": "Wan2.1 I2V 480P",
+ "size": "14B",
+ "workload": "i2v",
+ "recipe": { "height": 480, "width": 832, "numFrames": 81, "fps": 16, "numInferenceSteps": 40, "guidanceScale": 5, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 3, "boundaryRatio": null, "dmdDenoisingSteps": null, "attentionBackends": ["FLASH_ATTN", "SAGE_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "Wan-AI/Wan2.1-I2V-14B-720P-Diffusers",
+ "name": "Wan2.1 I2V 720P",
+ "size": "14B",
+ "workload": "i2v",
+ "recipe": { "height": 720, "width": 1280, "numFrames": 81, "fps": 16, "numInferenceSteps": 40, "guidanceScale": 5, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 5, "boundaryRatio": null, "dmdDenoisingSteps": null, "attentionBackends": ["FLASH_ATTN", "SAGE_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "loayrashid/TurboWan2.1-T2V-1.3B-Diffusers",
+ "name": "TurboWan2.1 T2V",
+ "size": "1.3B",
+ "workload": "t2v",
+ "recipe": { "height": 480, "width": 832, "numFrames": 81, "fps": 16, "numInferenceSteps": 4, "guidanceScale": 1, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 3, "boundaryRatio": null, "dmdDenoisingSteps": null, "attentionBackends": ["SLA_ATTN"], "defaultAttentionBackend": "SLA_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "loayrashid/TurboWan2.1-T2V-14B-Diffusers",
+ "name": "TurboWan2.1 T2V",
+ "size": "14B",
+ "workload": "t2v",
+ "recipe": { "height": 720, "width": 1280, "numFrames": 81, "fps": 16, "numInferenceSteps": 4, "guidanceScale": 1, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 5, "boundaryRatio": null, "dmdDenoisingSteps": null, "attentionBackends": ["SLA_ATTN"], "defaultAttentionBackend": "SLA_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "loayrashid/TurboWan2.2-I2V-A14B-Diffusers",
+ "name": "TurboWan2.2 I2V",
+ "size": "A14B",
+ "workload": "i2v",
+ "recipe": { "height": 720, "width": 1280, "numFrames": 81, "fps": 16, "numInferenceSteps": 4, "guidanceScale": 1, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 5, "boundaryRatio": 0.9, "dmdDenoisingSteps": null, "attentionBackends": ["SLA_ATTN"], "defaultAttentionBackend": "SLA_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "FastVideo/LongCat-Video-T2V-Diffusers",
+ "name": "LongCat T2V",
+ "size": "13.6B",
+ "workload": "t2v",
+ "recipe": { "height": 480, "width": 848, "numFrames": 121, "fps": 24, "numInferenceSteps": 50, "guidanceScale": 1, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": null, "boundaryRatio": null, "dmdDenoisingSteps": null, "attentionBackends": ["FLASH_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "FastVideo/Matrix-Game-2.0-Base-Distilled-Diffusers",
+ "name": "Matrix Game 2.0 Base",
+ "size": "1.3B",
+ "workload": "game",
+ "keyboardDim": 4,
+ "recipe": { "height": 352, "width": 640, "numFrames": 57, "fps": 25, "numInferenceSteps": 3, "guidanceScale": 1, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 5, "boundaryRatio": null, "dmdDenoisingSteps": [1000, 666, 333], "attentionBackends": ["FLASH_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "FastVideo/Matrix-Game-2.0-GTA-Distilled-Diffusers",
+ "name": "Matrix Game 2.0 GTA",
+ "size": "1.3B",
+ "workload": "game",
+ "keyboardDim": 2,
+ "recipe": { "height": 352, "width": 640, "numFrames": 57, "fps": 25, "numInferenceSteps": 3, "guidanceScale": 1, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 5, "boundaryRatio": null, "dmdDenoisingSteps": [1000, 666, 333], "attentionBackends": ["FLASH_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ },
+ {
+ "id": "FastVideo/Matrix-Game-2.0-TempleRun-Distilled-Diffusers",
+ "name": "Matrix Game 2.0 TempleRun",
+ "size": "1.3B",
+ "workload": "game",
+ "keyboardDim": 7,
+ "usesMouse": false,
+ "recipe": { "height": 352, "width": 640, "numFrames": 57, "fps": 25, "numInferenceSteps": 3, "guidanceScale": 1, "guidanceScale2": null, "embeddedCfgScale": 6, "flowShift": 5, "boundaryRatio": null, "dmdDenoisingSteps": [1000, 666, 333], "attentionBackends": ["FLASH_ATTN"], "defaultAttentionBackend": "FLASH_ATTN", "vsaSparsity": 0 }
+ }
]
}
diff --git a/consumer-guide/next.config.mjs b/consumer-guide/next.config.mjs
index bf58c0c3c..47f039e89 100644
--- a/consumer-guide/next.config.mjs
+++ b/consumer-guide/next.config.mjs
@@ -5,7 +5,6 @@ const nextConfig = {
output: "export",
trailingSlash: true,
basePath,
- assetPrefix: basePath,
typescript: {
ignoreBuildErrors: false,
},
diff --git a/consumer-guide/package.json b/consumer-guide/package.json
index d5945fab9..9b39db0b4 100644
--- a/consumer-guide/package.json
+++ b/consumer-guide/package.json
@@ -5,7 +5,7 @@
"packageManager": "pnpm@10.28.0",
"scripts": {
"dev": "next dev -p 3003",
- "build": "next build"
+ "build": "node scripts/validate-data.mjs && next build"
},
"dependencies": {
"lucide-react": "^0.564.0",
diff --git a/consumer-guide/scripts/validate-data.mjs b/consumer-guide/scripts/validate-data.mjs
new file mode 100644
index 000000000..26e26543f
--- /dev/null
+++ b/consumer-guide/scripts/validate-data.mjs
@@ -0,0 +1,52 @@
+import assert from "node:assert/strict"
+import { readFile } from "node:fs/promises"
+
+const load = async (name) => JSON.parse(await readFile(new URL(`../data/${name}`, import.meta.url), "utf8"))
+const [quickstart, tuning] = await Promise.all([load("quickstart.json"), load("tuning.json")])
+
+assert.match(tuning.source.fastvideoCommit, /^[0-9a-f]{40}$/)
+
+const backendIds = new Set(tuning.attentionBackends.map(({ id }) => id))
+const workloadIds = new Set(tuning.workloads.map(({ id }) => id))
+const modelIds = new Set()
+for (const model of tuning.models) {
+ assert(!modelIds.has(model.id), `duplicate model: ${model.id}`)
+ modelIds.add(model.id)
+ assert(workloadIds.has(model.workload), `${model.id}: unknown workload ${model.workload}`)
+
+ const { recipe } = model
+ for (const key of ["height", "width", "numFrames", "fps", "numInferenceSteps"]) {
+ assert(Number.isInteger(recipe[key]) && recipe[key] > 0, `${model.id}: invalid ${key}`)
+ }
+ if (model.workload === "game") {
+ assert(recipe.numFrames % 12 === 9, `${model.id}: Matrix frames must be 9 + 12k`)
+ }
+ assert(recipe.guidanceScale > 0, `${model.id}: guidance must be positive`)
+ assert(recipe.vsaSparsity >= 0 && recipe.vsaSparsity <= 1, `${model.id}: invalid VSA sparsity`)
+ if (recipe.dmdDenoisingSteps !== null) {
+ assert(recipe.dmdDenoisingSteps.length > 0 &&
+ recipe.dmdDenoisingSteps.every((step) => Number.isInteger(step) && step > 0),
+ `${model.id}: invalid DMD schedule`)
+ }
+ assert(recipe.attentionBackends.length > 0, `${model.id}: no attention backend`)
+ assert(recipe.attentionBackends.includes(recipe.defaultAttentionBackend), `${model.id}: invalid default backend`)
+ for (const backend of recipe.attentionBackends) {
+ assert(backendIds.has(backend), `${model.id}: unknown backend ${backend}`)
+ }
+}
+
+assert(modelIds.has(tuning.defaults.model_id), "unknown default model")
+const profileKeys = new Set()
+for (const profile of quickstart.profiles) {
+ assert(modelIds.has(profile.model), `quick-start profile uses unknown model: ${profile.model}`)
+ assert(tuning.models.find(({ id }) => id === profile.model).workload === profile.task,
+ `${profile.model}: quick-start task does not match recipe workload`)
+ const key = `${profile.task}:${profile.tier}`
+ assert(!profileKeys.has(key), `duplicate quick-start profile: ${key}`)
+ profileKeys.add(key)
+}
+
+const unsupportedClaims = /"(?:generationSeconds|runtimeSeconds|timeSec|firstRunSec|vramMin|ramMin)"/
+assert(!unsupportedClaims.test(JSON.stringify({ quickstart, tuning })), "unproven performance claim in recipe data")
+
+console.log(`Validated ${tuning.models.length} recipes and ${quickstart.profiles.length} quick-start profiles.`)
diff --git a/docs/README.md b/docs/README.md
index f2e22e853..f804502fb 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -3,10 +3,10 @@
This directory contains the FastVideo documentation built with MkDocs.
The Quick Start and Advanced Tuning pages embed an interactive **config
-generator** — a separate Next.js app in [`consumer-guide/`](../consumer-guide/)
-that is built independently and copied into the docs site under
-`/config-generator/`. To work on that app itself, see
-[`consumer-guide/README.md`](../consumer-guide/README.md).
+generator** — a separate Next.js app documented in
+[`consumer-guide/README.md`](../consumer-guide/README.md). It is built
+independently and copied into the docs artifact under `config-generator/`. Its
+asset URLs use the GitHub Pages base path reported by `actions/configure-pages`.
## Build the docs locally
diff --git a/docs/assets/config-generator.js b/docs/assets/config-generator.js
new file mode 100644
index 000000000..56aa62b7d
--- /dev/null
+++ b/docs/assets/config-generator.js
@@ -0,0 +1,15 @@
+(() => {
+ const resizeType = "fastvideo-config-generator:resize";
+
+ window.addEventListener("message", (event) => {
+ if (event.origin !== window.location.origin) return;
+ if (!event.data || typeof event.data !== "object" || event.data.type !== resizeType) return;
+
+ const frame = Array.from(document.querySelectorAll("iframe[data-config-generator]"))
+ .find((candidate) => candidate.contentWindow === event.source);
+ const height = Number(event.data.height);
+ if (!frame || !Number.isFinite(height) || height < 320 || height > 20000) return;
+
+ frame.style.height = `${Math.ceil(height)}px`;
+ });
+})();
diff --git a/docs/assets/custom.css b/docs/assets/custom.css
index f79d337d4..6c9b5ad8b 100644
--- a/docs/assets/custom.css
+++ b/docs/assets/custom.css
@@ -62,11 +62,11 @@ img {
}
.adv-tuning-guide-wrap {
- width: calc(100vw - 26rem);
+ width: 100%;
max-width: none;
overflow: hidden;
background: #fff;
- margin-right: 1rem;
+ margin-right: 0;
}
.adv-tuning-guide-frame {
diff --git a/docs/getting_started/advanced_tuning_guide.md b/docs/getting_started/advanced_tuning_guide.md
index 40edf9cc8..1f5ecd7b2 100644
--- a/docs/getting_started/advanced_tuning_guide.md
+++ b/docs/getting_started/advanced_tuning_guide.md
@@ -1,21 +1,14 @@
---
hide:
- - toc
+- toc
---
-
-
diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md
index 0ac03f5f3..4464afa47 100644
--- a/docs/getting_started/installation.md
+++ b/docs/getting_started/installation.md
@@ -68,5 +68,5 @@ uv pip install flash-attn --no-build-isolation -v
Now that FastVideo is installed, head to the [Quick Start](quick_start.md) to generate your first video.
!!! tip "Not sure which command to run?"
- - The [Quick Start](quick_start.md) page has an interactive Config Generator. Select your task and hardware to get a ready-to-copy `fastvideo generate` command instantly.
- - For fine-grained control over all parameters, use the [Advanced Tuning Guide](advanced_tuning_guide.md).
\ No newline at end of file
+ - The [Quick Start](quick_start.md) page has an interactive Config Generator. Select your task, maintained model profile, and GPU count to get a ready-to-copy `fastvideo generate` command.
+ - For fine-grained control over all parameters, use the [Advanced Tuning Guide](advanced_tuning_guide.md).
diff --git a/docs/getting_started/quick_start.md b/docs/getting_started/quick_start.md
index 453d81885..bdc22e2bc 100644
--- a/docs/getting_started/quick_start.md
+++ b/docs/getting_started/quick_start.md
@@ -25,83 +25,21 @@ uv pip install flash-attn --no-build-isolation -v
## ⚡ Generate Your Command
-Select your task and hardware to get a ready-to-run command:
+Select your task, model profile, and GPU count to get a ready-to-run command:
-
-
!!! tip "Need more control?"
Use the [Advanced Tuning Guide](advanced_tuning_guide.md) to tune all parameters — resolution, attention backend, memory offloading, and more.
-## Python API
-
-Prefer Python over the CLI? Here are equivalent examples:
-
-### Text-to-Video Generation
-
-```python
-from fastvideo import VideoGenerator
-
-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()
-```
-
## Next Steps
- [Advanced Tuning Guide](advanced_tuning_guide.md) - Fine-grained parameter tuning
diff --git a/mkdocs.yml b/mkdocs.yml
index 52af63949..70db2fd8b 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/config-generator.js]
css_files: [assets/custom.css]
- api-autonav:
modules: ["fastvideo"]
@@ -238,3 +238,4 @@ extra_css:
# Custom JavaScript
extra_javascript:
- assets/copy-page.js
+ - assets/config-generator.js