diff --git a/docs/assets/cookbook-recipes.json b/docs/assets/cookbook-recipes.json
index 8875361dc..e89e58d12 100644
--- a/docs/assets/cookbook-recipes.json
+++ b/docs/assets/cookbook-recipes.json
@@ -1,5 +1,5 @@
{
- "version": 11,
+ "version": 12,
"recipes": [
{
"id": "fastwan21-t2v",
@@ -10,6 +10,11 @@
"summary": "Generate a video in three denoising steps with the distilled FastWan2.1 1.3B checkpoint and video sparse attention.",
"model": "FastVideo/FastWan2.1-T2V-1.3B-Diffusers",
"source": "scripts/inference/inference_wan_VSA_DMD_1_3B.yaml",
+ "serving": {
+ "source": "examples/serving/openai_fastwan21_1_3b.yaml",
+ "install": "UV_TORCH_BACKEND=cu130 uv pip install -e .",
+ "env": "FASTVIDEO_ATTENTION_BACKEND=VIDEO_SPARSE_ATTN"
+ },
"command": "FASTVIDEO_ATTENTION_BACKEND=VIDEO_SPARSE_ATTN fastvideo generate --config scripts/inference/inference_wan_VSA_DMD_1_3B.yaml",
"gpu_types": ["NVIDIA"],
"hardware": {"gpu_count": 1, "evidence": "source-configured"},
@@ -25,6 +30,13 @@
"summary": "The maintained high-capacity Wan2.2 text-to-video example with CPU offload settings encoded in its checked-in Python source.",
"model": "Wan-AI/Wan2.2-T2V-A14B-Diffusers",
"source": "examples/inference/basic/basic_wan2_2.py",
+ "serving": {
+ "source": "examples/serving/openai_wan22_t2v_a14b.yaml",
+ "install": "UV_TORCH_BACKEND=cu130 uv pip install -e .",
+ "limitations": [
+ "Generation at 1280 × 720 on two GPUs with DiT CPU offload is slow. The client examples stop polling after 30 minutes while the job keeps running on the server; retrieve it by ID or raise the deadline."
+ ]
+ },
"command": "python examples/inference/basic/basic_wan2_2.py",
"gpu_types": ["NVIDIA"],
"hardware": {"gpu_count": 2, "evidence": "source-configured"},
@@ -54,6 +66,14 @@
"summary": "Use one maintained 5B checkpoint for text-to-video or add an image input to switch the same recipe to image-to-video.",
"model": "Wan-AI/Wan2.2-TI2V-5B-Diffusers",
"source": "examples/inference/basic/basic_wan2_2_ti2v.py",
+ "serving": {
+ "source": "examples/serving/openai_wan22_ti2v_5b.yaml",
+ "install": "UV_TORCH_BACKEND=cu130 uv pip install -e .",
+ "task": "Text to video",
+ "limitations": [
+ "This server walkthrough sends a text prompt only. The server API also accepts an image reference (input_reference) for this checkpoint, but the checked-in clients do not upload one. For the cookbook's image-to-video workflow, use the Python command."
+ ]
+ },
"command": "python examples/inference/basic/basic_wan2_2_ti2v.py",
"gpu_types": ["NVIDIA"],
"hardware": {"gpu_count": 1, "evidence": "source-configured"},
@@ -456,7 +476,8 @@
"source": "examples/inference/basic/basic_fasth3.py",
"serving": {
"source": "examples/serving/openai_fasth3.yaml",
- "install": "UV_TORCH_BACKEND=cu130 uv pip install -e \".[fasth3]\""
+ "install": "UV_TORCH_BACKEND=cu130 uv pip install -e \".[fasth3]\"",
+ "audio": true
},
"command": "UV_TORCH_BACKEND=cu130 uv pip install -e \".[fasth3]\"\npython examples/inference/basic/basic_fasth3.py --prompt \"(S1) A presenter says [English] FastVideo runs FastH3.\" --profile all",
"gpu_types": ["NVIDIA"],
@@ -481,7 +502,8 @@
"serving": {
"source": "examples/serving/mlx_fasth3.yaml",
"install": "uv pip install -e \".[mlx]\"",
- "prepare": "hf download FastVideo/FastVideo-Minimax-FastH3-Preview-v0.2 --local-dir ./FastH3-Preview-v0.2\npython scripts/checkpoint_conversion/convert_minimax_h3_mlx.py --model-root ./FastH3-Preview-v0.2/transformer --out ./FastH3-MLX --formats \"int6\""
+ "prepare": "hf download FastVideo/FastVideo-Minimax-FastH3-Preview-v0.2 --local-dir ./FastH3-Preview-v0.2\npython scripts/checkpoint_conversion/convert_minimax_h3_mlx.py --model-root ./FastH3-Preview-v0.2/transformer --out ./FastH3-MLX --formats \"int6\"",
+ "audio": true
},
"group": "fasth3-preview",
"group_label": "FastH3 V1",
@@ -528,7 +550,8 @@
"serving": {
"source": "examples/serving/openai_fasth3_spark.yaml",
"install": "UV_TORCH_BACKEND=cu130 uv pip install -e .",
- "env": "FASTVIDEO_VSA_SM100A=0 FASTVIDEO_FA4=0 FASTVIDEO_ATTENTION_BACKEND=VIDEO_SPARSE_ATTN_H3"
+ "env": "FASTVIDEO_VSA_SM100A=0 FASTVIDEO_FA4=0 FASTVIDEO_ATTENTION_BACKEND=VIDEO_SPARSE_ATTN_H3",
+ "audio": true
},
"command": "FASTVIDEO_VSA_SM100A=0 FASTVIDEO_FA4=0 FASTVIDEO_ATTENTION_BACKEND=VIDEO_SPARSE_ATTN_H3 FASTVIDEO_STAGE_LOGGING=1 fastvideo generate --config examples/inference/basic/basic_fasth3_spark.yaml",
"gpu_types": ["NVIDIA"],
@@ -592,7 +615,8 @@
"source": "examples/inference/basic/basic_fasth3_8step.py",
"serving": {
"source": "examples/serving/openai_fasth3_8step.yaml",
- "install": "UV_TORCH_BACKEND=cu130 uv pip install -e \".[fasth3]\""
+ "install": "UV_TORCH_BACKEND=cu130 uv pip install -e \".[fasth3]\"",
+ "audio": true
},
"command": "UV_TORCH_BACKEND=cu130 uv pip install -e \".[fasth3]\"\npython examples/inference/basic/basic_fasth3_8step.py --prompt \"(S1) A presenter says [English] FastVideo runs FastH3.\" --profile strict --no-inference-torch-compile --no-compile-vae",
"gpu_types": ["NVIDIA"],
@@ -621,7 +645,8 @@
"serving": {
"source": "examples/serving/mlx_fasth3_8step.yaml",
"install": "uv pip install -e \".[mlx]\"",
- "prepare": "hf download FastVideo/FastVideo-FastH3-8-Step-V2 --local-dir ./FastH3-8-Step-V2\npython scripts/checkpoint_conversion/convert_minimax_h3_mlx.py --model-root ./FastH3-8-Step-V2/transformer --out ./FastH3-8-Step-V2-MLX --formats \"int8\" --include-vsa"
+ "prepare": "hf download FastVideo/FastVideo-FastH3-8-Step-V2 --local-dir ./FastH3-8-Step-V2\npython scripts/checkpoint_conversion/convert_minimax_h3_mlx.py --model-root ./FastH3-8-Step-V2/transformer --out ./FastH3-8-Step-V2-MLX --formats \"int8\" --include-vsa",
+ "audio": true
},
"group": "fasth3-8step-v2",
"group_label": "FastH3 V2",
diff --git a/docs/assets/cookbook.js b/docs/assets/cookbook.js
index 5b00e95ae..689552355 100644
--- a/docs/assets/cookbook.js
+++ b/docs/assets/cookbook.js
@@ -506,6 +506,10 @@
const runtime = runtimeFor(recipe);
const profile = servingPanel && servingProfiles[recipe.id];
const useServer = Boolean(profile && usagePreference === "server");
+ // Only some servers expose the browser playground or return audio; both come from the serving profile.
+ const hasPlayground = Boolean(profile && profile.playground_url);
+ const hasAudio = Boolean(profile && profile.audio);
+ const playgroundAny = familyRecipes.some((item) => servingProfiles[item.id] && servingProfiles[item.id].playground_url);
// The measured local profile and the server config have separate evidence.
const activeRecipe = useServer ? { ...recipe, hardware: profile.hardware, evidence: "Source-backed" } : recipe;
const knobs = knobsFor(recipe);
@@ -515,14 +519,19 @@
usage.querySelectorAll("[data-cookbook-mode]").forEach((option) => {
const selected = option.dataset.cookbookMode === (useServer ? "server" : "python");
option.disabled = option.dataset.cookbookMode === "server" && !profile;
+ const hint = option.querySelector("[data-cookbook-server-hint]");
+ if (hint) hint.textContent = (profile ? hasPlayground : playgroundAny) ? "Playground, cURL, or an API client" : "cURL or an API client";
option.classList.toggle("cookbook-option--selected", selected);
option.setAttribute("aria-pressed", String(selected));
});
+ const servedNames = [...new Set(familyRecipes.filter((item) => servingProfiles[item.id]).map((item) => item.group_label || item.label))];
servingAvailability.textContent = profile
- ? "The playground and the OpenAI Python client share one server process. Both workflows can run on your own machine."
+ ? `${hasPlayground ? "The playground" : "cURL"} and the OpenAI Python client share one server process. Both workflows can run on your own machine.`
: servingLoadFailed
- ? "Server examples could not be loaded. Open the H3 server guide below, or use Python directly."
- : "This recipe uses Python directly. FastH3 V1 and FastH3 V2 can also run a local server for the playground and the OpenAI Python client.";
+ ? "Server examples could not be loaded. Use Python directly."
+ : servedNames.length
+ ? `This recipe uses Python directly. ${new Intl.ListFormat("en").format(servedNames)} can also run a local server for ${playgroundAny ? "the playground and " : "cURL and "}the OpenAI Python client.`
+ : "This recipe uses Python directly.";
servingPanel.hidden = !useServer;
commandBlock.hidden = useServer;
root.querySelector("[data-cookbook-python-note]").hidden = useServer;
@@ -531,11 +540,12 @@
if (useServer) {
const isMLX = profile.runtime === "mlx";
const isSpark = runtime.id === "spark";
+ const where = hasPlayground ? "in the playground or your app" : "in your app";
servingPanel.querySelector("[data-cookbook-server-lifetime]").textContent = isMLX
- ? "Start once, then change prompts in the playground or your app. MLX reuses its pipeline and prompt cache, but loads and releases model components between phases to limit unified-memory use. It does not keep all weights resident."
+ ? `Start once, then change prompts ${where}. MLX reuses its pipeline and prompt cache, but loads and releases model components between phases to limit unified-memory use. It does not keep all weights resident.`
: isSpark
- ? "Start once, then change prompts in the playground or your app. On a DGX Spark, lazy module load still reloads Qwen3-VL and the DiT between phases of each request, so later prompts are not a free hot cache."
- : "Start once, then change prompts in the playground or your app. CUDA requests reuse the loaded model. The Python SDK can also reuse a generator within one process.";
+ ? `Start once, then change prompts ${where}. On a DGX Spark, lazy module load still reloads Qwen3-VL and the DiT between phases of each request, so later prompts are not a free hot cache.`
+ : `Start once, then change prompts ${where}. CUDA requests reuse the loaded model. The Python SDK can also reuse a generator within one process.`;
servingPanel.querySelector("[data-cookbook-install-guide]").href = isMLX
? "../../getting_started/installation/mlx/"
: isSpark
@@ -546,7 +556,10 @@
servingPanel.querySelector("[data-cookbook-server-install]").textContent = profile.install;
servingPanel.querySelector("[data-cookbook-server-command]").textContent = profile.command;
servingPanel.querySelector("[data-cookbook-health-command]").textContent = profile.health_command;
- servingPanel.querySelector("[data-cookbook-playground]").href = profile.playground_url;
+ servingPanel.querySelectorAll("[data-cookbook-playground-only]").forEach((element) => {
+ element.hidden = !hasPlayground;
+ });
+ if (hasPlayground) servingPanel.querySelector("[data-cookbook-playground]").href = profile.playground_url;
const client = profile.clients[selectedClient];
const filename = client.source.split("/").pop();
servingPanel.querySelector("[data-cookbook-client-install]").textContent = client.install;
@@ -574,13 +587,17 @@
});
description.textContent = useServer
- ? `${recipe.group_label || recipe.label} generates video with audio. Start the local server, then use the playground or the OpenAI Python client. This profile uses the checked-in ${runtime.label} configuration.`
+ ? `${recipe.group_label || recipe.label} generates video${hasAudio ? " with audio" : ""}. Start the local server, then use ${hasPlayground ? "the playground or " : "cURL or "}the OpenAI Python client. This profile uses the checked-in ${runtime.label} configuration.`
: recipe.summary;
label.textContent = useServer ? `${recipe.group_label || recipe.label} · Server` : recipe.label;
model.textContent = recipe.model;
- task.textContent = recipe.task;
+ task.textContent = (useServer && recipe.serving && recipe.serving.task) || recipe.task;
hardwareValue.textContent = runtimeSummary(activeRecipe);
- if (artifact) artifact.textContent = useServer ? "MP4 with audio" : recipe.expected_artifact || "Not yet documented for this recipe.";
+ if (artifact) {
+ artifact.textContent = useServer
+ ? (hasAudio ? "MP4 with audio" : "MP4 video")
+ : recipe.expected_artifact || "Not yet documented for this recipe.";
+ }
if (evidenceCell) {
evidenceCell.textContent = activeRecipe.evidence || "Source-backed";
evidenceCell.classList.toggle("cookbook-badge--verified", activeRecipe.evidence === "Verified");
@@ -602,10 +619,12 @@
? "This MLX server config has no recorded hardware run. Measurements from the Python recipe are not server memory requirements. Only text-to-video/audio is wired; reference inputs and fast modes are not exposed here."
: runtime.id === "spark"
? "This Spark server config has no recorded serving benchmark. Lazy module load reloads Qwen3-VL and the DiT between phases of each request. Compilation of the DiT is disabled."
- : "This server config has no recorded serving benchmark. Compilation is disabled, unlike the measured Python performance profile.",
+ : `This server config has no recorded serving benchmark.${profile.compile_enabled === false ? " Compilation is disabled, unlike the measured Python performance profile." : ""}`,
`${profile.sampling.width} × ${profile.sampling.height} · ${profile.sampling.num_frames} frames · ${profile.sampling.fps} fps. The server supplies these defaults; the client sends the model and prompt.`,
"Generation is serialized. Job metadata is held in memory and is lost when the server restarts.",
- ] : recipe.limitations || []), ...knobCaveats];
+ ] : recipe.limitations || []),
+ ...(useServer && recipe.serving && Array.isArray(recipe.serving.limitations) ? recipe.serving.limitations : []),
+ ...knobCaveats];
notes.replaceChildren();
notes.hidden = limitations.length === 0;
if (limitations.length) {
diff --git a/docs/assets/custom.css b/docs/assets/custom.css
index 03a6eee7a..f0a30131f 100644
--- a/docs/assets/custom.css
+++ b/docs/assets/custom.css
@@ -1515,7 +1515,8 @@ img {
.cookbook-serving[hidden],
.cookbook-command[hidden],
-[data-cookbook-python-note][hidden] {
+[data-cookbook-python-note][hidden],
+[data-cookbook-playground-only][hidden] {
display: none;
}
diff --git a/docs/cookbook/cosmos.md b/docs/cookbook/cosmos.md
index 2ee864e5b..fbb813fae 100644
--- a/docs/cookbook/cosmos.md
+++ b/docs/cookbook/cosmos.md
@@ -5,7 +5,7 @@ hide:
# Cosmos recipes
-