FastVideo/FastVideo-FastH3-4-step-Preview-v1-VSA-DataFree
is a data-free DMD2 distillation of MiniMaxAI/MiniMax-H3, the
33B dual-modality DiT that emits a video and its synchronized soundtrack from one denoising pass. The student
keeps the teacher's architecture exactly and only replaces transformer/, so it is a drop-in for the same
diffusers modular pipeline — but it needs four DiT forwards instead of thirty, and it was distilled with
Video Sparse Attention on.
Everything runs unquantized at bfloat16.
MiniMaxH3Scheduler.set_timesteps(n) builds linspace(1, 0, n), applies the shift
σ' = s·σ / (1 + (s−1)·σ), and then drops the trailing zero when it forms the timesteps — so n sigma grid
points drive n − 1 model evaluations. The distilled ladder is t = 999, 749, 500, 250 → 0: five points, four
forwards.
This Space therefore fixes num_inference_steps = 5, matching the checkpoint's own
fastvideo_inference.json (num_inference_steps: 5, transformer_forwards: 4,
dmd_denoising_steps: [999, 749, 500, 250], guidance_scale: 1.0). It is not a knob. There is no CFG and no
negative prompt — the teacher is guidance-distilled and the student inherits that.
This checkpoint is the VSA variant. Its fastvideo_inference.json pins
attention_backend: VIDEO_SPARSE_ATTN_H3, vsa_sparsity: 0.9, vsa_tile_size: 64, and the transformer ships 50
trained attn.to_gate_compress tensors (~3.6 GiB) that only the sparse path consumes. FastVideo publishes a
separate …-Dense-DataFree checkpoint for people who want dense — running this one dense is running it
off-distribution.
The published kernel is vsa_kernel: sm100a, which is GB200-only, and the ZeroGPU pool is sm120 (RTX PRO 6000
Blackwell). FastVideo's other officially supported route is --vsa-kernel triton, which is pure Triton and
architecture-agnostic — so this Space vendors those two files verbatim from
FastVideo (Apache-2.0) into vsa_kernel/ and ports the H3 backend on top
of them:
| File | What it is |
|---|---|
vsa_kernel/block_sparse_attn_triton.py | FastVideo's Triton block-sparse attention, verbatim except that the autotune sweep is collapsed to the single config it lands on for Blackwell (re-enable it with H3_VSA_AUTOTUNE=1). |
vsa_kernel/index.py | FastVideo's map_to_index / topk_index_to_map, verbatim. |
vsa_h3.py | The port: MiniMaxH3VSAAttnProcessor, a diffusers attention processor reproducing MiniMaxH3VSABackend. |
vsa_h3.py follows FastVideo's video_sparse_attn_h3.py step for step: 64-token (4, 4, 4) tiles over the
post-patchify video grid, segment-pure prefix tiles, per-head fp32 pooled tile scores,
topk = max(1, min(⌈(1 − sparsity)·n_video_tiles⌉, n_video_tiles)), prefix keys exempt (always selected) and prefix
queries always dense, plus the compression branch softmax(scores) @ pool(v) broadcast back over each tile row and
scaled by the trained gate with no activation. The tile geometry is derived per-forward from the pipeline's own
token_tags / position_ids, so the [text | cond | audio | video] packing stays authoritative.
diffusers 0.40.0 has no to_gate_compress, so vsa_h3.add_gate_compress_modules() patches
MiniMaxH3TransformerBlock.__init__ before the pipeline loads; otherwise the 50 gate tensors load as
"unexpected keys" and are silently dropped. Blocks whose gate is all-zero have it removed again after load, exactly
as FastVideo does.
A hidden /selftest API endpoint runs the ported kernel at sparsity 0 against F.scaled_dot_product_attention on
a real packed layout, so a wrong tile order or transpose is caught without spending a generation.
MiniMax-H3 is ~196 GiB in bfloat16 and a ZeroGPU Space is evicted at 150 GB of storage, so no single
unquantized Space can hold it. Cutting MiniMaxH3Blocks at its text_encoder step splits it in two, and both
halves fit:
| Space | Subfolders | Download |
|---|---|---|
multimodalart/qwen3vl-conditioner | text_encoder/ + tokenizer/ + processor/ | 66.7 GB → 62.15 GiB bf16 |
| this one | transformer/ + vae/ + audio_vae/ | ~85 GB → 75.7 GiB resident |
The conditioner is a public Space and is unchanged by the distillation — the student's
modular_model_index.json points its text_encoder at the same Qwen3-VL weights — so this Space reuses it as-is
and calls it over the gradio API for every request.
h3_split_blocks.py subclasses the pipeline's blocks with the text_encoder step removed. Dropping the step drops
the components it declares, so load_components resolves only transformer / vae / audio_vae / the two
schedulers, and prompt_embeds + text_token_tags become ordinary required pipeline inputs. The wire format is
those two tensors — (1, num_text_tokens, 5120) bfloat16 and (num_text_tokens,) int64 — in one safetensors file
with the resolved height / width / num_frames in its metadata header.
The preview distills the T2VA path only, so this Space exposes no keyframe / reference inputs — the student's
transformer_ref tower is not packaged with the checkpoint. Image conditioning is what
multimodalart/minimax-h3 (the undistilled 30-step
teacher) is for.
MiniMax-H3 is trained on a structured multimodal caption, not a bare sentence:
integrated_multimodal_description: <shots, camera, subjects, action, lighting>
overall_soundscape: <diegetic sound>
non_diegetic_music: <score>
Expand prompt (on by default) sends a short prompt through the conditioner's Qwen3-VL prompt rewriter, which writes that structure for you and returns it. Turn it off when you have already written a full-format prompt — the examples that carry MiniMax's own official prompts do exactly that.
The two long examples are MiniMax's own published prompts, taken verbatim from the base model's repo (Apache-2.0 code / docs):
scripts/readme/reproducible-768p-t2va-request.shdocs/VIDEO_PROMPT_WRITING_GUIDE_base_en.mdFixed by the checkpoint: 24 fps, a 768 pixel short edge at the training canvas, num_frames snapped up to the next
17·n + 5. The distillation's operating point is 1344×768 × 124 frames (≈5 s) — that is the default, and it is
exactly the layout VSA was tuned on (grid = 37×24×42, 37 296 video rows, 672 tiles, 66 selected). The duration
slider reaches 8 s and the canvas dropdown offers smaller/faster grids, both outside the distilled operating point,
so quality degrades gracefully rather than being guaranteed. The 8 s ceiling is a VRAM limit, not a model limit:
75.7 GiB of weights sit resident on a 95.0 GiB card and the sparse working set grows with sequence length.
The checkpoint itself is a preview.
On this Space, over gradio_client. Startup — the ~85 GB download plus the load, with no placement and therefore no
pack — is 78 s.
| Canvas × frames | Packed rows | Video tiles kept | Denoise + decode | Per forward | Peak allocated |
|---|---|---|---|---|---|
| 544×544 × 56 (2.3 s) | 4 913 | 10/100 | 6 s | 1.4 s | 81.05 GiB |
| 1344×768 × 124 (5.2 s) | 37 296 | 66/660 | 59–72 s | 14.7–18.0 s | 83.70 GiB |
| 1344×768 × 192 (8.0 s) | 57 456 | 102/1020 | 87 s | 21.9 s | 88.00 GiB |
Sparse attention makes the cost linear in the packed rows rather than quadratic, so get_duration drops the
quadratic term a dense path needs. The spread on the 5 s point is how fast a slice of the pool the request lands on;
the fit is taken at the slow end (1.95e-3 · rows) so a slow slice is not aborted mid-video. That books 98 s at the
5 s default and 143 s at the 8 s maximum.
A cold worker pays a one-time 68 s: 75.7 GiB across PCIe (~11 s) plus the Triton JIT of the vendored kernels (~57 s). The same 544×544 request measures 74 s cold against 6 s warm. That is booked only on the first request a process serves rather than padded onto every request.
At 88.00 GiB on a 95.0 GiB card the 8 s ceiling leaves ~7 GiB — which is why the slider stops at 8 s and not 10 s.
The vendored kernels are checked against F.scaled_dot_product_attention on the live GPU by the hidden /selftest
endpoint: at sparsity = 0 the ported path reproduces dense attention to 4.1e-03 relative / 0.999996 cosine,
which is bf16 rounding.
H3_PLACEMENT=lazy: the weights move onto the card on the first GPU call and stay there. spaces' startup
torch.pack() would write a second on-disk copy of every resident CUDA tensor, and 85 + 75 GB exceeds the 150 GB
quota, so packing is not an option here. The one-time .to("cuda") plus the Triton JIT of the block-sparse kernels
lands inside the first request of a cold worker; after that there is no offloading in the request path at all.
| Variable | Default | Meaning |
|---|---|---|
H3_MODEL_REPO | FastVideo/FastVideo-FastH3-4-step-Preview-v1-VSA-DataFree | The distilled diffusers-layout checkpoint. Public. |
H3_CONDITIONER | multimodalart/qwen3vl-conditioner | The public Space this one asks for embeddings. |
H3_ATTENTION | vsa | The ported VSA-H3 backend. Any other value is passed to set_attention_backend as a dense escape hatch (e.g. _native_cudnn). |
H3_VSA_SPARSITY | 0.9 | The checkpoint's trained sparsity. |
H3_VSA_AUTOTUNE | unset | Set to 1 to restore FastVideo's full Triton autotune sweep instead of the pinned Blackwell config. |
H3_PLACEMENT | lazy | lazy moves all weights onto the card on the first GPU call; offload hands placement to ComponentsManager.enable_auto_cpu_offload. |
H3_GPU_SIZE | xlarge | ZeroGPU allocation size. large (48 GB) does not fit 75 GiB of weights. |
None. Every weight this Space downloads is public, and the conditioner is a public Space called on the requesting user's own ZeroGPU token.
The weights are under the MiniMax H3 Community License, inherited from the base model — it carries territory and
acceptable-use restrictions. Read
the base model's license before using outputs. The
vendored kernels in vsa_kernel/ are Apache-2.0, from
hao-ai-lab/FastVideo.
4 commits
FastVideo/FastVideo-FastH3-4-step-Preview-v1-VSA-DataFree
is a data-free DMD2 distillation of MiniMaxAI/MiniMax-H3, the
33B dual-modality DiT that emits a video and its synchronized soundtrack from one denoising pass. The student
keeps the teacher's architecture exactly and only replaces transformer/, so it is a drop-in for the same
diffusers modular pipeline — but it needs four DiT forwards instead of thirty, and it was distilled with
Video Sparse Attention on.
Everything runs unquantized at bfloat16.
MiniMaxH3Scheduler.set_timesteps(n) builds linspace(1, 0, n), applies the shift
σ' = s·σ / (1 + (s−1)·σ), and then drops the trailing zero when it forms the timesteps — so n sigma grid
points drive n − 1 model evaluations. The distilled ladder is t = 999, 749, 500, 250 → 0: five points, four
forwards.
This Space therefore fixes num_inference_steps = 5, matching the checkpoint's own
fastvideo_inference.json (num_inference_steps: 5, transformer_forwards: 4,
dmd_denoising_steps: [999, 749, 500, 250], guidance_scale: 1.0). It is not a knob. There is no CFG and no
negative prompt — the teacher is guidance-distilled and the student inherits that.
This checkpoint is the VSA variant. Its fastvideo_inference.json pins
attention_backend: VIDEO_SPARSE_ATTN_H3, vsa_sparsity: 0.9, vsa_tile_size: 64, and the transformer ships 50
trained attn.to_gate_compress tensors (~3.6 GiB) that only the sparse path consumes. FastVideo publishes a
separate …-Dense-DataFree checkpoint for people who want dense — running this one dense is running it
off-distribution.
The published kernel is vsa_kernel: sm100a, which is GB200-only, and the ZeroGPU pool is sm120 (RTX PRO 6000
Blackwell). FastVideo's other officially supported route is --vsa-kernel triton, which is pure Triton and
architecture-agnostic — so this Space vendors those two files verbatim from
FastVideo (Apache-2.0) into vsa_kernel/ and ports the H3 backend on top
of them:
| File | What it is |
|---|---|
vsa_kernel/block_sparse_attn_triton.py | FastVideo's Triton block-sparse attention, verbatim except that the autotune sweep is collapsed to the single config it lands on for Blackwell (re-enable it with H3_VSA_AUTOTUNE=1). |
vsa_kernel/index.py | FastVideo's map_to_index / topk_index_to_map, verbatim. |
vsa_h3.py | The port: MiniMaxH3VSAAttnProcessor, a diffusers attention processor reproducing MiniMaxH3VSABackend. |
vsa_h3.py follows FastVideo's video_sparse_attn_h3.py step for step: 64-token (4, 4, 4) tiles over the
post-patchify video grid, segment-pure prefix tiles, per-head fp32 pooled tile scores,
topk = max(1, min(⌈(1 − sparsity)·n_video_tiles⌉, n_video_tiles)), prefix keys exempt (always selected) and prefix
queries always dense, plus the compression branch softmax(scores) @ pool(v) broadcast back over each tile row and
scaled by the trained gate with no activation. The tile geometry is derived per-forward from the pipeline's own
token_tags / position_ids, so the [text | cond | audio | video] packing stays authoritative.
diffusers 0.40.0 has no to_gate_compress, so vsa_h3.add_gate_compress_modules() patches
MiniMaxH3TransformerBlock.__init__ before the pipeline loads; otherwise the 50 gate tensors load as
"unexpected keys" and are silently dropped. Blocks whose gate is all-zero have it removed again after load, exactly
as FastVideo does.
A hidden /selftest API endpoint runs the ported kernel at sparsity 0 against F.scaled_dot_product_attention on
a real packed layout, so a wrong tile order or transpose is caught without spending a generation.
MiniMax-H3 is ~196 GiB in bfloat16 and a ZeroGPU Space is evicted at 150 GB of storage, so no single
unquantized Space can hold it. Cutting MiniMaxH3Blocks at its text_encoder step splits it in two, and both
halves fit:
| Space | Subfolders | Download |
|---|---|---|
multimodalart/qwen3vl-conditioner | text_encoder/ + tokenizer/ + processor/ | 66.7 GB → 62.15 GiB bf16 |
| this one | transformer/ + vae/ + audio_vae/ | ~85 GB → 75.7 GiB resident |
The conditioner is a public Space and is unchanged by the distillation — the student's
modular_model_index.json points its text_encoder at the same Qwen3-VL weights — so this Space reuses it as-is
and calls it over the gradio API for every request.
h3_split_blocks.py subclasses the pipeline's blocks with the text_encoder step removed. Dropping the step drops
the components it declares, so load_components resolves only transformer / vae / audio_vae / the two
schedulers, and prompt_embeds + text_token_tags become ordinary required pipeline inputs. The wire format is
those two tensors — (1, num_text_tokens, 5120) bfloat16 and (num_text_tokens,) int64 — in one safetensors file
with the resolved height / width / num_frames in its metadata header.
The preview distills the T2VA path only, so this Space exposes no keyframe / reference inputs — the student's
transformer_ref tower is not packaged with the checkpoint. Image conditioning is what
multimodalart/minimax-h3 (the undistilled 30-step
teacher) is for.
MiniMax-H3 is trained on a structured multimodal caption, not a bare sentence:
integrated_multimodal_description: <shots, camera, subjects, action, lighting>
overall_soundscape: <diegetic sound>
non_diegetic_music: <score>
Expand prompt (on by default) sends a short prompt through the conditioner's Qwen3-VL prompt rewriter, which writes that structure for you and returns it. Turn it off when you have already written a full-format prompt — the examples that carry MiniMax's own official prompts do exactly that.
The two long examples are MiniMax's own published prompts, taken verbatim from the base model's repo (Apache-2.0 code / docs):
scripts/readme/reproducible-768p-t2va-request.shdocs/VIDEO_PROMPT_WRITING_GUIDE_base_en.mdFixed by the checkpoint: 24 fps, a 768 pixel short edge at the training canvas, num_frames snapped up to the next
17·n + 5. The distillation's operating point is 1344×768 × 124 frames (≈5 s) — that is the default, and it is
exactly the layout VSA was tuned on (grid = 37×24×42, 37 296 video rows, 672 tiles, 66 selected). The duration
slider reaches 8 s and the canvas dropdown offers smaller/faster grids, both outside the distilled operating point,
so quality degrades gracefully rather than being guaranteed. The 8 s ceiling is a VRAM limit, not a model limit:
75.7 GiB of weights sit resident on a 95.0 GiB card and the sparse working set grows with sequence length.
The checkpoint itself is a preview.
On this Space, over gradio_client. Startup — the ~85 GB download plus the load, with no placement and therefore no
pack — is 78 s.
| Canvas × frames | Packed rows | Video tiles kept | Denoise + decode | Per forward | Peak allocated |
|---|---|---|---|---|---|
| 544×544 × 56 (2.3 s) | 4 913 | 10/100 | 6 s | 1.4 s | 81.05 GiB |
| 1344×768 × 124 (5.2 s) | 37 296 | 66/660 | 59–72 s | 14.7–18.0 s | 83.70 GiB |
| 1344×768 × 192 (8.0 s) | 57 456 | 102/1020 | 87 s | 21.9 s | 88.00 GiB |
Sparse attention makes the cost linear in the packed rows rather than quadratic, so get_duration drops the
quadratic term a dense path needs. The spread on the 5 s point is how fast a slice of the pool the request lands on;
the fit is taken at the slow end (1.95e-3 · rows) so a slow slice is not aborted mid-video. That books 98 s at the
5 s default and 143 s at the 8 s maximum.
A cold worker pays a one-time 68 s: 75.7 GiB across PCIe (~11 s) plus the Triton JIT of the vendored kernels (~57 s). The same 544×544 request measures 74 s cold against 6 s warm. That is booked only on the first request a process serves rather than padded onto every request.
At 88.00 GiB on a 95.0 GiB card the 8 s ceiling leaves ~7 GiB — which is why the slider stops at 8 s and not 10 s.
The vendored kernels are checked against F.scaled_dot_product_attention on the live GPU by the hidden /selftest
endpoint: at sparsity = 0 the ported path reproduces dense attention to 4.1e-03 relative / 0.999996 cosine,
which is bf16 rounding.
H3_PLACEMENT=lazy: the weights move onto the card on the first GPU call and stay there. spaces' startup
torch.pack() would write a second on-disk copy of every resident CUDA tensor, and 85 + 75 GB exceeds the 150 GB
quota, so packing is not an option here. The one-time .to("cuda") plus the Triton JIT of the block-sparse kernels
lands inside the first request of a cold worker; after that there is no offloading in the request path at all.
| Variable | Default | Meaning |
|---|---|---|
H3_MODEL_REPO | FastVideo/FastVideo-FastH3-4-step-Preview-v1-VSA-DataFree | The distilled diffusers-layout checkpoint. Public. |
H3_CONDITIONER | multimodalart/qwen3vl-conditioner | The public Space this one asks for embeddings. |
H3_ATTENTION | vsa | The ported VSA-H3 backend. Any other value is passed to set_attention_backend as a dense escape hatch (e.g. _native_cudnn). |
H3_VSA_SPARSITY | 0.9 | The checkpoint's trained sparsity. |
H3_VSA_AUTOTUNE | unset | Set to 1 to restore FastVideo's full Triton autotune sweep instead of the pinned Blackwell config. |
H3_PLACEMENT | lazy | lazy moves all weights onto the card on the first GPU call; offload hands placement to ComponentsManager.enable_auto_cpu_offload. |
H3_GPU_SIZE | xlarge | ZeroGPU allocation size. large (48 GB) does not fit 75 GiB of weights. |
None. Every weight this Space downloads is public, and the conditioner is a public Space called on the requesting user's own ZeroGPU token.
The weights are under the MiniMax H3 Community License, inherited from the base model — it carries territory and
acceptable-use restrictions. Read
the base model's license before using outputs. The
vendored kernels in vsa_kernel/ are Apache-2.0, from
hao-ai-lab/FastVideo.
4 commits