Skip to content

Commit ac48bb3

Browse files
aryan5vAryan Kumarcoderabbitai[bot]CodeRabbit
authored
[feat] Add MiniMax H3 MLX T2VA inference (#1770)
Co-authored-by: Aryan Kumar <aryank@Aryans-Mac-Studio.local> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
1 parent 3987b9d commit ac48bb3

19 files changed

Lines changed: 5333 additions & 9 deletions

docs/getting_started/installation/mps.md

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# MPS (Apple Silicon)
22

3-
Install FastVideo on Apple Silicon and run FastMetal-QAD.
3+
Install FastVideo on Apple Silicon and run FastMetal-QAD or FastH3 Preview.
44

5-
Apple Silicon uses the MLX runtime and the FastMetal-QAD INT8 checkpoints.
5+
Apple Silicon uses the MLX runtime. FastMetal-QAD ships ready-to-run MLX
6+
checkpoints; FastH3 Preview currently requires a local MLX DiT conversion.
67
See the [FastMetal-QAD blog](https://haoailab.com/blogs/fastmetal/) and the
78
[FastMetal collection](https://huggingface.co/collections/FastVideo/fastmetal).
89

@@ -132,6 +133,57 @@ CUDA FastWan-QAD (`FastVideo/FastWan-QAD-1.3B`, `FastVideo/FastWan-QAD-FP8-1.3B`
132133

133134
`basic_mps.py` is a generic PyTorch MPS demo. For local video on Mac, use the FastMetal commands above.
134135

136+
## Run FastH3 Preview
137+
138+
FastH3 Preview uses the existing MLX runtime for text-to-video-with-audio
139+
(T2VA). The runtime streams the Qwen3-VL text conditioner, loads one
140+
heavyweight component at a time, denoises synchronized video and audio
141+
latents with a converted INT8, INT6, or INT4 DiT, and decodes both modalities
142+
with native MLX VAEs.
143+
144+
Download the FastH3 snapshot, then convert one or more DiT formats:
145+
146+
```bash
147+
hf download FastVideo/FastVideo-Minimax-FastH3-Preview-v0.2 \
148+
--local-dir ./FastH3-Preview-v0.2
149+
150+
python scripts/checkpoint_conversion/convert_minimax_h3_mlx.py \
151+
--model-root ./FastH3-Preview-v0.2/transformer \
152+
--out ./FastH3-MLX \
153+
--formats "int6"
154+
```
155+
156+
Run the baseline path:
157+
158+
```bash
159+
python examples/inference/basic/mlx_fasth3.py \
160+
--model-root ./FastH3-Preview-v0.2 \
161+
--mlx-checkpoint ./FastH3-MLX/int6 \
162+
--prompt "(S1) A presenter says <d>[English] Fast H3 is amazing.</d>" \
163+
--height 480 --width 832 --num-frames 124 --seed 2026 \
164+
--output-path ./outputs/fasth3_int6.mp4
165+
```
166+
167+
Add `--fast` for temporal fast mode. It denoises a shorter video sequence,
168+
uses MLX RIFE to restore the requested frame count, and keeps the audio
169+
sequence at full duration:
170+
171+
```bash
172+
python examples/inference/basic/mlx_fasth3.py \
173+
--model-root ./FastH3-Preview-v0.2 \
174+
--mlx-checkpoint ./FastH3-MLX/int6 \
175+
--prompt "(S1) A presenter says <d>[English] Fast H3 is even faster.</d>" \
176+
--height 720 --width 1280 --num-frames 124 --seed 2027 \
177+
--fast \
178+
--output-path ./outputs/fasth3_int6_fast_720p.mp4
179+
```
180+
181+
!!! note "Current MLX scope"
182+
This source runtime supports T2VA and temporal `--fast`. FL2VA, Ref2VA,
183+
spatial fast mode, two-pass refinement, VSA, and `VideoGenerator`
184+
registry dispatch are not wired yet. The checkpoint uses the MiniMax H3
185+
Community License; review the model card before use or redistribution.
186+
135187
## Development Environment Setup
136188

137189
If you're planning to contribute to FastVideo please see the following page:
@@ -141,6 +193,9 @@ If you're planning to contribute to FastVideo please see the following page:
141193

142194
- **1.3B / 5B:** 16 GB unified memory and up (M1 and later)
143195
- **14B:** 36 GB unified memory and up
196+
- **FastH3 Preview:** validated on an M4 Max with 36 GB unified memory; use one
197+
converted DiT format at a time and leave substantial free disk space for the
198+
source snapshot plus the converted checkpoint
144199
- Fanless 13-inch MacBook Air can run 1.3B and 5B at the same resolutions
145200

146201
## Troubleshooting

docs/inference/support_matrix.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,11 @@ optimizations: absence means **untested**, not incompatible.
185185
| MLX FastMetal T2V 1.3B | [`FastVideo/FastMetal-1.3B-QAD`](https://huggingface.co/FastVideo/FastMetal-1.3B-QAD) | 480x832, 81 frames, 3-step DMD, INT8 DiT + TAEHV decode | Apple M4 Max, 16 GB+ unified memory | Released |
186186
| MLX FastMetal TI2V 5B | [`FastVideo/FastMetal-5B-QAD`](https://huggingface.co/FastVideo/FastMetal-5B-QAD) | 480p / 720p, 81 frames, 3-step DMD, INT8 DiT + TAEHV decode | Apple M4 Max, 16 GB+ unified memory | Released |
187187
| MLX FastMetal T2V 14B | [`FastVideo/FastMetal-14B-QAD`](https://huggingface.co/FastVideo/FastMetal-14B-QAD) | 480p / 720p, 81 frames, 3-step DMD, INT8 DiT + TAEHV decode | Apple M4 Max, 36 GB+ unified memory | Released |
188+
| MLX FastH3 Preview T2VA | [`FastVideo/FastVideo-Minimax-FastH3-Preview-v0.2`](https://huggingface.co/FastVideo/FastVideo-Minimax-FastH3-Preview-v0.2) + locally converted DiT | 480p / 720p, 124 frames, 4-step DMD2, INT8/INT6/INT4 DiT, native video + audio VAE; optional temporal RIFE fast mode | Apple M4 Max, 36 GB unified memory | Source runtime; T2VA only |
188189

189-
Apple Silicon uses FastMetal-QAD. CUDA FastWan-QAD (`FastVideo/FastWan-QAD-1.3B`,
190+
Apple Silicon uses the native MLX runtime. FastMetal-QAD is the packaged Wan
191+
release, while FastH3 Preview currently uses a source checkout and local DiT
192+
conversion. CUDA FastWan-QAD (`FastVideo/FastWan-QAD-1.3B`,
190193
`FastVideo/FastWan-QAD-FP8-1.3B`) is the NVIDIA release. See the
191194
[Apple Silicon guide](../getting_started/installation/mps.md) and the
192195
[FastMetal-QAD blog](https://haoailab.com/blogs/fastmetal/).

examples/inference/basic/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,23 @@ with
3939

4040
`examples/inference/basic/basic_mps.py` is the older PyTorch MPS demo.
4141

42+
FastH3 Preview T2VA also runs through the native MLX runtime. Convert the DiT
43+
to INT8, INT6, or INT4 first, then run:
44+
45+
```bash
46+
python examples/inference/basic/mlx_fasth3.py \
47+
--model-root ./FastH3-Preview-v0.2 \
48+
--mlx-checkpoint ./FastH3-MLX/int6 \
49+
--prompt "(S1) A presenter says <d>[English] Fast H3 is amazing.</d>" \
50+
--height 480 --width 832 --num-frames 124 \
51+
--output-path ./outputs/fasth3_int6.mp4
52+
```
53+
54+
Pass `--fast` for temporal RIFE fast mode. This MLX entrypoint currently
55+
supports T2VA only; FL2VA, Ref2VA, spatial fast mode, and two-pass refinement
56+
remain follow-up work. The complete setup and conversion commands are in the
57+
[Apple Silicon guide](https://hao-ai-lab.github.io/FastVideo/getting_started/installation/mps/).
58+
4259
For an example running DMD+VSA inference:
4360
```
4461
python examples/inference/basic/basic_dmd.py
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
"""End-to-end MiniMax-H3 (FastH3) generation with the Apple Silicon MLX runtime.
3+
4+
Accepts a text prompt and produces an MP4 with H.264 video at 24 fps and
5+
stereo AAC audio at 32 kHz. One heavyweight model phase is resident at a time.
6+
7+
python examples/inference/basic/mlx_fasth3.py \
8+
--model-root ~/models/FastH3-Preview-v0.2 \
9+
--mlx-checkpoint ~/models/FastH3-MLX/int8 \
10+
--prompt '(S1) A red panda says <d>[English] Fast H3 is amazing.</d>' \
11+
--height 480 --width 832 --num-frames 124 --seed 2026 \
12+
--output-path ~/fasth3_outputs/int8.mp4
13+
14+
Conditioning uses the streamed Qwen3-VL text encoder on first use and caches
15+
the resulting embeddings under --prompt-cache-dir for instant reuse.
16+
17+
``--fast`` is temporal fast mode. It keeps full-duration audio while
18+
denoising fewer video frames, then uses MLX RIFE 4.25 to reconstruct the
19+
requested frame count. A 1280x720 request runs on H3's 1280x736 grid and is
20+
center-cropped after decode.
21+
22+
This entrypoint currently supports text-to-video-with-audio only. It does not
23+
yet wire FL2VA, Ref2VA, spatial fast mode, or two-pass refinement.
24+
"""
25+
26+
from __future__ import annotations
27+
28+
import argparse
29+
import json
30+
from pathlib import Path
31+
32+
33+
def parse_args() -> argparse.Namespace:
34+
parser = argparse.ArgumentParser(description=__doc__,
35+
formatter_class=argparse.RawDescriptionHelpFormatter)
36+
parser.add_argument("--model-root", type=Path, default=Path.home() / "models/FastH3-Preview-v0.2",
37+
help="H3 snapshot root (vae/, audio_vae/, text_encoder/, tokenizer/)")
38+
parser.add_argument("--mlx-checkpoint", type=Path, required=True,
39+
help="pre-quantized MLX DiT directory (int8/int6/int4 mlx_h3_dit format)")
40+
parser.add_argument(
41+
"--prompt",
42+
required=True,
43+
help="H3 text prompt; use (S1) and <d>[Language] words</d> for explicit dialogue",
44+
)
45+
parser.add_argument("--output-path", type=Path, required=True)
46+
parser.add_argument("--height", type=int, default=480)
47+
parser.add_argument("--width", type=int, default=832)
48+
parser.add_argument("--num-frames", type=int, default=124)
49+
parser.add_argument("--seed", type=int, default=0)
50+
parser.add_argument("--steps", type=int, default=4, help="denoise steps (trained ladder = 4)")
51+
parser.add_argument(
52+
"--fast",
53+
action=argparse.BooleanOptionalAction,
54+
default=False,
55+
help="denoise fewer video frames, then use MLX RIFE to restore the target frame count; audio stays full length",
56+
)
57+
parser.add_argument("--fast-factor", type=int, default=2,
58+
help="temporal reduction target for --fast (default: 2)")
59+
parser.add_argument("--fast-sharpen", type=float, default=0.6,
60+
help="unsharp strength after RIFE interpolation (0 disables)")
61+
parser.add_argument("--rife-weights-dir", type=Path, default=None,
62+
help="optional local mlx-community/RIFE-4.25 snapshot")
63+
parser.add_argument("--vae-dtype", choices=("fp32", "fp16", "bf16"), default="fp32")
64+
parser.add_argument("--prompt-cache-dir", type=Path, default=None,
65+
help="directory for reusable prompt embedding caches")
66+
parser.add_argument(
67+
"--tiled-video-decode",
68+
action=argparse.BooleanOptionalAction,
69+
default=True,
70+
help="decode with the reference 256px overlapping VAE tiles (disable only for diagnostics)",
71+
)
72+
return parser.parse_args()
73+
74+
75+
def main() -> None:
76+
args = parse_args()
77+
from fastvideo.mlx_runtime.minimax_h3_pipeline import MiniMaxH3MLXPipeline
78+
79+
pipeline = MiniMaxH3MLXPipeline(
80+
model_root=args.model_root,
81+
mlx_dit_checkpoint=args.mlx_checkpoint,
82+
vae_dtype=args.vae_dtype,
83+
prompt_cache_dir=args.prompt_cache_dir,
84+
)
85+
result = pipeline.generate(
86+
args.prompt,
87+
output_path=args.output_path,
88+
height=args.height,
89+
width=args.width,
90+
num_frames=args.num_frames,
91+
seed=args.seed,
92+
num_steps=args.steps,
93+
tiled_video_decode=args.tiled_video_decode,
94+
fast=args.fast,
95+
fast_factor=args.fast_factor,
96+
fast_sharpen=args.fast_sharpen,
97+
rife_weights_dir=args.rife_weights_dir,
98+
)
99+
print(json.dumps({
100+
"video_path": result.video_path,
101+
"timings_s": {k: round(v, 2) for k, v in result.timings.items()},
102+
"peak_memory_gib": {k: round(v, 2) for k, v in result.peak_memory_gib.items()},
103+
"audio_samples": int(result.waveform.shape[-1]),
104+
}, indent=2))
105+
106+
107+
if __name__ == "__main__":
108+
main()

fastvideo/mlx_runtime/__init__.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
# SPDX-License-Identifier: Apache-2.0
2-
"""Experimental Apple MLX runtime helpers.
3-
4-
This package is intentionally small for now. It exists to grow the Apple-native
5-
FastWan path in measurable steps: shape planning, primitive benchmarks, then
6-
Wan block parity, then full DiT/runtime support.
7-
"""
2+
"""Apple Silicon MLX inference helpers."""
83

94
from fastvideo.mlx_runtime.fastwan import (
105
FastWanShape,
@@ -71,6 +66,12 @@
7166
enhance_result_as_metrics,
7267
load_or_enhance_prompt,
7368
)
69+
from fastvideo.mlx_runtime.minimax_h3_pipeline import (
70+
FastTemporalPlan,
71+
GenerationResult,
72+
MiniMaxH3MLXPipeline,
73+
plan_fast_temporal,
74+
)
7475

7576
__all__ = [
7677
"AppliedMemoryLimits",
@@ -81,10 +82,13 @@
8182
"DEFAULT_REFINE_SIGMA",
8283
"EnhanceResult",
8384
"FastSpatialPlan",
85+
"FastTemporalPlan",
8486
"FastWanShape",
87+
"GenerationResult",
8588
"MLXQuantizationSpec",
8689
"MLXWanDiT",
8790
"MLXWanTransformerBlock",
91+
"MiniMaxH3MLXPipeline",
8892
"RefinePlan",
8993
"TwoPassResult",
9094
"UnsupportedMLXCheckpointError",
@@ -110,6 +114,7 @@
110114
"PIXEL_UPSAMPLE_MODES",
111115
"default_refine_timesteps",
112116
"plan_fast_spatial",
117+
"plan_fast_temporal",
113118
"plan_refine_resolutions",
114119
"prepare_refine_latents",
115120
"quantization_support_error",

fastvideo/mlx_runtime/fastwan.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ def from_name(cls, name: str | None) -> MLXQuantizationSpec | None:
6464
return None
6565
if name == "int8":
6666
return cls(mode="affine", bits=8, group_size=64)
67+
if name == "int6":
68+
return cls(mode="affine", bits=6, group_size=64)
6769
if name == "int4":
6870
return cls(mode="affine", bits=4, group_size=64)
6971
if name == "mxfp8":

0 commit comments

Comments
 (0)