Buckets:
| # Run Wan2.1-VACE-1.3B inference (generate.py --task vace-1.3B) on this host. | |
| # | |
| # Upstream: https://github.com/Wan-Video/Wan2.1, pinned at | |
| # 9737cba9c1c3c4d04b33fcad41c111989865d315 | |
| # cloned into third_party/wan2.1 (see third_party/PINNED_COMMITS.txt). | |
| # | |
| # Environment: a dedicated conda env `wan-vace` (python 3.10), separate from | |
| # `fpgm`, created with: | |
| # conda create -y -n wan-vace python=3.10 | |
| # PY=~/miniconda3/envs/wan-vace/bin/python | |
| # $PY -m pip install torch==2.6.0 torchvision==0.21.0 \ | |
| # --index-url https://download.pytorch.org/whl/cu124 | |
| # $PY -m pip install "numpy<2" "opencv-python-headless>=4.9.0.80" \ | |
| # "diffusers>=0.31.0" "transformers>=4.49.0" "tokenizers>=0.20.3" \ | |
| # "accelerate>=1.1.1" tqdm imageio easydict ftfy imageio-ffmpeg \ | |
| # decord einops huggingface_hub dashscope | |
| # $PY -m pip install "https://github.com/Dao-AILab/flash-attention/releases/download/v2.7.3/flash_attn-2.7.3%2Bcu12torch2.6cxx11abiFALSE-cp310-cp310-linux_x86_64.whl" | |
| # `decord` and `einops` are both imported by wan/ but missing from Wan2.1's | |
| # own requirements.txt -- without them, `import wan` fails at | |
| # wan/modules/vae.py (einops) and any --src_video/--src_mask run fails inside | |
| # VaceVideoProcessor (decord). `dashscope` IS needed too, despite being a | |
| # prompt-extension-only dependency: generate.py imports it unconditionally | |
| # at module load time (`from wan.utils.prompt_extend import | |
| # DashScopePromptExpander, ...` -> `import dashscope`), so it must be | |
| # installed even though this launcher never calls --use_prompt_extend. | |
| # | |
| # flash_attn is NOT optional, despite appearances. wan/modules/attention.py | |
| # does define an `attention()` dispatcher that falls back cleanly to | |
| # torch.nn.functional.scaled_dot_product_attention when flash_attn is | |
| # missing -- but at this pinned commit, wan/modules/model.py imports and | |
| # calls the raw `flash_attention` function directly (see its `from | |
| # .attention import flash_attention` and every self/cross-attn call site), | |
| # never the `attention()` wrapper. That raw function unconditionally | |
| # `assert FLASH_ATTN_2_AVAILABLE`s, so every vace-1.3B run crashes ~90s in, | |
| # after both models are already loaded, if flash_attn isn't importable. The | |
| # SDPA fallback in attention.py is dead code from this entrypoint's | |
| # perspective. Confirmed by hitting the AssertionError in | |
| # wan/modules/attention.py:112 through generate.py -> wan/vace.py -> the | |
| # VaceWanModel's forward -> WanAttentionBlock.self_attn -> flash_attention, | |
| # then fixing it with the prebuilt wheel above (same | |
| # cu12/torch2.6/cxx11abiFALSE/cp310 build already proven working for the | |
| # `trellis2` env on this host -- installs from a wheel, no source build). | |
| # `gradio` from requirements.txt is skipped: it is only the web demo, which | |
| # this launcher doesn't use. | |
| # torch==2.6.0+cu124 mirrors the repo's other proven conda env (`trellis2`); | |
| # nothing here needs a newer CUDA toolkit since VACE-1.3B has no custom CUDA | |
| # extensions to compile. | |
| # | |
| # Chosen conda (not uv) because every other GPU-model env on this host | |
| # (fpgm, mujoco, sam3d-objects, trellis2) is conda; cosmos-transfer2.5's uv | |
| # .venv is the outlier, driven by its need for transformer_engine's own | |
| # CUDA-version-matched NVRTC, which Wan2.1 does not have. | |
| # | |
| # Talks to the rest of the repo over subprocess + files only (CLI flags in, | |
| # an mp4 out) -- the fpgm env's torch/numpy stack is load-bearing for | |
| # SAM3/TAPNext/pyrender and must never be touched by this script. | |
| # | |
| # Why the environment workarounds below exist: | |
| # | |
| # .nvshim FIRST on LD_LIBRARY_PATH -- this host's NVIDIA kernel module is | |
| # 570.172.08 but /usr/lib's libnvidia-ml.so.1 points at 580.173.02, so | |
| # nvmlInit_v2() fails ("Failed to initialize NVML: Driver/library version | |
| # mismatch", confirmed by running plain `nvidia-smi` without the shim). | |
| # scripts/nvidia_lib_shim.sh builds $REPO_ROOT/.nvshim from the matching | |
| # 570.172.08 libraries that are still on disk, no root needed. | |
| # | |
| # IMPORTANT correction to the Cosmos-Transfer2.5 launcher's version of this | |
| # comment: for THIS stack (torch==2.6.0+cu124, no transformer_engine), | |
| # skipping .nvshim does NOT reproduce the CUDACachingAllocator.cpp:983 | |
| # hard assert. Tested directly, including with | |
| # PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True set: torch just emits | |
| # `UserWarning: Can't initialize NVML` once at import and every CUDA op | |
| # after that -- conv2d, large allocations, and a full real 50-step | |
| # vace-1.3B run -- completed normally. The hard assert is apparently | |
| # specific to transformer_engine's own direct NVML usage in the Cosmos | |
| # stack, not a general torch/CUDA-allocator property. .nvshim is kept here | |
| # anyway because it's cheap and because `nvidia-smi` ITSELF hard-fails | |
| # (exit 18) without it, which breaks this script's own GPU auto-pick / | |
| # wait-for-headroom logic below -- that dependency, not a torch crash, is | |
| # the real reason it's required for this launcher. | |
| # | |
| # PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True -- this is a busy | |
| # 4xH200 host shared with other users; free memory is fragmented, and a | |
| # large contiguous reservation is exactly what fails without this. | |
| # | |
| # GPU auto-pick + wait-for-headroom -- measured VACE-1.3B peak VRAM at | |
| # 832x480/81 frames on this host: see the number this script prints and | |
| # the report that shipped with it. Typical free memory per card here is | |
| # only 4-25 GB out of 143 GB (heavily contended). Launching into | |
| # insufficient memory doesn't just fail this run, it raises memory | |
| # pressure on whatever else is resident on that GPU -- so this polls for | |
| # real headroom instead of gambling (same pattern as | |
| # scripts/wait_and_run_cosmos.sh). | |
| # | |
| # Usage: | |
| # scripts/run_wan_vace.sh <save_file.mp4> [extra generate.py args...] | |
| # | |
| # Example (control video + reference image + prompt, VACE's native | |
| # 480P bucket): | |
| # scripts/run_wan_vace.sh outputs/wan_vace/demo.mp4 \ | |
| # --src_video /abs/path/depth_control.mp4 \ | |
| # --src_ref_images /abs/path/plate.png \ | |
| # --prompt "..." | |
| # | |
| # generate.py's own --task/--size/--ckpt_dir defaults are set below and can | |
| # be overridden by passing them again in the extra-args tail (argparse takes | |
| # the last occurrence). | |
| # | |
| # Environment overrides: | |
| # WAN_GPU CUDA device index. Default: auto-pick the card with the | |
| # most free memory at launch time. | |
| # WAN_MIN_FREE_GB Minimum free VRAM required before launching (default 12). | |
| # WAN_WAIT_POLL_S Poll interval in seconds while waiting for headroom | |
| # (default 60). | |
| # WAN_WAIT_MAX_S Give up waiting after this many seconds (default 3600). | |
| # WAN_CKPT_DIR Wan2.1-VACE-1.3B checkpoint dir (default: the snapshot | |
| # already downloaded under ~/.cache/huggingface/hub). | |
| # WAN_TASK generate.py --task (default vace-1.3B). | |
| # WAN_SIZE generate.py --size (default 832*480; VACE-1.3B only | |
| # supports 480*832 / 832*480 -- see SUPPORTED_SIZES in | |
| # third_party/wan2.1/wan/configs/__init__.py). | |
| set -euo pipefail | |
| REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" | |
| WAN_DIR="$REPO_ROOT/third_party/wan2.1" | |
| ENV_PY="/home/quang/miniconda3/envs/wan-vace/bin/python" | |
| NVSHIM="$REPO_ROOT/.nvshim" | |
| WAN_CKPT_DIR="${WAN_CKPT_DIR:-/home/quang/.cache/huggingface/hub/models--Wan-AI--Wan2.1-VACE-1.3B/snapshots/574e6a744642ce3bee319afc31496b88bde8aac4}" | |
| WAN_TASK="${WAN_TASK:-vace-1.3B}" | |
| WAN_SIZE="${WAN_SIZE:-832*480}" | |
| WAN_MIN_FREE_GB="${WAN_MIN_FREE_GB:-12}" | |
| WAN_WAIT_POLL_S="${WAN_WAIT_POLL_S:-60}" | |
| WAN_WAIT_MAX_S="${WAN_WAIT_MAX_S:-3600}" | |
| if [[ $# -lt 1 ]]; then | |
| echo "usage: $0 <save_file.mp4> [extra generate.py args...]" >&2 | |
| exit 2 | |
| fi | |
| # Resolved to an absolute path *before* the cd below, exactly like | |
| # run_cosmos_transfer.sh -- a relative --save_file would otherwise land | |
| # under third_party/wan2.1 instead of where the caller meant. | |
| SAVE_FILE_ARG="$1"; shift | |
| mkdir -p "$(dirname "$SAVE_FILE_ARG")" | |
| SAVE_FILE="$(readlink -f "$SAVE_FILE_ARG")" | |
| # Any remaining path-shaped args (--src_video, --src_mask, --src_ref_images | |
| # take comma-separated paths) must also be resolved before the cd. We only | |
| # rewrite the args we recognize; anything else passes through untouched. | |
| EXTRA_ARGS=() | |
| prev="" | |
| for arg in "$@"; do | |
| case "$prev" in | |
| --src_video|--src_mask) | |
| EXTRA_ARGS+=("$(readlink -f "$arg")") | |
| prev="" | |
| continue | |
| ;; | |
| --src_ref_images) | |
| resolved="" | |
| IFS=',' read -ra _refs <<<"$arg" | |
| for r in "${_refs[@]}"; do | |
| resolved+="$(readlink -f "$r")," | |
| done | |
| EXTRA_ARGS+=("${resolved%,}") | |
| prev="" | |
| continue | |
| ;; | |
| esac | |
| EXTRA_ARGS+=("$arg") | |
| case "$arg" in | |
| --src_video|--src_mask|--src_ref_images) prev="$arg" ;; | |
| *) prev="" ;; | |
| esac | |
| done | |
| [[ -x "$ENV_PY" ]] || { echo "no wan-vace conda env at $ENV_PY -- see the header of this script for setup commands" >&2; exit 1; } | |
| [[ -d "$WAN_DIR/wan" ]] || { echo "Wan2.1 not cloned at $WAN_DIR -- git clone https://github.com/Wan-Video/Wan2.1 $WAN_DIR && (cd $WAN_DIR && git checkout 9737cba9c1c3c4d04b33fcad41c111989865d315)" >&2; exit 1; } | |
| [[ -f "$WAN_CKPT_DIR/config.json" ]] || { echo "checkpoint dir not found or incomplete: $WAN_CKPT_DIR" >&2; exit 1; } | |
| export LD_LIBRARY_PATH="$NVSHIM:${LD_LIBRARY_PATH:-}" | |
| export PYTORCH_CUDA_ALLOC_CONF="${PYTORCH_CUDA_ALLOC_CONF:-expandable_segments:True}" | |
| pick_gpu() { | |
| # Prints "index free_mib" for the card with the most free memory, or | |
| # "-1 0" if nvidia-smi (even shimmed) can't be read. | |
| LD_LIBRARY_PATH="$NVSHIM" nvidia-smi --query-gpu=index,memory.free \ | |
| --format=csv,noheader,nounits 2>/dev/null \ | |
| | awk -F', *' '{print $1, $2}' | sort -k2 -rn | head -1 | |
| } | |
| if [[ -n "${WAN_GPU:-}" ]]; then | |
| GPU="$WAN_GPU" | |
| echo "WAN_GPU set explicitly: using GPU $GPU (no headroom check)" | |
| else | |
| need_mib=$(( WAN_MIN_FREE_GB * 1024 )) | |
| waited=0 | |
| while true; do | |
| read -r idx free_mib < <(pick_gpu) | |
| if [[ -z "${idx:-}" || "$idx" == "-1" ]]; then | |
| echo "WARNING: could not read GPU memory via nvidia-smi (even with .nvshim)" >&2 | |
| GPU=0 | |
| break | |
| fi | |
| if (( free_mib >= need_mib )); then | |
| echo "$(date '+%H:%M:%S') GPU $idx has $((free_mib/1024)) GB free (need ${WAN_MIN_FREE_GB} GB) -- using it" | |
| GPU="$idx" | |
| break | |
| fi | |
| echo "$(date '+%H:%M:%S') best card is GPU $idx with $((free_mib/1024)) GB free, need ${WAN_MIN_FREE_GB} GB -- waiting ${WAN_WAIT_POLL_S}s (${waited}/${WAN_WAIT_MAX_S}s waited)" | |
| if (( waited >= WAN_WAIT_MAX_S )); then | |
| echo "ERROR: no GPU reached ${WAN_MIN_FREE_GB} GB free within ${WAN_WAIT_MAX_S}s -- giving up rather than launching into a card that will likely OOM" >&2 | |
| exit 1 | |
| fi | |
| sleep "$WAN_WAIT_POLL_S" | |
| waited=$(( waited + WAN_WAIT_POLL_S )) | |
| done | |
| fi | |
| export CUDA_VISIBLE_DEVICES="$GPU" | |
| cd "$WAN_DIR" | |
| echo "task: $WAN_TASK" | |
| echo "size: $WAN_SIZE" | |
| echo "ckpt_dir: $WAN_CKPT_DIR" | |
| echo "save to: $SAVE_FILE" | |
| echo "GPU: $CUDA_VISIBLE_DEVICES" | |
| exec "$ENV_PY" generate.py \ | |
| --task "$WAN_TASK" \ | |
| --size "$WAN_SIZE" \ | |
| --ckpt_dir "$WAN_CKPT_DIR" \ | |
| --save_file "$SAVE_FILE" \ | |
| "${EXTRA_ARGS[@]}" | |
Xet Storage Details
- Size:
- 11.4 kB
- Xet hash:
- e893155c2f4f1d2b10582ccbd050644ddafd37f271a4a19b7cf55ac15a62f4c3
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.