Instructions to use FastVideo/FastH3-Omni-MLX with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use FastVideo/FastH3-Omni-MLX with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] hf download FastVideo/FastH3-Omni-MLX --local-dir FastH3-Omni-MLX
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
Download bench/prepare_model_dir.sh from FastVideo/FastH3-Omni-MLX: direct link, hf CLI and curl.
- Browser
- Download file 19.6 kB
-
https://huggingface.co/FastVideo/FastH3-Omni-MLX/resolve/main/bench/prepare_model_dir.sh
- Command line
-
hf download hf://FastVideo/FastH3-Omni-MLX/bench/prepare_model_dir.sh
-
curl -L -o prepare_model_dir.sh https://huggingface.co/FastVideo/FastH3-Omni-MLX/resolve/main/bench/prepare_model_dir.sh
19.6 kB
| # 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 <<EOF | |
| Usage: $0 --fastvideo <FastVideo checkout> [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: <model-dir>/../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 <label> <command...>; records elapsed time, preserves exit status | |
| local label="$1" t0=$SECONDS rc=0; shift | |
| "$@" || rc=$? | |
| TIMINGS+=("$label: $((SECONDS - t0))s"); return $rc | |
| } | |
| # ---- Lock (opt-in) ------------------------------------------------------------- | |
| LOCK_HELD="" | |
| lock_release() { if [[ -n "$LOCK_HELD" ]]; then rm -rf "$LOCK_HELD"; LOCK_HELD=""; fi; } | |
| lock_acquire() { | |
| [[ -n "$LOCK_DIR" && $DRY_RUN -eq 0 ]] || return 0 | |
| mkdir -p "$LOCK_DIR" | |
| until mkdir "$LOCK_DIR/gpu.lock" 2>/dev/null; do | |
| log "waiting for $LOCK_DIR/gpu.lock (owner pid $(cat "$LOCK_DIR/gpu.lock/owner" 2>/dev/null || echo ?)); retry in 30 s" | |
| sleep 30 | |
| done | |
| LOCK_HELD="$LOCK_DIR/gpu.lock"; echo $$ > "$LOCK_HELD/owner"; log "acquired $LOCK_HELD" | |
| } | |
| trap 'lock_release' EXIT | |
| trap 'lock_release; exit 130' INT TERM | |
| # ---- Preflight ------------------------------------------------------------------- | |
| free_gb() { df -Pk "$1" | awk 'NR==2 {printf "%d", $4/1048576}'; } | |
| existing_parent() { local p="$1"; while [[ ! -d "$p" ]]; do p="$(dirname "$p")"; done; echo "$p"; } | |
| need_space() { # need_space <dir> <GB needed> <stage label> | |
| local dir; dir="$(existing_parent "$1")"; local free; free="$(free_gb "$dir")" | |
| if (( free < $2 + RESERVE_GB )); then | |
| local msg="not enough disk for $3: need ~$2 GB + $RESERVE_GB GB reserve, only $free GB free on $dir" | |
| if [[ $DRY_RUN -eq 1 ]]; then warn "$msg"; else die "$msg (partial work is kept; free space and re-run)"; fi | |
| fi | |
| } | |
| preflight() { | |
| [[ "$(uname -s)" == Darwin && "$(uname -m)" == arm64 ]] || warn "not Apple Silicon macOS; MLX conversion will not run" | |
| [[ -f "$FASTVIDEO/scripts/checkpoint_conversion/convert_minimax_h3_mlx.py" ]] \ | |
| || die "$FASTVIDEO is not a FastVideo checkout with convert_minimax_h3_mlx.py (need branch aryan/omniref-mlx-speed)" | |
| command -v "$PYTHON" >/dev/null || die "python not found: $PYTHON" | |
| if [[ $DRY_RUN -eq 0 ]]; then | |
| PYTHONPATH="$FASTVIDEO" "$PYTHON" -I -c 'import sys; sys.path.insert(0, sys.argv[1]); import mlx.core, fastvideo.mlx_runtime.minimax_h3_conditioner' "$FASTVIDEO" \ | |
| || die "$PYTHON cannot import mlx and fastvideo; pass --python of the FastVideo venv" | |
| fi | |
| if [[ $SKIP_DOWNLOAD -eq 0 ]]; then | |
| command -v hf >/dev/null || die "'hf' CLI not found; run: $PYTHON -m pip install -U huggingface_hub" | |
| fi | |
| local mem_gb; mem_gb=$(( $(sysctl -n hw.memsize 2>/dev/null || echo 0) / 1073741824 )) | |
| log "total RAM: ${mem_gb} GB" | |
| (( mem_gb >= 24 )) || warn "RAM below 24 GB; int6/int8 conversion (peak ~21 GB) may fail" | |
| local f out_gb=$((OUT_FIXED_GB + ENC_GB)) fmts="" | |
| for f in $FORMATS; do | |
| if [[ $f == bf16 ]] && (( mem_gb < 64 )); then | |
| warn "skipping bf16: conversion needs ~40 GB free RAM and this Mac has ${mem_gb} GB (<64)"; continue | |
| fi | |
| out_gb=$((out_gb + $(fmt_gb "$f"))); fmts+="$f " | |
| done | |
| FORMATS="${fmts% }" | |
| local src_gb=0; [[ $SKIP_DOWNLOAD -eq 0 ]] && src_gb=$SRC_GB | |
| if [[ "$(df -Pk "$(existing_parent "$SOURCES")" | awk 'NR==2{print $1}')" == "$(df -Pk "$(existing_parent "$MODEL_DIR")" | awk 'NR==2{print $1}')" ]]; then | |
| log "sources and outputs share a disk: need ~$((src_gb + out_gb)) GB total" | |
| need_space "$MODEL_DIR" $((src_gb + out_gb)) "whole build" | |
| else | |
| need_space "$MODEL_DIR" "$out_gb" "outputs" | |
| [[ $src_gb -eq 0 ]] || need_space "$SOURCES" "$src_gb" "downloads" | |
| fi | |
| log "formats to build: ${FORMATS:-none}" | |
| } | |
| # ---- Stage: download --------------------------------------------------------------- | |
| sources_ok() { # all required source files present and well-formed (quiet) | |
| helper shards-check "$S_OMNI/transformer_ref" diffusion_pytorch_model >/dev/null 2>&1 || return 1 | |
| local f | |
| for f in transformer_ref/config.json scheduler/scheduler_config.json audio_scheduler/scheduler_config.json fastvideo_inference.json; do | |
| [[ -s "$S_OMNI/$f" ]] || return 1; done | |
| helper shards-check "$S_QAD/text_encoder" model >/dev/null 2>&1 || return 1 | |
| for f in text_encoder/config.json text_encoder/model.safetensors.index.json audio_vae/config.json audio_vae/diffusion_pytorch_model.safetensors \ | |
| processor/tokenizer.json tokenizer/tokenizer.json; do [[ -s "$S_QAD/$f" ]] || return 1; done | |
| helper st-check "$S_QAD/audio_vae/diffusion_pytorch_model.safetensors" "$S_DEC/quality-36L/decoder-nvfp4.safetensors" \ | |
| "$S_DEC/fast-8L/decoder-nvfp4.safetensors" "$S_LVAE/lightvae-lite-h3-encoder.safetensors" >/dev/null 2>&1 || return 1 | |
| } | |
| hf_get() { # hf_get <repo> <rev> <dir> <include...> | |
| local repo="$1" rev="$2" dir="$3"; shift 3 | |
| local inc=(); local p; for p in "$@"; do inc+=(--include "$p"); done | |
| mkdir -p "$dir" | |
| log "hf download $repo @ ${rev:0:10}" | |
| run hf download "$repo" --revision "$rev" "${inc[@]}" --local-dir "$dir" | |
| } | |
| stage_download() { | |
| if [[ $SKIP_DOWNLOAD -eq 1 ]]; then | |
| log "download: skipped (--skip-download); checking $SOURCES" | |
| [[ $DRY_RUN -eq 1 ]] || sources_ok || die "sources under $SOURCES are incomplete (need omni-bf16/ nvfp4-qad/ vae-decoders/ lightvae/)" | |
| return | |
| fi | |
| if [[ $DRY_RUN -eq 0 ]] && sources_ok; then log "download: all sources present and valid, skipping"; return; fi | |
| need_space "$SOURCES" "$SRC_GB" "downloads" | |
| hf_get "$REPO_OMNI" "$REV_OMNI" "$S_OMNI" 'transformer_ref/*' 'scheduler/*' 'audio_scheduler/*' fastvideo_inference.json | |
| hf_get "$REPO_QAD" "$REV_QAD" "$S_QAD" 'text_encoder/*' 'processor/*' 'tokenizer/*' 'audio_vae/*' | |
| hf_get "$REPO_DEC" "$REV_DEC" "$S_DEC" quality-36L/decoder-nvfp4.safetensors fast-8L/decoder-nvfp4.safetensors | |
| hf_get "$REPO_LVAE" "$REV_LVAE" "$S_LVAE" lightvae-lite-h3-encoder.safetensors | |
| [[ $DRY_RUN -eq 1 ]] || sources_ok || die "downloads finished but source validation failed; re-run to resume" | |
| } | |
| # ---- Stage: assemble root/, vae/, licenses --------------------------------------------- | |
| copy_file() { # atomic, skip when identical | |
| local src="$1" dst="$2" | |
| [[ -f "$src" ]] || die "missing source file: $src" | |
| if [[ -f "$dst" ]] && cmp -s "$src" "$dst"; then return 0; fi | |
| mkdir -p "$(dirname "$dst")" | |
| local tmp="$dst.partial.$$"; cp "$src" "$tmp" && mv -f "$tmp" "$dst" | |
| } | |
| copy_dir() { # copy_dir <src dir> <dst dir>: regular files only (skip AppleDouble / .cache) | |
| local src="$1" dst="$2" f | |
| [[ -d "$src" ]] || die "missing source dir: $src" | |
| while IFS= read -r f; do copy_file "$src/$f" "$dst/$f"; done \ | |
| < <(cd "$src" && find -L . -type f ! -name '._*' ! -name '.DS_Store' ! -path './.cache/*' ! -name '*.metadata' | sed 's#^\./##' | sort) | |
| } | |
| stage_assemble() { | |
| [[ $DRY_RUN -eq 0 ]] || { log "assemble: would copy root/ configs, vae/ weights, licenses and derive root/vae/config.json"; return 0; } | |
| local r="$MODEL_DIR/root" | |
| log "assembling root/, vae/, licenses" | |
| copy_file "$S_OMNI/fastvideo_inference.json" "$r/fastvideo_inference.json" | |
| copy_dir "$S_OMNI/scheduler" "$r/scheduler" | |
| copy_dir "$S_OMNI/audio_scheduler" "$r/audio_scheduler" | |
| copy_file "$S_OMNI/transformer_ref/config.json" "$r/transformer_ref/config.json" | |
| copy_dir "$S_QAD/audio_vae" "$r/audio_vae" | |
| copy_dir "$S_QAD/processor" "$r/processor" | |
| copy_dir "$S_QAD/tokenizer" "$r/tokenizer" | |
| copy_file "$S_DEC/quality-36L/decoder-nvfp4.safetensors" "$MODEL_DIR/vae/fasth3-quality-36L-decoder-nvfp4.safetensors" | |
| copy_file "$S_DEC/fast-8L/decoder-nvfp4.safetensors" "$MODEL_DIR/vae/fasth3-fast-8L-decoder-nvfp4.safetensors" | |
| copy_file "$S_LVAE/lightvae-lite-h3-encoder.safetensors" "$MODEL_DIR/vae/lightvae-lite-h3-encoder.safetensors" | |
| helper vae-config "$S_LVAE/lightvae-lite-h3-encoder.safetensors" "$r/vae/config.json" | |
| local l; for l in LICENSE LICENSE-Qwen3-VL NOTICE; do copy_file "$KIT_DIR/licenses/$l" "$MODEL_DIR/$l"; done | |
| } | |
| # ---- Stage: DiT conversion (sequential) ------------------------------------------------- | |
| convert_one() { # convert_one <fmt> | |
| local fmt="$1" out="$MODEL_DIR/dit/$1" stage="$MODEL_DIR/dit/.partial-$1" | |
| if [[ $DRY_RUN -eq 1 ]]; then | |
| echo " (dry-run) (cd $FASTVIDEO && PYTHONPATH=$FASTVIDEO $PYTHON scripts/checkpoint_conversion/convert_minimax_h3_mlx.py --model-root $S_OMNI/transformer_ref --out $stage --formats $fmt --include-vsa) # then mv to $out" | |
| return 0 | |
| fi | |
| if [[ -s "$out/mlx_h3_dit.json" ]] && helper st-check "$out/mlx_h3_dit.safetensors" >/dev/null 2>&1; then | |
| log "dit/$fmt: already converted and well-formed, skipping"; return 0 | |
| fi | |
| need_space "$MODEL_DIR" "$(fmt_gb "$fmt")" "dit/$fmt conversion" | |
| rm -rf "$out" "$stage" # incomplete or corrupt earlier attempt | |
| lock_acquire | |
| log "converting dit/$fmt (this takes a while)" | |
| (cd "$FASTVIDEO" && run env PYTHONPATH="$FASTVIDEO" "$PYTHON" scripts/checkpoint_conversion/convert_minimax_h3_mlx.py \ | |
| --model-root "$S_OMNI/transformer_ref" --out "$stage" --formats "$fmt" --include-vsa) | |
| if [[ $DRY_RUN -eq 0 ]]; then | |
| helper st-check "$stage/$fmt/mlx_h3_dit.safetensors" || die "dit/$fmt conversion produced an invalid file" | |
| mkdir -p "$MODEL_DIR/dit"; mv "$stage/$fmt" "$out"; rm -rf "$stage" | |
| fi | |
| lock_release | |
| } | |
| stage_dit() { local f; for f in $FORMATS; do timed "dit/$f" convert_one "$f"; done; } | |
| # ---- Stage: NVFP4 encoder cache ----------------------------------------------------------- | |
| stage_encoder() { | |
| local out="$MODEL_DIR/root/text_encoder" | |
| if [[ $DRY_RUN -eq 1 ]]; then | |
| echo " (dry-run) $PYTHON -c 'export_mlx_h3_nvfp4_encoder($S_QAD/text_encoder, $out)'; then set source_dir=$ENCODER_SOURCE_FIELD" | |
| return 0 | |
| fi | |
| if [[ -s "$out/model.safetensors" && -s "$out/mlx_h3_nvfp4_encoder.json" ]] \ | |
| && helper st-check "$out/model.safetensors" >/dev/null 2>&1; then | |
| log "text_encoder cache: present and well-formed, skipping export" | |
| else | |
| need_space "$MODEL_DIR" "$ENC_GB" "encoder export" | |
| rm -rf "$out" "$MODEL_DIR"/root/.text_encoder.partial-* # export refuses a non-empty target | |
| lock_acquire | |
| log "exporting NVFP4 encoder cache (format v2)" | |
| run env PYTHONPATH="$FASTVIDEO" "$PYTHON" -c ' | |
| import sys | |
| from fastvideo.mlx_runtime.minimax_h3_conditioner import export_mlx_h3_nvfp4_encoder | |
| export_mlx_h3_nvfp4_encoder(sys.argv[1], sys.argv[2])' "$S_QAD/text_encoder" "$out" | |
| lock_release | |
| fi | |
| # Make the manifest independent of where sources lived (same edit as our reference build). | |
| [[ $DRY_RUN -eq 1 ]] || helper rewrite-source "$out/mlx_h3_nvfp4_encoder.json" "$ENCODER_SOURCE_FIELD" | |
| } | |
| # ---- Stage: verify ---------------------------------------------------------------------------- | |
| stage_verify() { | |
| [[ $DRY_RUN -eq 0 ]] || { log "verify: skipped in dry-run"; return 0; } | |
| local exp; exp="$(mktemp)"; printf '%s\n' "$EXPECTED_SHA256" > "$exp" | |
| log "verifying SHA-256 against the reference build" | |
| local rc=0; helper verify "$MODEL_DIR" "$exp" $FORMATS || rc=$? | |
| rm -f "$exp"; return $rc | |
| } | |
| # ---- Main ------------------------------------------------------------------------------------------- | |
| log "model dir: $MODEL_DIR | sources: $SOURCES | python: $PYTHON | formats: $FORMATS" | |
| preflight | |
| timed download stage_download | |
| timed assemble stage_assemble | |
| stage_dit | |
| timed encoder stage_encoder | |
| VERIFY_RC=0; timed verify stage_verify || VERIFY_RC=$? | |
| if [[ $VERIFY_RC -eq 0 && $SKIP_DOWNLOAD -eq 0 && $KEEP_SOURCES -eq 0 && $DRY_RUN -eq 0 ]]; then | |
| log "removing downloaded sources $SOURCES (use --keep-sources to retain)"; rm -rf "$SOURCES" | |
| fi | |
| echo; echo "================ SUMMARY ================" | |
| printf 'timings:\n'; printf ' %s\n' ${TIMINGS[@]+"${TIMINGS[@]}"} | |
| echo "model dir: $MODEL_DIR" | |
| (( ${#WARNINGS[@]} == 0 )) || { echo "warnings:"; printf ' - %s\n' "${WARNINGS[@]}"; } | |
| if [[ $DRY_RUN -eq 1 ]]; then echo "dry-run: nothing was downloaded or converted"; exit 0; fi | |
| if [[ $VERIFY_RC -ne 0 ]]; then | |
| echo "RESULT: VERIFICATION FAILED. Downloaded-file hash mismatches mean a corrupt/changed download:" | |
| echo "delete the named files under $SOURCES and $MODEL_DIR and re-run."; exit 1 | |
| fi | |
| echo "RESULT: OK" | |
| FIRST_FMT="${FORMATS%% *}" | |
| cat <<EOF | |
| Next, from $KIT_DIR (use the FastVideo venv python): | |
| $PYTHON run_matrix.py --self-test --data-root ~/omniref-handoff | |
| $PYTHON run_matrix.py --smoke --model-dir "$MODEL_DIR" --fastvideo-root "$FASTVIDEO" --data-root ~/omniref-handoff | |
| $PYTHON run_matrix.py --model-dir "$MODEL_DIR" --fastvideo-root "$FASTVIDEO" --data-root ~/omniref-handoff --out ./results-mystudio | |
| (DiT format used by default: ${FIRST_FMT:-int6}; see run_matrix.py --help for selecting int8/bf16.) | |
| EOF | |