#!/usr/bin/env bash # convert-swift15-w4dn8.sh — RUNS ON DANKSERVER. The final recipe convert of # the Swift 1.5 checkpoint: gate_up experts through GPTQ against the 10M-token # calibration Grams, down projections block-fp8, protected experts bf16, # trunk int8, PLE fixed-scale fp8 (scripts/recipes/ # qwen4exp-swift15-w4dn8-i8-hc8m.recipe). # # The convert engine is 1.0.13 — the SAME pin that will serve the result, so # the .rad crosses no version boundary. Only the Gram files cross versions # (1.2.0 wrote them; the RADGRAM2 magic, gram.r%d.%s.bin naming and the # calib_gram/calib_resolve/calib_stem symbols are byte-identical between the # two libquant.so). rad-convert runs on the host backend (no GPU visible in # the container) — production can stay up through this script. # # Prereqs (campaign order): calibration complete (calib-push printed TAP_DONE), # the protected-expert cast lines rewritten into the recipe by # derive-protected-experts.py, and the 335 GiB bf16 container deleted — # the peak-disk arithmetic needs that space (checkpoint 335.3 + output ~132 # GiB on the volume; --reuse is NOT used, see the recipe header). set -euo pipefail PIN_1013=sha256:4c3f484564bb2872784e46b0a59cbd71becc9c1e746e301a3596ffae253c322e IMAGE="stilldeadcode/radiance@$PIN_1013" CKPT=/home/dank/models/ukisai/Swift1.5-Qwen3.8-Flash-Next RECIPE=/home/dank/serverAppConfigs-swift15/scripts/recipes/qwen4exp-swift15-w4dn8-i8-hc8m.recipe CALIB=/home/dank/corpora/calib OUT=/home/dank/models/rad/qwen4exp-swift15-w4dn8-i8-hc8m.rad LOG=/home/dank/logs/swift15_convert.log # gate_up ~37.6 + down ~18.8 + PLE 47.7 + trunk/embeddings/head ~10 GiB (all-w4 expert table; # the plugin's one-encoding law dropped the block-fp8 down tier -- see the recipe header). MIN_MIB=${FRANK_SWIFT15_RAD_MIN_MIB:-108000} # Gate 1: inputs. A missing Gram directory converts UNCALIBRATED with a # per-tensor stderr note, which is silently worse than failing here. [ -d "$CKPT" ] || { echo "no checkpoint at $CKPT" >&2; exit 1; } [ -f "$RECIPE" ] || { echo "no recipe at $RECIPE" >&2; exit 1; } GRAMS=$(find "$CALIB" -maxdepth 1 -name 'gram.r*.bin' 2>/dev/null | wc -l) [ "$GRAMS" -ge 48 ] || { echo "calib dir $CALIB holds $GRAMS gram files (<48)" >&2; exit 1; } if [ -e "$OUT" ]; then echo "$OUT already exists; move it to trash first" >&2; exit 1; fi if [ -e /home/dank/models/rad/qwen4exp-swift15-bf16.rad ]; then echo "the bf16 container is still on disk; delete it first (peak disk)" >&2; exit 1 fi # Gate 2: disk. Output ~132 GiB plus convert staging, 20 GiB of slack. FREE_MIB=$(df --output=avail -m /home/dank/models | tail -1 | tr -d ' ') [ "$FREE_MIB" -ge $((MIN_MIB + 20480)) ] || { echo "only ${FREE_MIB} MiB free on /home/dank/models" >&2; exit 1; } # The recipe names $CALIB; rad-convert expands it from the environment # (MOE-W4.md:275). The recipe's cast section must name THIS checkpoint's # protected experts — derive-protected-experts.py rewrote it. echo "convert: $CKPT -> $OUT (gptq calib=$CALIB, $(date -Is))" | tee "$LOG" podman run --rm --name swift15-convert \ -v /home/dank/models:/models \ -v /home/dank/serverAppConfigs-swift15:/campaign:ro \ -v "$CALIB:/data/calib:ro" \ -e CALIB=/data/calib \ --entrypoint /opt/radiance/bin/rad-convert \ "$IMAGE" "/models/${CKPT#/home/dank/models/}" \ --recipe /campaign/"${RECIPE#/home/dank/serverAppConfigs-swift15/}" \ -o /models/rad/"$(basename "$OUT")" 2>&1 | tee -a "$LOG" rc=${PIPESTATUS[0]} [ "$rc" -eq 0 ] || { echo "rad-convert rc=$rc; log: $LOG" >&2; exit "$rc"; } # Gate 3: the artifact. rad-info's recipe echo is the receipt that GPTQ ran. SIZE_MIB=$(( $(stat -c %s "$OUT") / 1048576 )) [ "$SIZE_MIB" -ge "$MIN_MIB" ] || { echo "$OUT is ${SIZE_MIB} MiB < floor ${MIN_MIB}" >&2; exit 1; } podman run --rm --entrypoint /opt/radiance/bin/rad-info \ -v /home/dank/models:/models:ro "$IMAGE" /models/rad/"$(basename "$OUT")" \ 2>&1 | tee -a "$LOG" | grep -c 'gptq' >/dev/null \ || echo "WARNING: rad-info shows no gptq rule in the recorded recipe" >&2 echo "CONVERT_DONE ${SIZE_MIB} MiB -> $OUT"