451 lines
17 KiB
Markdown
451 lines
17 KiB
Markdown
# MiniMax H3
|
||
|
||
> Joint video and audio generation with text, first/last keyframes, and
|
||
> mixed image/video/audio references
|
||
|
||
## Summary
|
||
|
||
- Vendor: MiniMaxAI
|
||
- Model: [`MiniMaxAI/MiniMax-H3`](https://huggingface.co/MiniMaxAI/MiniMax-H3)
|
||
- Tasks: T2VA, FL2VA, and Ref2VA
|
||
- Mode: OpenAI-compatible `/v1/videos` HTTP serving
|
||
- Maintainer: Community
|
||
|
||
MiniMax H3 is a CFG-distilled joint video/audio diffusion transformer. Its
|
||
checkpoint has two independently served partitions:
|
||
|
||
- `FL2VA`: text-to-video+audio (`t2va`) and first-frame-to-video+audio
|
||
(`fl2va`)
|
||
- `Ref2VA`: up to 9 images, 3 videos, and 3 audio references in supported
|
||
image/video/audio combinations (`ref2va`; audio-only is rejected)
|
||
|
||
The generated MP4 contains H.264 video and synchronized stereo audio.
|
||
|
||
## Prerequisites
|
||
|
||
The checkpoint requires Hugging Face access approval. Download it and point
|
||
`MODEL_ROOT` at the directory containing the `FL2VA` and `Ref2VA`
|
||
subdirectories:
|
||
|
||
```bash
|
||
hf auth login
|
||
export MODEL_ROOT=/path/to/MiniMax-H3
|
||
hf download MiniMaxAI/MiniMax-H3 --local-dir "${MODEL_ROOT}"
|
||
```
|
||
|
||
Install vLLM-Omni from the checkout containing MiniMax H3 support. On
|
||
Blackwell, install the optional FlashAttention-4 dependency:
|
||
|
||
```bash
|
||
uv venv
|
||
source .venv/bin/activate
|
||
uv pip install -e '.[fa4]'
|
||
```
|
||
|
||
`ffmpeg` and `ffprobe` must be available on `PATH`. They are used for
|
||
reference-video preparation and MP4 output.
|
||
|
||
## Start a server
|
||
|
||
One server loads one checkpoint partition. Set `MODEL` to `FL2VA` for T2VA
|
||
and FL2VA requests, or to `Ref2VA` for either Ref2VA request.
|
||
|
||
### Single GPU: accuracy and memory first
|
||
|
||
The single-GPU configuration uses model-level CPU offload.
|
||
This matches the accuracy-qualified reference path and prevents the Qwen3-VL
|
||
encoder and DiT from being resident on the GPU at the same time.
|
||
|
||
```bash
|
||
export MODEL="${MODEL_ROOT}/FL2VA"
|
||
export PORT=8091
|
||
|
||
CUDA_VISIBLE_DEVICES=0 \
|
||
VLLM_WORKER_MULTIPROC_METHOD=spawn \
|
||
VLLM_OMNI_VIDEO_SYNC_TIMEOUT=1800 \
|
||
vllm serve "${MODEL}" \
|
||
--omni \
|
||
--host 0.0.0.0 \
|
||
--port "${PORT}" \
|
||
--trust-remote-code \
|
||
--num-gpus 1 \
|
||
--enable-cpu-offload \
|
||
--diffusion-attention-backend FLASH_ATTN
|
||
```
|
||
|
||
Use a GPU with enough memory for the active H3 component and enough system RAM
|
||
for the offloaded components. CPU offload reduces GPU memory pressure but adds
|
||
PCIe/NVLink transfer latency.
|
||
|
||
### Four GPUs: measured best-practice throughput
|
||
|
||
The best validated four-GPU configuration on four NVIDIA B300 GPUs is:
|
||
|
||
- no CPU or layerwise offload;
|
||
- Ulysses sequence parallelism degree 4;
|
||
- native tiled VAE patch parallelism degree 4;
|
||
- regional `torch.compile` for the repeated DiT blocks;
|
||
- CuTe FlashAttention-4 through the `FLASH_ATTN` backend, with Ring and TP
|
||
left at 1.
|
||
|
||
```bash
|
||
export MODEL="${MODEL_ROOT}/FL2VA"
|
||
export PORT=8091
|
||
|
||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||
FLASHINFER_DISABLE_VERSION_CHECK=1 \
|
||
VLLM_WORKER_MULTIPROC_METHOD=spawn \
|
||
VLLM_OMNI_VIDEO_SYNC_TIMEOUT=1800 \
|
||
vllm serve "${MODEL}" \
|
||
--omni \
|
||
--host 0.0.0.0 \
|
||
--port "${PORT}" \
|
||
--trust-remote-code \
|
||
--num-gpus 4 \
|
||
--usp 4 \
|
||
--ring 1 \
|
||
--vae-patch-parallel-size 4 \
|
||
--vae-parallel-mode tile \
|
||
--vae-use-tiling \
|
||
--diffusion-attention-backend FLASH_ATTN
|
||
```
|
||
|
||
On Blackwell, `FLASH_ATTN` selects FA4. Confirm the server log contains
|
||
`Using CuTe FlashAttention-4 on Blackwell` before recording measurements.
|
||
|
||
Do not add `--enforce-eager` to this performance configuration. The first
|
||
request includes regional compilation; warm the server once before measuring
|
||
steady-state latency. H3 is CFG-distilled, so `--cfg-parallel-size` must remain
|
||
1. The H3 VAE supports its native `tile` mode, not
|
||
`spatial_shard_height` or `spatial_shard_width`.
|
||
|
||
### Text encoder tensor parallelism
|
||
|
||
The Qwen3-VL text encoder (~51.5 GB in BF16 for the retained 50 layers) is by
|
||
default fully resident on the DiT main rank. On multi-GPU no-offload runs that
|
||
rank becomes the peak-memory hotspot. Add `--text-encoder-tp-size N` to shard
|
||
the encoder across the first `N` DiT ranks (the encoder is implemented with
|
||
vLLM-style tensor-parallel layers and runs with distributed collectives over
|
||
its own encoder process group):
|
||
|
||
```bash
|
||
export MODEL="${MODEL_ROOT}/FL2VA"
|
||
export PORT=8091
|
||
|
||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||
FLASHINFER_DISABLE_VERSION_CHECK=1 \
|
||
VLLM_WORKER_MULTIPROC_METHOD=spawn \
|
||
VLLM_OMNI_VIDEO_SYNC_TIMEOUT=1800 \
|
||
vllm serve "${MODEL}" \
|
||
--omni \
|
||
--host 0.0.0.0 \
|
||
--port "${PORT}" \
|
||
--trust-remote-code \
|
||
--num-gpus 4 \
|
||
--usp 4 \
|
||
--ring 1 \
|
||
--text-encoder-tp-size 4 \
|
||
--vae-patch-parallel-size 4 \
|
||
--vae-parallel-mode tile \
|
||
--vae-use-tiling \
|
||
--diffusion-attention-backend FLASH_ATTN
|
||
```
|
||
|
||
`N` must divide the Qwen3-VL head counts (64 attention heads / 8 KV heads), so
|
||
valid values on a 4-GPU server are 1, 2, and 4 (1, 2, 4, 8 on 8 GPUs). The
|
||
encoder TP rank set is the first `N` DiT ranks; on 4 GPUs
|
||
`--text-encoder-tp-size 4` shards the encoder 4-way, dropping the DiT main
|
||
rank's no-offload peak by roughly `(N-1)/N` of the ~51.5 GB encoder while the
|
||
other ranks each gain `~51.5/N` GB. The encoder output remains identical to
|
||
the reference path within bf16 rounding: every encoder rank all-reduces the
|
||
row-parallel projections, so the full `[seq, 5120]` layer-50 hidden state is
|
||
replicated on every rank.
|
||
|
||
To serve Ref2VA, stop the FL2VA server and restart the same one- or four-GPU
|
||
command with:
|
||
|
||
```bash
|
||
export MODEL="${MODEL_ROOT}/Ref2VA"
|
||
```
|
||
|
||
### Online FP8 quantization
|
||
|
||
MiniMax H3 supports load-time FP8 quantization of the DiT. The checkpoint
|
||
remains BF16 on disk; vLLM-Omni quantizes eligible weights while loading and
|
||
uses dynamic activation scaling during inference. By default, attention and
|
||
MLP linears in the token refiner and main DiT blocks, the condition
|
||
projection, and all AdaLN projections use FP8. Patch, timestep, and final
|
||
projections remain FP32; the text encoder and VAEs are unchanged.
|
||
|
||
Add this option to an existing H3 server command:
|
||
|
||
```bash
|
||
--quantization fp8
|
||
```
|
||
|
||
Use `ignored_layers` to keep any otherwise eligible linear in BF16. H3
|
||
resolves the `transformer` component before constructing the DiT, so names do
|
||
not start with `transformer.`. Entries are exact runtime linear prefixes; a
|
||
parent name such as `blocks.0.attn` does not exclude its children.
|
||
|
||
Eligible names are the `attn.qkv_proj`, `attn.out_proj`, `mlp.fc1`, and
|
||
`mlp.fc2` children under `token_refiner.blocks.<0-1>` or `blocks.<0-49>`.
|
||
The other eligible names are `condition_proj`,
|
||
`blocks.<0-49>.adaln_proj.linear`, and `final_layer.adaln_proj.linear`.
|
||
For example, keep the first main block's attention projections in BF16 with:
|
||
|
||
```bash
|
||
--diffusion-quantization-config \
|
||
'{"transformer":{"method":"fp8","ignored_layers":["blocks.0.attn.qkv_proj","blocks.0.attn.out_proj"]}}'
|
||
```
|
||
|
||
The structured option replaces `--quantization fp8`. Online FP8 is currently
|
||
incompatible with H3 layerwise offload because the offload path produces a
|
||
weight stride rejected by the Cutlass FP8 kernel. Use resident FP8 with tensor
|
||
parallelism and VAE tiling instead.
|
||
|
||
## HTTP API examples
|
||
|
||
The following requests use the synchronous endpoint so the returned body can
|
||
be saved directly as an MP4. The asynchronous `POST /v1/videos` endpoint can
|
||
also be used when job polling is preferred.
|
||
|
||
All four tasks use 24 FPS, 50 sigma points, seed values from the validated
|
||
workloads, and the checkpoint-reference video/audio flow shifts of 12 and 3.
|
||
Decimal durations are passed through `extra_params`.
|
||
|
||
Set the endpoint once:
|
||
|
||
```bash
|
||
export API_URL="http://127.0.0.1:${PORT}/v1/videos/sync"
|
||
```
|
||
|
||
### 1. T2VA: text to video and audio
|
||
|
||
Run this request against the `FL2VA` partition:
|
||
|
||
```bash
|
||
curl -sS -X POST "${API_URL}" \
|
||
-F 'prompt=In a snowy blue-purple forest, Ori carefully walks past a sleeping giant; footsteps crunch in the snow while the creature breathes and softly snorts.' \
|
||
-F 'width=1344' \
|
||
-F 'height=768' \
|
||
-F 'aspect_ratio=16:9' \
|
||
-F 'fps=24' \
|
||
-F 'num_inference_steps=50' \
|
||
-F 'flow_shift=12' \
|
||
-F 'seed=1101' \
|
||
-F 'extra_params={"task":"t2va","duration":8.7,"audio_flow_shift":3.0}' \
|
||
-o t2va.mp4
|
||
```
|
||
|
||
### 2. FL2VA: first frame to video and audio
|
||
|
||
Run this request against the `FL2VA` partition. When width and height are
|
||
omitted, H3 preserves the first-frame aspect ratio and uses a 768-pixel short
|
||
edge.
|
||
|
||
```bash
|
||
export FIRST_FRAME=/path/to/fl2va_first_frame.png
|
||
|
||
curl -sS -X POST "${API_URL}" \
|
||
-F 'prompt=A man stands beside a yellow car at night. The car drives away; he follows it with his eyes and begins singing sadly, with synchronized voice and city ambience.' \
|
||
-F 'fps=24' \
|
||
-F 'num_inference_steps=50' \
|
||
-F 'flow_shift=12' \
|
||
-F 'seed=2101' \
|
||
-F 'extra_params={"task":"fl2va","duration":8.7,"audio_flow_shift":3.0}' \
|
||
-F "input_reference=@${FIRST_FRAME};type=image/png" \
|
||
-o fl2va.mp4
|
||
```
|
||
|
||
Use the same `FL2VA` partition for the official tail-keyframe forms. A single
|
||
image with `frame_indices=[-1]` conditions the last frame; two ordered images
|
||
with `frame_indices=[0,-1]` condition the first and last frames:
|
||
|
||
```bash
|
||
export LAST_FRAME=/path/to/fl2va_last_frame.png
|
||
export FIRST_FRAME=/path/to/fl2va_first_frame.png
|
||
|
||
curl -sS -X POST "${API_URL}" \
|
||
-F 'prompt=The subject moves naturally from the first image to the last image.' \
|
||
-F 'num_inference_steps=50' \
|
||
-F 'flow_shift=12' \
|
||
-F 'seed=2102' \
|
||
-F 'extra_params={"task":"fl2va","duration":8.7,"frame_indices":[0,-1],"audio_flow_shift":3.0}' \
|
||
-F "input_references=@${FIRST_FRAME};type=image/png" \
|
||
-F "input_references=@${LAST_FRAME};type=image/png" \
|
||
-o fl2va_first_last.mp4
|
||
```
|
||
|
||
### 3. Ref2VA: image-only, image/audio, or mixed references
|
||
|
||
Run these requests against the `Ref2VA` partition. Image-only Ref2VA omits
|
||
`audio_reference`; adding one or more audio references is optional. The typed
|
||
fields accept one object or an ordered JSON list. `audio_reference` accepts an
|
||
HTTP(S) URL or a `data:` URL.
|
||
|
||
```bash
|
||
python -m http.server 8092 \
|
||
--bind 127.0.0.1 \
|
||
--directory /path/to/reference_assets
|
||
```
|
||
|
||
Then submit an image-only request from another terminal:
|
||
|
||
```bash
|
||
export REF_IMAGE=/path/to/reference_assets/ref2va_image.png
|
||
|
||
curl -sS -X POST "${API_URL}" \
|
||
-F 'prompt=A white cat sits on a beige couch and slowly looks toward the camera.' \
|
||
-F 'aspect_ratio=adaptive' \
|
||
-F 'short_edge=768' \
|
||
-F 'num_inference_steps=50' \
|
||
-F 'flow_shift=12' \
|
||
-F 'seed=3100' \
|
||
-F 'extra_params={"task":"ref2va","duration":8.0,"audio_flow_shift":3.0}' \
|
||
-F "input_reference=@${REF_IMAGE};type=image/png" \
|
||
-o ref2va_image_only.mp4
|
||
```
|
||
|
||
An image-plus-audio request is:
|
||
|
||
```bash
|
||
export REF_IMAGE=/path/to/reference_assets/ref2va_image.png
|
||
export AUDIO_URL=http://127.0.0.1:8092/ref2va_audio.mp3
|
||
|
||
curl -sS -X POST "${API_URL}" \
|
||
-F 'prompt=A white cat with black mustache and eyebrow markings sits on a beige couch, lip-syncing precisely to the complete reference audio before shifting from confusion to deadpan speechlessness.' \
|
||
-F 'width=1344' \
|
||
-F 'height=768' \
|
||
-F 'fps=24' \
|
||
-F 'num_inference_steps=50' \
|
||
-F 'flow_shift=12' \
|
||
-F 'seed=3101' \
|
||
-F 'extra_params={"task":"ref2va","duration":15.0,"audio_flow_shift":3.0}' \
|
||
-F "input_reference=@${REF_IMAGE};type=image/png" \
|
||
-F "audio_reference={\"audio_url\":\"${AUDIO_URL}\"}" \
|
||
-o ref2va_image_audio.mp4
|
||
```
|
||
|
||
The requested duration should cover the complete audio. If `duration` is
|
||
shorter, the reference soundtrack is truncated to the generated clip.
|
||
|
||
### 4. Ref2VA: video, separate audio, and mixed references
|
||
|
||
Run this request against the `Ref2VA` partition. Repeat the
|
||
`input_references` multipart field once per source video. H3 consumes the
|
||
videos in form order and preserves their original soundtracks during
|
||
conditioning.
|
||
|
||
```bash
|
||
export SUBJECT_VIDEO=/path/to/green_screen_subject.mp4
|
||
export BACKGROUND_VIDEO=/path/to/fairytale_background.mov
|
||
|
||
curl -sS -X POST "${API_URL}" \
|
||
-F 'prompt=Remove the green screen background of Video 1 and replace it with the fairytale environment from Video 2. Match the background motion to the character actions and relight the character to fit the scene.' \
|
||
-F 'width=1344' \
|
||
-F 'height=768' \
|
||
-F 'fps=24' \
|
||
-F 'num_inference_steps=50' \
|
||
-F 'flow_shift=12' \
|
||
-F 'seed=3101' \
|
||
-F 'extra_params={"task":"ref2va","duration":15.0,"audio_flow_shift":3.0}' \
|
||
-F "input_references=@${SUBJECT_VIDEO};type=video/mp4" \
|
||
-F "input_references=@${BACKGROUND_VIDEO};type=video/quicktime" \
|
||
-o ref2va_video_video.mp4
|
||
```
|
||
|
||
The server stores uploaded references only for the lifetime of the request and
|
||
deletes temporary files after generation. A video may use its embedded
|
||
soundtrack, a separate `audio_reference`, or both. To send a mixed multipart
|
||
request, repeat `input_references` for each image, video, or audio file; the
|
||
server classifies them by MIME type and preserves the per-type order.
|
||
|
||
Reference videos must be MP4/MOV with H.264/H.265 video, optional AAC/MP3
|
||
audio, 2–15 seconds each, and at most 15 seconds combined. They may still be
|
||
longer than the generated clip. Use `start_time_seconds` to select a
|
||
synchronized segment; for multiple typed video references, pass one value per
|
||
video in `extra_params.start_time_seconds`.
|
||
|
||
Reference images accept JPG/JPEG, PNG, WEBP, HEIC, or HEIF up to 30 MiB. Standalone
|
||
audio references accept WAV or MP3 up to 15 MiB, with 2–15 seconds per file and
|
||
at most 15 seconds combined.
|
||
|
||
## Official input matrix and limits
|
||
|
||
| Task | Supported references | Limits |
|
||
|------|----------------------|--------|
|
||
| T2VA | text only | prompt must be non-empty |
|
||
| FL2VA | first image, last image, or ordered first+last images | at most 2 images; `frame_indices` is `[0]`, `[-1]`, or `[0,-1]` |
|
||
| Ref2VA | image-only, image+image, image+video, video+audio, and mixed image/video/audio | images ≤9, videos ≤3, audios ≤3, total references ≤12; audio requires a visual reference |
|
||
|
||
The H3 output contract is 4–15 seconds at 24 FPS, stereo 32 kHz audio, and a
|
||
32-pixel canvas multiple. T2VA requires one named output ratio from `21:9`,
|
||
`16:9`, `4:3`, `1:1`, `3:4`, or `9:16`. FL2VA always follows the first input
|
||
image's ratio and ignores a generic `aspect_ratio` override. Ref2VA defaults to
|
||
`16:9`; `adaptive` and SGLang's `auto` spelling are accepted aliases for that
|
||
default. `short_edge` controls the 768-pixel canvas and must be `768`.
|
||
`num_outputs_per_prompt` accepts 1–10 and derives each output seed as
|
||
`seed + output_index`. The asynchronous endpoint returns all
|
||
outputs; the synchronous raw-MP4 endpoint returns the first output when more
|
||
than one is requested.
|
||
|
||
## Key parameters
|
||
|
||
| Parameter | Recommended value | Notes |
|
||
|-----------|-------------------|-------|
|
||
| `task` | `t2va`, `fl2va`, or `ref2va` | Passed in `extra_params`; must match the served partition |
|
||
| `duration` | Workload-specific | Decimal seconds in `extra_params`; converted to H3-compatible frame count |
|
||
| `fps` | `24` | H3 output FPS is fixed |
|
||
| `num_inference_steps` | `50` | Matches the reference accuracy workloads |
|
||
| `flow_shift` | `12` | Video sigma shift |
|
||
| `audio_flow_shift` | `3` | Audio sigma shift, passed in `extra_params` |
|
||
| `seed` | Task-specific | Use a fixed value for reproducibility |
|
||
| `aspect_ratio` | Task-specific | T2VA requires a named ratio; FL2VA follows the input image; Ref2VA defaults to `16:9` |
|
||
| `short_edge` | `768` | H3 shape policy requires exactly 768 when `width`/`height` are omitted |
|
||
| `num_outputs_per_prompt` | `1` | 1–10; async API returns every output |
|
||
| `start_time_seconds` | `0` | Reference-video segment start; use a list in `extra_params` for multiple videos |
|
||
| `width`, `height` | Multiples of 32 | Output aspect ratio must be between 1:4 and 4:1 |
|
||
|
||
## Validated four-GPU evidence
|
||
|
||
The four-GPU recommendation was measured on four NVIDIA B300 GPUs with one
|
||
excluded warmup followed by three requests.
|
||
|
||
| Workload | Configuration | Observed result |
|
||
|----------|---------------|-----------------|
|
||
| FL2VA, 209 frames, 1248x768 | no offload, U4, VPP4 tile, regional compile | 86.964 s mean HTTP client latency |
|
||
| Two-video Ref2VA, 362 frames, 1344x768 | no offload, U4, VPP4 tile, regional compile | 784.394 s accounted model-stage mean |
|
||
|
||
These measurements describe the validated shapes rather than a general
|
||
throughput guarantee. Multi-video Ref2VA is much slower because the two
|
||
reference videos expand both the Qwen3-VL vision sequence and the packed DiT
|
||
attention sequence.
|
||
|
||
## Validated FP8 evidence
|
||
|
||
With eager DiT/text-encoder TP2 and VAE tiling, the 384x672, 107-frame,
|
||
10-step quality case measured LPIPS 0.1156 (limit 0.20), PSNR 23.6316 dB,
|
||
audio spectral cosine 0.9589 (minimum 0.80), and audio RMS ratio 0.9342. The
|
||
resident per-GPU peak was 68.52 GiB for BF16 and 53.51 GiB for FP8, a 22%
|
||
reduction.
|
||
|
||
## Known limitations
|
||
|
||
- Each server process loads only one checkpoint partition.
|
||
- H3 currently executes one generation request per diffusion batch.
|
||
- The first regional-compile request is a warmup and should not be included in
|
||
steady-state performance measurements.
|
||
- Online FP8 is not compatible with layerwise offload.
|
||
- Image+audio Ref2VA accepts exactly one image and one audio reference.
|
||
- Video Ref2VA accepts one or more video files, but not an additional standalone
|
||
audio reference.
|
||
- VAE patch parallelism requires size 1 or the full DiT group size and supports
|
||
the H3 native `tile` mode only.
|
||
|
||
## Additional resources
|
||
|
||
- [Supported models](../../docs/models/supported_models.md)
|
||
- [Video API](../../docs/serving/videos_api.md)
|
||
- [Diffusion parallelism](../../docs/user_guide/diffusion/parallelism/overview.md)
|