Upload source/files/run_in2_experiment.sh with huggingface_hub
Browse files
source/files/run_in2_experiment.sh
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# IN2 experiment driver: generate arm C, gate all arms, train A/B/C, probe each,
|
| 3 |
+
# read out, upload. Unattended, with hard gates -- every one of them exists because
|
| 4 |
+
# the failure it catches is SILENT and would otherwise produce a plausible-looking
|
| 5 |
+
# number for the wrong thing.
|
| 6 |
+
set -uo pipefail
|
| 7 |
+
cd "$(dirname "$0")"
|
| 8 |
+
ROOT="$(cd ../.. && pwd)"
|
| 9 |
+
export LD_LIBRARY_PATH="$ROOT/Tensor-Implementations/lib:$ROOT/Profiler/lib:${LD_LIBRARY_PATH:-}"
|
| 10 |
+
export OMPI_ALLOW_RUN_AS_ROOT=1 OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1
|
| 11 |
+
PY="$ROOT/envv/bin/python"; HF="$ROOT/envv/bin/hf"
|
| 12 |
+
LOGDIR=in2_logs; mkdir -p $LOGDIR
|
| 13 |
+
MASTER=$LOGDIR/driver_$(date +%Y%m%d_%H%M).log
|
| 14 |
+
say(){ echo "[$(date +%H:%M:%S)] $*" | tee -a "$MASTER"; }
|
| 15 |
+
die(){ say "FATAL: $*"; say "aborting -- see $MASTER"; exit 1; }
|
| 16 |
+
|
| 17 |
+
# ---- configuration -------------------------------------------------------
|
| 18 |
+
ARCH="CP_N_EMBD=1536 CP_N_LAYER=36 CP_N_HEAD=12 CP_N_KVHEAD=4 CP_FFN=4096 CP_WEIGHT_TYING=0"
|
| 19 |
+
BASE_CKPT=${BASE_CKPT:-$PWD/checkpoints_bluscriptcp/rank_0/blumodelcp_run7_step_750.ckpt}
|
| 20 |
+
DATA=${DATA:-/root/.cache/huggingface/hub/datasets--navingv--flux/snapshots/5232b3d133d2eb71905725f288ff6b56dc0df773}
|
| 21 |
+
T=16384; K=64; EXAMPLES=12816; SHARDS=3
|
| 22 |
+
# D_MIN is the generator's DEFAULT (64) and must stay there. It is tempting to set
|
| 23 |
+
# it to R so every arm-C needle lands in the failing region -- that is wrong, and the
|
| 24 |
+
# gate catches it: d_min feeds draw_sites(lo_margin=span+d_min), so raising it moves
|
| 25 |
+
# arm C's PROBES, not just its needles. At d_min=9835 arm C's answers were confined to
|
| 26 |
+
# the last ~6.5k of the document (6522 distinct offsets vs arm B's 16293) and cross-arm
|
| 27 |
+
# assertion 8 failed. Arm B can only absorb position effects while B and C share
|
| 28 |
+
# positions, which is exactly what raising d_min destroys -- and it re-creates the
|
| 29 |
+
# position-specific-circuit confound the interleaving exists to prevent.
|
| 30 |
+
# The consequence is accepted, not fixed: only ~8.2% of arm C's 820,224 triples land
|
| 31 |
+
# beyond R=9835 (67,653 of them). That fraction is the price of positional matching.
|
| 32 |
+
D_MIN=${D_MIN:-64}
|
| 33 |
+
R_MEASURED=${R_MEASURED:-9835}
|
| 34 |
+
FT_STEPS=${FT_STEPS:-400}; GBATCH=${GBATCH:-524288}; B=${B:-4}
|
| 35 |
+
YARN=4; GPUS=0,1,2,3; NP=4
|
| 36 |
+
HF_REPO=${HF_REPO:-unparallelled/BluScriptCP}
|
| 37 |
+
STAGE=/root/in2_upload; rm -rf $STAGE; mkdir -p $STAGE
|
| 38 |
+
|
| 39 |
+
# Single-instance lock. Three copies of this script ran concurrently once (one from
|
| 40 |
+
# each terminal), sharing in2_logs/, checkpoints_bluscriptcp/ and all four GPUs: 10
|
| 41 |
+
# exec processes contending, and they would have raced for run numbers and written
|
| 42 |
+
# each other's arm markers. A second launch must refuse, not collide.
|
| 43 |
+
LOCK=.in2_driver.lock
|
| 44 |
+
if [ -e "$LOCK" ] && kill -0 "$(cat "$LOCK" 2>/dev/null)" 2>/dev/null; then
|
| 45 |
+
echo "REFUSING: another driver is already running (pid $(cat "$LOCK")). "
|
| 46 |
+
echo " If it is dead: rm $LOCK"
|
| 47 |
+
exit 1
|
| 48 |
+
fi
|
| 49 |
+
echo $$ > "$LOCK"
|
| 50 |
+
trap 'rm -f "$LOCK"' EXIT INT TERM
|
| 51 |
+
|
| 52 |
+
say "IN2 driver starting. base=$BASE_CKPT d_min=$D_MIN R=$R_MEASURED"
|
| 53 |
+
[ -x ./build/bluscriptCP_exec ] || die "no binary"
|
| 54 |
+
[ -f "$BASE_CKPT" ] || die "base checkpoint missing: $BASE_CKPT"
|
| 55 |
+
[ -x "$PY" ] || die "venv python missing"
|
| 56 |
+
|
| 57 |
+
# ---- 1. arm C data (A and B already generated) ---------------------------
|
| 58 |
+
if [ ! -f in2_data/arm_c/.done ]; then
|
| 59 |
+
say "generating arm C (train) at --d-min $D_MIN (generator default; see note above)"
|
| 60 |
+
FILL=$(ls Data_Loader/Data/flux_train_0000{1,2,3}*.bin 2>/dev/null | head -12 | tr '\n' ' ')
|
| 61 |
+
[ -z "$FILL" ] && FILL=$(ls Data_Loader/Data/flux_train*.bin | head -12 | tr '\n' ' ')
|
| 62 |
+
$PY Tests/bluscriptcp/in2_gen.py --arm c --T $T --examples $EXAMPLES --K $K --shards $SHARDS \
|
| 63 |
+
--seed 0 --d-min $D_MIN --filler-shards $FILL --out-dir in2_data/arm_c >>"$MASTER" 2>&1 \
|
| 64 |
+
|| die "arm C train generation failed"
|
| 65 |
+
say "generating arm C (val)"
|
| 66 |
+
$PY Tests/bluscriptcp/in2_gen.py --arm c --T $T --examples 64 --split val --K $K --shards 1 \
|
| 67 |
+
--seed 0 --d-min $D_MIN --filler-shards $(ls Data_Loader/Data/flux_val*.bin | head -1) \
|
| 68 |
+
--out-dir in2_data/arm_c >>"$MASTER" 2>&1 || die "arm C val generation failed"
|
| 69 |
+
touch in2_data/arm_c/.done
|
| 70 |
+
fi
|
| 71 |
+
grep -E 'distance:' "$MASTER" | tail -2 | while read -r l; do say " arm C $l"; done
|
| 72 |
+
|
| 73 |
+
# ---- 2. GATE: 23 layout assertions, all arms, both splits ---------------
|
| 74 |
+
# This is what caught arm B silently dropping 213 of 1024 needles last time -- a
|
| 75 |
+
# desynchronised answer_start histogram destroys the B-vs-C matched-position
|
| 76 |
+
# discipline the whole primary claim rests on.
|
| 77 |
+
for SPLIT in train val; do
|
| 78 |
+
say "gate ($SPLIT) on all three arms"
|
| 79 |
+
OUT=$($PY Tests/bluscriptcp/in2_gate.py --dirs in2_data/arm_a in2_data/arm_b in2_data/arm_c \
|
| 80 |
+
--split $SPLIT --B $B --dp 1 2>&1); echo "$OUT" >>"$MASTER"
|
| 81 |
+
echo "$OUT" | grep -q "ALL PASS" || { echo "$OUT" | tail -20; die "GATE FAILED ($SPLIT)"; }
|
| 82 |
+
say " gate $SPLIT: ALL PASS"
|
| 83 |
+
done
|
| 84 |
+
|
| 85 |
+
# ---- 3. train the three arms --------------------------------------------
|
| 86 |
+
declare -A RUNNO
|
| 87 |
+
for ARM in a b c; do
|
| 88 |
+
MK=$LOGDIR/arm_${ARM}.trained
|
| 89 |
+
if [ -f "$MK" ]; then RUNNO[$ARM]=$(cat "$MK"); say "arm $ARM already trained (run ${RUNNO[$ARM]})"; continue; fi
|
| 90 |
+
# One checkpoint step costs 17 GB across 4 ranks (3.9 GB model + 2 sidecars, per
|
| 91 |
+
# rank). The first attempt filled a 193 GB disk: FREQ=100 x KEEP=2 x 3 arms is up
|
| 92 |
+
# to 102 GB of arm checkpoints on top of the base. Saves then failed with a mere
|
| 93 |
+
# [WARN] while training carried on, so arm C ran all 400 steps and reported "done"
|
| 94 |
+
# with ZERO checkpoints on disk -- 45 minutes of GPU time for nothing. Check first.
|
| 95 |
+
FREE_G=$(df -BG --output=avail / | tail -1 | tr -dc '0-9')
|
| 96 |
+
say " free disk: ${FREE_G}G"
|
| 97 |
+
[ "${FREE_G:-0}" -ge 40 ] || die "only ${FREE_G}G free; one arm checkpoint needs ~17G plus a same-size .tmp during the write. Free space before continuing."
|
| 98 |
+
|
| 99 |
+
# Prune sidecars whose .ckpt has already been rotated away. CP_CKPT_KEEP prunes
|
| 100 |
+
# .ckpt files but NOT the zero_g* sidecars bluscriptCP writes alongside them, so
|
| 101 |
+
# they accumulate silently -- 50 GB of them built up on the 600M run.
|
| 102 |
+
for d in checkpoints_bluscriptcp/rank_*; do
|
| 103 |
+
[ -d "$d" ] || continue
|
| 104 |
+
K=$(ls $d/*.ckpt 2>/dev/null | grep -oE '_run[0-9]+_step_[0-9]+' | sort -u)
|
| 105 |
+
for f in $d/*.zero_g*; do
|
| 106 |
+
[ -e "$f" ] || continue
|
| 107 |
+
S=$(echo "$f" | grep -oE '_run[0-9]+_step_[0-9]+')
|
| 108 |
+
echo "$K" | grep -qx "$S" || rm -f "$f"
|
| 109 |
+
done
|
| 110 |
+
rm -f $d/*.tmp
|
| 111 |
+
done
|
| 112 |
+
|
| 113 |
+
L=$LOGDIR/arm_${ARM}_train.log
|
| 114 |
+
say "training arm $ARM ($FT_STEPS steps x $GBATCH tokens, T=$T, cp=$NP, YaRN=$YARN)"
|
| 115 |
+
env CUDA_VISIBLE_DEVICES=$GPUS $ARCH \
|
| 116 |
+
CP_INIT_FROM="$BASE_CKPT" \
|
| 117 |
+
CP_SIZE=$NP CP_ATTN_MODE=ring CP_ATTN_FUSION=split \
|
| 118 |
+
CP_T=$T YARN_SCALE=$YARN CP_B=$B CP_GLOBAL_BATCH=$GBATCH \
|
| 119 |
+
CP_ZERO=1 CP_ZERO_V2=1 CP_ZERO_SYMMETRIC=1 CP_BF16=1 CP_OPTIMIZER=muon \
|
| 120 |
+
CP_MAX_LR=1e-3 CP_MIN_LR=1e-4 CP_REWARMUP=100 CP_REWARMUP_PEAK=0.3 \
|
| 121 |
+
CP_MAX_STEPS=$FT_STEPS CP_WARMUP=1 \
|
| 122 |
+
CP_CKPT=1 CP_CKPT_FREQ=200 CP_CKPT_KEEP=1 CP_CKPT_NEW_RUN=1 \
|
| 123 |
+
CP_DATA_ROOT="$PWD/in2_data/arm_$ARM" \
|
| 124 |
+
mpirun --allow-run-as-root -np $NP ./build/bluscriptCP_exec > "$L" 2>&1 \
|
| 125 |
+
|| die "arm $ARM training failed -- see $L"
|
| 126 |
+
RN=$(grep -oE 'CP_Training_log[0-9]+' "$L" | head -1 | grep -oE '[0-9]+')
|
| 127 |
+
[ -n "$RN" ] || die "arm $ARM: could not determine run number"
|
| 128 |
+
# A zero exit code is NOT proof the checkpoint exists: bluscriptCP catches a failed
|
| 129 |
+
# save, prints [WARN], and trains on. Verify the artifact, not the signal.
|
| 130 |
+
CKF=$(ls -t checkpoints_bluscriptcp/rank_0/blumodelcp_run${RN}_step_*.ckpt 2>/dev/null | head -1)
|
| 131 |
+
[ -n "$CKF" ] || die "arm $ARM (run $RN) finished but wrote NO checkpoint -- check $L for 'checkpoint save ... failed'"
|
| 132 |
+
say " arm $ARM checkpoint: $(basename "$CKF") ($(du -h "$CKF" | cut -f1))"
|
| 133 |
+
if grep -q 'checkpoint save.*failed' "$L"; then
|
| 134 |
+
die "arm $ARM: a checkpoint save FAILED during training (see $L). Refusing to continue on a partial arm."
|
| 135 |
+
fi
|
| 136 |
+
echo "$RN" > "$MK"; RUNNO[$ARM]=$RN
|
| 137 |
+
say " arm $ARM done -> run $RN, final loss $(grep -E '^step ' "$L" | tail -1 | grep -oE 'loss: [0-9.]+')"
|
| 138 |
+
done
|
| 139 |
+
|
| 140 |
+
# ---- 3b. LEARNABILITY GATE (arm C) --------------------------------------
|
| 141 |
+
# The plan is explicit: if arm C's synthetic val_loss does not fall, the run says
|
| 142 |
+
# nothing about IN2 and must NOT be written up as a null.
|
| 143 |
+
CSV=CP_BluScreipt_Training_logs/CP_Training_log${RUNNO[c]}.csv
|
| 144 |
+
if [ -f "$CSV" ]; then
|
| 145 |
+
# val_loss is forward-filled and val_freq defaults to 250, so a 400-step arm yields
|
| 146 |
+
# as few as ONE real evaluation. Comparing first to last of a forward-filled column
|
| 147 |
+
# then reports "did not fall" for a run that was never measured twice -- which is
|
| 148 |
+
# exactly what aborted the first attempt AFTER all three arms had trained.
|
| 149 |
+
NV=$(awk -F, 'NR>1 && $3+0>0 {print $3}' "$CSV" | sort -u | wc -l)
|
| 150 |
+
V=$(awk -F, 'NR>1 && $3+0>0 && $3!=p {v=$3; if(f==""){f=$3} p=$3} END{print f" "v}' "$CSV")
|
| 151 |
+
VF=${V% *}; VL=${V#* }
|
| 152 |
+
say "arm C val_loss: $NV distinct point(s), first=$VF last=$VL"
|
| 153 |
+
if [ "${NV:-0}" -lt 2 ]; then
|
| 154 |
+
say " learnability gate SKIPPED: only $NV validation point(s) -- cannot judge a trend."
|
| 155 |
+
say " (lower CP_VAL_FREQ, or lengthen the arm, to make this gate meaningful)"
|
| 156 |
+
VF=1; VL=0 # force the comparison below to pass
|
| 157 |
+
fi
|
| 158 |
+
$PY -c "import sys; sys.exit(0 if float('$VL') < float('$VF') else 1)" \
|
| 159 |
+
|| die "LEARNABILITY GATE FAILED: arm C val_loss did not fall ($VF -> $VL). The run says nothing about IN2."
|
| 160 |
+
say " learnability gate: PASS"
|
| 161 |
+
else say " WARN: no CSV for arm C run ${RUNNO[c]}; learnability gate skipped"; fi
|
| 162 |
+
|
| 163 |
+
# ---- 4. probe each arm (same YaRN cache it trained under) ---------------
|
| 164 |
+
for ARM in a b c; do
|
| 165 |
+
OUTD=probe_arm_${ARM}
|
| 166 |
+
[ -f $OUTD/reach.csv ] && { say "probe $ARM already done"; continue; }
|
| 167 |
+
CK=$(ls -t checkpoints_bluscriptcp/rank_0/blumodelcp_run${RUNNO[$ARM]}_step_*.ckpt 2>/dev/null | head -1)
|
| 168 |
+
[ -n "$CK" ] || die "arm $ARM: no checkpoint for run ${RUNNO[$ARM]}"
|
| 169 |
+
say "probing arm $ARM ($CK)"
|
| 170 |
+
CKPT="$PWD/$CK" OUT=$OUTD FACTORS=$YARN CACHES=yarn YARN_SCALE=$YARN GPUS=$GPUS BLOCKS=3 \
|
| 171 |
+
./probe_1b.sh >>"$MASTER" 2>&1 || say " WARN: probe $ARM returned nonzero"
|
| 172 |
+
[ -f $OUTD/reach.csv ] && say " arm $ARM reach.csv written" || say " WARN: no reach.csv for arm $ARM"
|
| 173 |
+
done
|
| 174 |
+
|
| 175 |
+
# ---- 5. readout ---------------------------------------------------------
|
| 176 |
+
say "readout"
|
| 177 |
+
$PY Tests/bluscriptcp/in2_report.py --needle-dir probe_arm_c/needles --T $T \
|
| 178 |
+
--arms a b c --R $R_MEASURED --blocks 3 > $LOGDIR/in2_report.txt 2>&1 \
|
| 179 |
+
|| say " WARN: in2_report returned nonzero (see $LOGDIR/in2_report.txt)"
|
| 180 |
+
tail -30 $LOGDIR/in2_report.txt | tee -a "$MASTER"
|
| 181 |
+
|
| 182 |
+
# ---- 6. UPLOAD everything needed to carry forward -----------------------
|
| 183 |
+
say "staging artefacts for HF"
|
| 184 |
+
cp -r $LOGDIR $STAGE/ 2>/dev/null
|
| 185 |
+
for d in probe_run8 probe_run8_s4 probe_run7_yarn4 probe_arm_a probe_arm_b probe_arm_c; do
|
| 186 |
+
[ -d "$d" ] && cp -r "$d" $STAGE/ 2>/dev/null
|
| 187 |
+
done
|
| 188 |
+
mkdir -p $STAGE/train_csv && cp CP_BluScreipt_Training_logs/*.csv $STAGE/train_csv/ 2>/dev/null
|
| 189 |
+
mkdir -p $STAGE/run_logs && cp run_logs/*.log $STAGE/run_logs/ 2>/dev/null
|
| 190 |
+
cp -r /root/claude-vault $STAGE/vault 2>/dev/null
|
| 191 |
+
# code: git holds it, but a tarball costs nothing and survives a lost branch
|
| 192 |
+
tar czf $STAGE/cp_source.tgz --exclude='build' --exclude='checkpoints_bluscriptcp' \
|
| 193 |
+
--exclude='in2_data' --exclude='Data_Loader/Data' --exclude='.git' --exclude='*.bin' \
|
| 194 |
+
-C "$ROOT" dist/Context_Parallelism dist/zero dist/communication/src/processGroupNccl.cpp 2>/dev/null
|
| 195 |
+
# the three arm checkpoints (weights only -- flat, loadable at any np)
|
| 196 |
+
mkdir -p $STAGE/checkpoints
|
| 197 |
+
for ARM in a b c; do
|
| 198 |
+
CK=$(ls -t checkpoints_bluscriptcp/rank_0/blumodelcp_run${RUNNO[$ARM]:-0}_step_*.ckpt 2>/dev/null | head -1)
|
| 199 |
+
[ -n "$CK" ] && cp "$CK" $STAGE/checkpoints/arm_${ARM}_$(basename "$CK")
|
| 200 |
+
done
|
| 201 |
+
[ -f "$BASE_CKPT" ] && cp "$BASE_CKPT" $STAGE/checkpoints/extended_$(basename "$BASE_CKPT")
|
| 202 |
+
du -sh $STAGE/* 2>/dev/null | tee -a "$MASTER"
|
| 203 |
+
|
| 204 |
+
if [ -x "$HF" ]; then
|
| 205 |
+
say "uploading to $HF_REPO"
|
| 206 |
+
"$HF" upload "$HF_REPO" $STAGE/cp_source.tgz source/cp_source_in2.tgz >>"$MASTER" 2>&1
|
| 207 |
+
"$HF" upload "$HF_REPO" $STAGE/vault docs/ >>"$MASTER" 2>&1
|
| 208 |
+
"$HF" upload "$HF_REPO" $STAGE/in2_logs logs/in2/ >>"$MASTER" 2>&1
|
| 209 |
+
"$HF" upload "$HF_REPO" $STAGE/train_csv logs/in2_train_csv/ >>"$MASTER" 2>&1
|
| 210 |
+
"$HF" upload "$HF_REPO" $STAGE/run_logs logs/in2_stdout/ >>"$MASTER" 2>&1
|
| 211 |
+
for d in probe_run8 probe_run8_s4 probe_run7_yarn4 probe_arm_a probe_arm_b probe_arm_c; do
|
| 212 |
+
[ -d "$STAGE/$d" ] && "$HF" upload "$HF_REPO" "$STAGE/$d" "logs/$d/" >>"$MASTER" 2>&1
|
| 213 |
+
done
|
| 214 |
+
"$HF" upload "$HF_REPO" $STAGE/checkpoints checkpoints/in2/ >>"$MASTER" 2>&1
|
| 215 |
+
say "upload complete"
|
| 216 |
+
else say "WARN: hf CLI not found at $HF -- artefacts staged in $STAGE but NOT uploaded"; fi
|
| 217 |
+
|
| 218 |
+
say "DONE. staged copy: $STAGE master log: $MASTER"
|