refact(frank-dragoon): serve gates run on the shared gate library

The hand-written image/ck/tokenizer gates are require_image, require_bytes and
require_dir_files from scripts/lib/llm-gates.sh. The serve seam is unchanged
and still green (its podman stub now models `podman image exists`, the
primitive the gate uses). Behaviour preserved: every refusal names the
artifact, the endpoint and the fetch script.
This commit is contained in:
Frank committed 2026-10-13 21:40:02 -07:00
1 parent acf2a5cee5
commit 028afdeb58
4 files changed
+50 -42

No files matched your search

+4 -2
View File
@@ -69,12 +69,14 @@ require_min_mib() {
# require_dir_files — DIR must be a directory and every named member a # require_dir_files — DIR must be a directory and every named member a
# regular file (the flat-tokenizer law: snapshot symlinks dangle in the # regular file (the flat-tokenizer law: snapshot symlinks dangle in the
# container mount even when the directory itself is real). # container mount even when the directory itself is real. A symlinked
# DIRECTORY is refused too: a HF cache snapshot can link the directory
# itself, and a link dangles inside the container mount.
require_dir_files() { require_dir_files() {
local dir="$1" label="$2" hint="$3" local dir="$1" label="$2" hint="$3"
shift 3 shift 3
local f local f
if [[ ! -d "$dir" ]]; then if [[ -L "$dir" || ! -d "$dir" ]]; then
_gate_fail "$label: missing at $dir" "$hint" _gate_fail "$label: missing at $dir" "$hint"
fi fi
for f in "$@"; do for f in "$@"; do
+30 -40
View File
@@ -29,7 +29,14 @@
# exactly like frank-archon's. # exactly like frank-archon's.
set -euo pipefail set -euo pipefail
# The artifact gates are the shared library — the serve script and the
# deployer refuse the same missing/truncated payload with the same words
# (the 2026-10-01 crash-loop was the two hand-written twins disagreeing).
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export GATE_PREFIX=frank-dragoon
# shellcheck disable=SC1091
source "$SCRIPT_DIR/lib/llm-gates.sh"
ENV_FILE="${FRANK_DRAGOON_ENV_FILE:-$SCRIPT_DIR/frank-dragoon.env}" ENV_FILE="${FRANK_DRAGOON_ENV_FILE:-$SCRIPT_DIR/frank-dragoon.env}"
if [ ! -f "$ENV_FILE" ]; then if [ ! -f "$ENV_FILE" ]; then
echo "frank-dragoon: env file not found: $ENV_FILE" >&2 echo "frank-dragoon: env file not found: $ENV_FILE" >&2
@@ -57,55 +64,37 @@ set +a
: "${HALOGEN_KV_SLOTS:?HALOGEN_KV_SLOTS must be set}" : "${HALOGEN_KV_SLOTS:?HALOGEN_KV_SLOTS must be set}"
: "${HALOGEN_SPEC_ADAPT:?HALOGEN_SPEC_ADAPT must be set (0 = head always on)}" : "${HALOGEN_SPEC_ADAPT:?HALOGEN_SPEC_ADAPT must be set (0 = head always on)}"
# Capture podman's output BEFORE grepping: under `set -o pipefail` the # The image gate is the digest-pinned ref itself: `podman image exists` takes
# `podman images | grep -q` pattern races (grep exits on first match, # a name@sha256 ref in one command, so a retagged look-alike or a newer
# podman dies SIGPIPE 141, pipefail turns the 141 into "no match" — the # latest cannot satisfy it and there is no pipefail/SIGPIPE race with grep
# 2026-09-30 deploy refusal). # (the 2026-09-30 deploy refusal). Runs as dank: the unit's process uses
DIGESTS="$(podman images --format '{{.RepoDigests}}')" # dank's rootless storage.
if ! grep -qF "${FRANK_DRAGOON_IMAGE#*@}" <<<"$DIGESTS"; then require_image "$FRANK_DRAGOON_IMAGE" \
echo "frank-dragoon: image $FRANK_DRAGOON_IMAGE not in podman storage;" >&2 "pull it: podman pull docker://$FRANK_DRAGOON_IMAGE" || exit 1
echo " pull it: podman pull ${FRANK_DRAGOON_IMAGE%@*}" >&2
exit 1
fi
# Size-exact gates on all three payload files: a truncated curl exits 0 on # Size-exact gates on all three payload files: a truncated curl exits 0 on
# some failures and a partial .hgn loads far enough to look alive, so the # some failures and a partial .hgn loads far enough to look alive, so the
# byte counts from the Hub tree API are the contract (fleet fetch law). # byte counts from the Hub tree API are the contract (fleet fetch law).
ck() { require_bytes "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_CHECKPOINT" \
# ck <env-name> <path> <expected-bytes> checkpoint "$FRANK_DRAGOON_CHECKPOINT_BYTES" \
if [ ! -f "$2" ]; then "fetch it: bash scripts/download-frank-dragoon-flashnext.sh" || exit 1
echo "frank-dragoon: $1 $2 missing;" >&2 require_bytes "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_NGRAM_TABLE" \
echo " fetch it: bash scripts/download-frank-dragoon-flashnext.sh" >&2 n-gram-table "$FRANK_DRAGOON_NGRAM_BYTES" \
exit 1 "fetch it: bash scripts/download-frank-dragoon-flashnext.sh" || exit 1
fi require_bytes "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_VISION_TOWER" \
local sz vision-tower "$FRANK_DRAGOON_VISION_BYTES" \
sz="$(stat -c %s "$2")" "fetch it: bash scripts/download-frank-dragoon-flashnext.sh" || exit 1
if [ "$sz" != "$3" ]; then
echo "frank-dragoon: $1 is $sz bytes, expected $3 (partial download?)" >&2
exit 1
fi
}
ck checkpoint "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_CHECKPOINT" "$FRANK_DRAGOON_CHECKPOINT_BYTES"
ck n-gram-table "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_NGRAM_TABLE" "$FRANK_DRAGOON_NGRAM_BYTES"
ck vision-tower "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_VISION_TOWER" "$FRANK_DRAGOON_VISION_BYTES"
# The payload ships the tokenizer as a FLAT DIRECTORY. A HuggingFace cache # The payload ships the tokenizer as a FLAT DIRECTORY. A HuggingFace cache
# snapshot symlinks each file into a sibling blobs/, and those symlinks # snapshot symlinks each file into a sibling blobs/, and those symlinks
# dangle inside the container mount — so the directory must be real and # dangle inside the container mount — so the directory must be real and
# every named file must be a regular file, not a link. # every named file must be a regular file, not a link.
TOK_DIR="$FRANK_DRAGOON_MODEL_DIR/tokenizer" # The tokenizer file list is word-separated ON PURPOSE (one argument per
if [ -L "$TOK_DIR" ] || [ ! -d "$TOK_DIR" ]; then # file), so the unquoted expansion below is the interface, not a slip.
echo "frank-dragoon: tokenizer dir $TOK_DIR missing or a symlink (HF cache snapshot?);" >&2 # shellcheck disable=SC2086
echo " fetch it: bash scripts/download-frank-dragoon-flashnext.sh" >&2 require_dir_files "$FRANK_DRAGOON_MODEL_DIR/tokenizer" tokenizer \
exit 1 "fetch it: bash scripts/download-frank-dragoon-flashnext.sh" \
fi $FRANK_DRAGOON_TOKENIZER_FILES || exit 1
for TF in $FRANK_DRAGOON_TOKENIZER_FILES; do
if [ ! -f "$TOK_DIR/$TF" ] || [ -L "$TOK_DIR/$TF" ]; then
echo "frank-dragoon: tokenizer file $TOK_DIR/$TF missing or a symlink;" >&2
echo " fetch it: bash scripts/download-frank-dragoon-flashnext.sh" >&2
exit 1
fi
done
# GPU access is mandatory but the node paths are variables so the seam can # GPU access is mandatory but the node paths are variables so the seam can
# drive the gate on a box without an AMD GPU. # drive the gate on a box without an AMD GPU.
@@ -115,6 +104,7 @@ if [ ! -e "$DEV_KFD" ] || [ ! -d "$DEV_DRI" ]; then
echo "frank-dragoon: $DEV_KFD or $DEV_DRI missing — no gfx1151 to serve on" >&2 echo "frank-dragoon: $DEV_KFD or $DEV_DRI missing — no gfx1151 to serve on" >&2
exit 1 exit 1
fi fi
# Host-RAM preflight — the engine's pin gate, checked BEFORE podman run. # Host-RAM preflight — the engine's pin gate, checked BEFORE podman run.
# The engine refuses to pin when MemAvailable is below the checkpoint plus # The engine refuses to pin when MemAvailable is below the checkpoint plus
# a 16 GiB floor: pinned pages cannot be swapped, and under the floor the # a 16 GiB floor: pinned pages cannot be swapped, and under the floor the
+7
View File
@@ -45,6 +45,13 @@ podman_stub() {
#!/usr/bin/env bash #!/usr/bin/env bash
case "$1" in case "$1" in
images) printf '%s\n' "${STUB_DIGESTS:-}" ;; images) printf '%s\n' "${STUB_DIGESTS:-}" ;;
image)
# `podman image exists REF` — exit 1 when storage lacks the ref,
# which is exactly the primitive the shared gate library calls.
case " ${STUB_DIGESTS:-} " in
*" $3 "*) exit 0 ;;
*) exit 1 ;;
esac ;;
rm) exit 0 ;; rm) exit 0 ;;
run|wait) echo "PODMAN $*"; exit "${STUB_WAIT_RC:-0}" ;; run|wait) echo "PODMAN $*"; exit "${STUB_WAIT_RC:-0}" ;;
*) exit 0 ;; *) exit 0 ;;
+9
View File
@@ -130,6 +130,15 @@ call() {
[[ "$output" == *"tokenizer: missing"* ]] [[ "$output" == *"tokenizer: missing"* ]]
} }
@test "the flat-directory gate refuses a symlinked directory" {
# The snapshot directory itself can be the link (a HF cache blobs/
# sibling), and a link into the container mount dangles there too.
ln -s "$TMP/flat" "$TMP/linked-dir"
call "require_dir_files '$TMP/linked-dir' tokenizer 'fetch the tokenizer' tokenizer.json"
[ "$status" -ne 0 ]
[[ "$output" == *"tokenizer: missing"* ]]
}
@test "the image gate refuses a ref the storage does not hold" { @test "the image gate refuses a ref the storage does not hold" {
# A name@sha256 ref exists only if THAT manifest was pulled, so the # A name@sha256 ref exists only if THAT manifest was pulled, so the
# gate is the ref itself — one command, no grep race. # gate is the ref itself — one command, no grep race.