diff --git a/scripts/apply-protected-cast.sh b/scripts/apply-protected-cast.sh new file mode 100755 index 0000000..a1553c5 --- /dev/null +++ b/scripts/apply-protected-cast.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# apply-protected-cast.sh — RUNS ON DANKSERVER. Rewrite the recipe's +# PROTECTED-BEGIN/END section from Swift's OWN calibration grams: run +# derive-protected-experts.py over the calib dir, splice its cast lines into +# the marked section of the recipe in place, keep the pre-edit recipe in the +# campaign trash. Refuses to run twice (marker text says derived). +set -euo pipefail +CALIB=${1:-/home/dank/corpora/calib} +RECIPE=${2:-/home/dank/serverAppConfigs-swift15/scripts/recipes/qwen4exp-swift15-w4dn8-i8-hc8m.recipe} +PY=/home/dank/venvs/radcorpus/bin/python +DERIVE=/home/dank/serverAppConfigs-swift15/scripts/derive-protected-experts.py +TS=$(date +%Y%m%d-%H%M%S) + +grep -q "derive-protected-experts.py rewrote" "$RECIPE" && { + echo "recipe already carries derived cast lines; refusing to re-derive" >&2; exit 1; } +grep -q "PROTECTED-BEGIN" "$RECIPE" || { echo "no PROTECTED markers in $RECIPE" >&2; exit 1; } + +"$PY" "$DERIVE" "$CALIB" > "/tmp/protected-cast-$TS.txt" +N=$(grep -c 'cast dtype=bf16' "/tmp/protected-cast-$TS.txt") +[ "$N" -ge 2 ] || { echo "derive produced $N cast lines; refusing" >&2; exit 1; } +echo "derived $N protected experts -> /tmp/protected-cast-$TS.txt" + +cp -f "$RECIPE" "/home/dank/.AGENTTRASH/$TS-qwen4exp-swift15-w4dn8-i8-hc8m.recipe" +"$PY" - "$RECIPE" "/tmp/protected-cast-$TS.txt" <<'PYEOF' +import sys +recipe, castfile = sys.argv[1], sys.argv[2] +lines = open(recipe).read().splitlines() +cast = [l for l in open(castfile).read().splitlines() if 'cast dtype=bf16' in l] +out, insec = [], False +for l in lines: + if 'PROTECTED-BEGIN' in l: + insec = True + out.append(l) + out.extend(cast) + elif 'PROTECTED-END' in l: + insec = False + out.append(l) + out.append('# (derive-protected-experts.py rewrote this section from Swift\'s own grams)') + elif not insec: + out.append(l) +open(recipe, 'w').write('\n'.join(out) + '\n') +print(f'spliced {len(cast)} cast lines into {recipe}') +PYEOF diff --git a/scripts/kld-gate-swift15.sh b/scripts/kld-gate-swift15.sh new file mode 100755 index 0000000..f67f3b9 --- /dev/null +++ b/scripts/kld-gate-swift15.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# kld-gate-swift15.sh — RUNS ON DANKSERVER. Boot the w4dn8 candidate on the +# serving pin (1.0.13) in --kld-ref mode: score it against the bf16 reference +# recorded by run-swift15-bf16.sh kld, on the held-out 1.8M-token corpus. +# Gate (settled design): mean KL <= 0.03 AND top-1 agreement >= 95%. +# On fail the escalation is gate_up w4nl -> w5nl (+3-4 h), not a re-tune. +# +# Shape: the candidate's experts are ~37.6 GiB a rank at w4dn8, under the +# 40960 MiB pool, so the tier is fully resident — no disk tier here; the +# record scored the bf16 model at its own (disk-tier) shape, which is a +# placement choice, not a numerics choice (weights resident are identical). +set -uo pipefail + +PIN_1013=sha256:4c3f484564bb2872784e46b0a59cbd71becc9c1e746e301a3596ffae253c322e +IMAGE="stilldeadcode/radiance@$PIN_1013" +RAD=/home/dank/models/rad/qwen4exp-swift15-w4dn8-i8-hc8m.rad +C=swift15gate +LOG=/home/dank/logs/swift15_gate.log +REPORT=/home/dank/corpora/kld-cand.json +MAX_KL=${FRANK_KLD_GATE_MAX_KL:-0.03} +MIN_TOP1=${FRANK_KLD_GATE_MIN_TOP1:-95} + +restore() { sudo systemctl start frank-primary >/dev/null 2>&1 || true; } +trap restore EXIT +sudo systemctl stop frank-primary >/dev/null 2>&1 || true +podman rm -f "$C" >/dev/null 2>&1 || true +[ -d /home/dank/corpora/kld-ref ] && [ -n "$(ls /home/dank/corpora/kld-ref)" ] || { + echo "no kld reference recorded" >&2; exit 1; } + +podman run --rm --name "$C" \ + --network host --init \ + --security-opt seccomp=unconfined \ + --ulimit memlock=-1:-1 \ + --device /dev/kfd --device /dev/dri \ + -v /home/dank/models:/models:ro \ + -v /home/dank/radiance-state:/data \ + -v /home/dank/corpora/kld-ref:/data/kld-ref:ro \ + -v /home/dank/corpora/out/kld.jsonl:/data/kld.jsonl:ro \ + -v /home/dank/corpora:/out \ + "$IMAGE" \ + --model /models/rad/$(basename "$RAD") \ + --tp 2 --placement expert_tiered --host-pool-mib 40960 \ + --deterministic --gpu-headroom-mib 768 \ + --num-speculative-tokens 0 \ + --kv-cache-dtype fp8 --max-model-len 16384 \ + --ngram-placement ram \ + --override-chat-template /models/tpl.jinja \ + --kld-ref /data/kld-ref --kld-corpus /data/kld.jsonl \ + --vram-kv-mib 3072 --max-num-seqs 4 --max-num-batched-tokens 32768 \ + --kld-out /out/kld-cand.json 2>&1 | tee "$LOG" +rc=${PIPESTATUS[0]} +[ "$rc" -eq 0 ] || { echo "kld run rc=$rc; log: $LOG" >&2; exit "$rc"; } + +python3 - "$REPORT" "$MAX_KL" "$MIN_TOP1" <<'PYEOF' +import json, sys, math +report, max_kl, min_top1 = sys.argv[1], float(sys.argv[2]), float(sys.argv[3]) +d = json.load(open(report)) +print(json.dumps(d, indent=1)[:4000]) +flat = {} +def walk(o, path=""): + if isinstance(o, dict): + for k, v in o.items(): walk(v, f"{path}.{k}" if path else k) + elif isinstance(o, list): + pass + else: + flat[path] = o +walk(d) +def pick(*pats): + for p in pats: + for k, v in flat.items(): + if all(s in k.lower() for s in p) and isinstance(v, (int, float)) \ + and not (isinstance(v, float) and math.isnan(v)): + return k, v + return None, None +klk, kl = pick(("mean", "kl"), ("kl", "mean"), ("mean",)) +tk, t1 = pick(("top1",), ("top", "1"), ("agreement",), ("top-1",)) +t1 = t1 * 100 if t1 is not None and t1 <= 1 else t1 +print(f"GATE mean_kl: {kl!r} ({klk}) <= {max_kl}? top1: {t1!r} ({tk}) >= {min_top1}?") +if kl is None or t1 is None: + print("GATE UNKNOWN — key names not recognised, inspect the report by hand") + sys.exit(2) +ok = kl <= max_kl and t1 >= min_top1 +print("GATE_PASS" if ok else "GATE_FAIL") +sys.exit(0 if ok else 1) +PYEOF +echo "report: $REPORT rows: $REPORT.rows" diff --git a/scripts/recipes/qwen4exp-swift15-w4dn8-i8-hc8m.recipe b/scripts/recipes/qwen4exp-swift15-w4dn8-i8-hc8m.recipe index d800668..fc4e0cf 100644 --- a/scripts/recipes/qwen4exp-swift15-w4dn8-i8-hc8m.recipe +++ b/scripts/recipes/qwen4exp-swift15-w4dn8-i8-hc8m.recipe @@ -30,6 +30,7 @@ # law -- an expert whose share of its layer's down-input energy is at least 3% and at least three # times its share of the routing, an even count on each rank's half of the layer (the four-bit GEMM # takes the rest as two equal tables by parity) by adding that half's next most energetic expert. +# PROTECTED-BEGIN (rewritten from Swift's OWN grams by derive-protected-experts.py) blk.34.ffn_*_exps.407.weight cast dtype=bf16 blk.34.ffn_*_exps.496.weight cast dtype=bf16 blk.44.ffn_*_exps.292.weight cast dtype=bf16 @@ -40,6 +41,7 @@ blk.47.ffn_*_exps.122.weight cast dtype=bf16 blk.47.ffn_*_exps.143.weight cast dtype=bf16 blk.47.ffn_*_exps.399.weight cast dtype=bf16 blk.47.ffn_*_exps.445.weight cast dtype=bf16 +# PROTECTED-END # Every other routed expert, trunk and MTP layer alike: the down projection as E4M3 codes with a # bf16 scale a 128x128 block -- one amax per block, scale = amax/448 rounded to bf16, the exact