#!/usr/bin/env bash # Build the FastH3 Omni MLX benchmark model directory from PUBLIC Hugging Face # sources. Resumable, idempotent, self-verifying. Run `./prepare_model_dir.sh --help`. set -euo pipefail KIT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" HELPERS="$KIT_DIR/prepare_helpers.py" # ---- Pinned source revisions (HF `main` commit SHAs fetched 2026-10-20) ------ REV_OMNI="53e0d26a1691aa473b6262d8196a8feb2c297df0" # FastVideo/FastVideo-FastH3-Omni-8-Step-V1 REV_QAD="f01e6a23ac6066aa9eb622cf94b6259442e70ec2" # FastVideo/FastH3-Omni-NVFP4-QAD REV_DEC="bd0c248f2a0f57ca643b3df2ac1f375fa55101fb" # FastVideo/FastH3-NVFP4-VAE-Decoder REV_LVAE="4925ea978147b9de492d7a5fd4f9c6d1854ca1cc" # lightx2v/LightVAE REPO_OMNI="FastVideo/FastVideo-FastH3-Omni-8-Step-V1" REPO_QAD="FastVideo/FastH3-Omni-NVFP4-QAD" REPO_DEC="FastVideo/FastH3-NVFP4-VAE-Decoder" REPO_LVAE="lightx2v/LightVAE" ENCODER_SOURCE_FIELD="hf://FastVideo/FastH3-Omni-NVFP4-QAD/text_encoder" # Reference SHA-256 of our build (from the validated model dir). Downloaded/copied # files must match exactly (failure). dit/* and root/text_encoder/{model.safetensors, # mlx_h3_nvfp4_encoder.json} are converter outputs: a mismatch is only a WARNING # (MLX version / hardware may change the quantized bytes). read -r -d '' EXPECTED_SHA256 <<'EXPECTED_EOF' || true 59b99642b95ea21630e311198ddbfffbfe05aadba0c2f5d884cbdf4efcc90f44 LICENSE c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 LICENSE-Qwen3-VL 6f63c0101bdd0c7188ba540277ddd619952e2874437f9702bcdfa644b355dbc4 NOTICE fcc29480ea8247d75b5bdb86805c03044083af614244600d13467814acd4eb9b dit/int6/mlx_h3_dit.json 9ea28f8944686dbfecd684664ea5574b90994e3f232c4552b5c15dfc1faeb446 dit/int6/mlx_h3_dit.safetensors 804780f7133477067bd6bbfbc02dc8b3cf9feeb400f97c08f5b1d5f6cbab3840 root/audio_scheduler/scheduler_config.json 9a3c645ff892b376c6f5f4c8685964cd75474731af594ff058492a0000caabb6 root/audio_vae/config.json 52c59e67ba8de5477c81bfbced0327aabf500f1bfdeefd5ee754529241cb26cb root/audio_vae/diffusion_pytorch_model.safetensors 68577f23f0c866e329aa8a578b2455e4ca9a26a3ca1d15ddd6d0e99091007e18 root/fastvideo_inference.json 5c72a170d2a4a1a3bc5adad2e689ae28138a9700e5b8c96c0266331e86c0acce root/processor/chat_template.json 599bab54075088774b1733fde865d5bd747cbcc7a547c5bc12610e874e26f5e3 root/processor/merges.txt 27225450ac9c6529872ee1924fcb0962ff5634834f817040f444118116f4e516 root/processor/preprocessor_config.json a5d85b6dcc535e6b93115a9ef287e6132fdbf30270da6218194ba742261173c7 root/processor/tokenizer.json a07e942ac874baa13758de8d1fbdb186683cc03416b5589e1b6671c6b3057c68 root/processor/tokenizer_config.json 7768af27c1fafa9cc9011c1dc20067e03f8915e03b63504550e11d5066986d13 root/processor/video_preprocessor_config.json ca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910 root/processor/vocab.json 8fa6c3aa70dc9e691e1a6df899fd1b6f75f70481a27cee6e18a303817075c304 root/scheduler/scheduler_config.json 29d4a1e1db0e112612bf1e7c457a42ce9fe13fd0c69a0c3a64c1d291325bd9eb root/text_encoder/config.json 1d50c336c0e34645699039d78e7532fe2e7c21b2ed71f98d4252b682d53e075e root/text_encoder/mlx_h3_nvfp4_encoder.json f91405a223c95ed013267a6274dbee40d832a28cf89184ab86f84214b1882809 root/text_encoder/model.safetensors 599bab54075088774b1733fde865d5bd747cbcc7a547c5bc12610e874e26f5e3 root/tokenizer/merges.txt a5d85b6dcc535e6b93115a9ef287e6132fdbf30270da6218194ba742261173c7 root/tokenizer/tokenizer.json a07e942ac874baa13758de8d1fbdb186683cc03416b5589e1b6671c6b3057c68 root/tokenizer/tokenizer_config.json ca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910 root/tokenizer/vocab.json 658c1f6278262db3072d9a6f4832c7c49280f91c94a8455414525cd6648de677 root/transformer_ref/config.json ffa6d5e7a95dc57ae0494a8c0caf588e2c555252c159104d310ffcc450e1c914 root/vae/config.json c0d0f1beb88f0579d2a9fb249850419dbf3baa05d86ce37d3135c8c8873b4752 vae/fasth3-fast-8L-decoder-nvfp4.safetensors 84a92170f1cf6737f0509f8350cf642832afe3c0c0b1766bf4a0e6e820b62729 vae/fasth3-quality-36L-decoder-nvfp4.safetensors 4b0e5af102da19324c64df40abbd95008d8acb0b9cf76e4704d74c8ac20cefac vae/lightvae-lite-h3-encoder.safetensors EXPECTED_EOF # Approximate sizes in GB. SRC_GB=95; ENC_GB=16; OUT_FIXED_GB=7 # decoders+encoder(vae)+misc fmt_gb() { case "$1" in int6) echo 17;; int8) echo 20;; bf16) echo 38;; *) echo 0;; esac; } # ---- Options ----------------------------------------------------------------- FASTVIDEO=""; MODEL_DIR="$PWD/fasth3-omni-mlx-model"; SOURCES=""; FORMATS="int6" PYTHON="python3"; SKIP_DOWNLOAD=0; KEEP_SOURCES=0; LOCK_DIR=""; DRY_RUN=0 RESERVE_GB=5 # free space that must remain after each big stage usage() { cat < [options] --fastvideo DIR FastVideo checkout (branch aryan/omniref-mlx-speed). Required. --model-dir DIR output model directory (default: $MODEL_DIR) --sources DIR download directory (default: /../fasth3-sources); may be deleted afterwards --formats "int6" DiT formats, space/comma separated: int6 (default), int8, bf16 --python PATH python of the FastVideo venv (needs mlx, fastvideo); default python3 --skip-download use existing --sources (layout: omni-bf16/ nvfp4-qad/ vae-decoders/ lightvae/) --keep-sources do not delete downloaded sources after a successful verified build --reserve-gb N free GB that must remain after each big stage (default $RESERVE_GB) --lock-dir DIR opt-in: serialize memory-heavy stages via mkdir DIR/gpu.lock --dry-run print the hf/conversion commands without running them Safe to interrupt (Ctrl-C) and re-run: finished stages are verified and skipped. EOF } while [[ $# -gt 0 ]]; do case "$1" in --fastvideo) FASTVIDEO="$2"; shift 2;; --model-dir) MODEL_DIR="$2"; shift 2;; --sources) SOURCES="$2"; shift 2;; --formats) FORMATS="$2"; shift 2;; --python) PYTHON="$2"; shift 2;; --skip-download) SKIP_DOWNLOAD=1; shift;; --keep-sources) KEEP_SOURCES=1; shift;; --reserve-gb) RESERVE_GB="$2"; shift 2;; --lock-dir) LOCK_DIR="$2"; shift 2;; --dry-run) DRY_RUN=1; shift;; -h|--help) usage; exit 0;; *) echo "unknown option: $1" >&2; usage >&2; exit 2;; esac done [[ -n "$FASTVIDEO" ]] || { echo "error: --fastvideo is required" >&2; usage >&2; exit 2; } FORMATS="${FORMATS//,/ }" for f in $FORMATS; do [[ "$(fmt_gb "$f")" != 0 ]] || { echo "error: unknown format '$f' (int6|int8|bf16)" >&2; exit 2; }; done abspath() { case "$1" in /*) echo "${1%/}";; *) echo "$PWD/${1%/}";; esac; } MODEL_DIR="$(abspath "$MODEL_DIR")"; FASTVIDEO="$(abspath "$FASTVIDEO")" [[ -n "$SOURCES" ]] || SOURCES="$(dirname "$MODEL_DIR")/fasth3-sources" SOURCES="$(abspath "$SOURCES")" S_OMNI="$SOURCES/omni-bf16"; S_QAD="$SOURCES/nvfp4-qad"; S_DEC="$SOURCES/vae-decoders"; S_LVAE="$SOURCES/lightvae" log() { printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*"; } warn() { printf '[%s] WARNING: %s\n' "$(date +%H:%M:%S)" "$*" >&2; WARNINGS+=("$*"); } die() { printf '[%s] ERROR: %s\n' "$(date +%H:%M:%S)" "$*" >&2; exit 1; } WARNINGS=(); declare -a TIMINGS=() run() { if [[ $DRY_RUN -eq 1 ]]; then echo " (dry-run) $*"; else "$@"; fi; } helper() { "$PYTHON" -I "$HELPERS" "$@"; } timed() { # timed