- serve script: header + every fallback default re-keyed to the Bonsai-2 trio (vendor LLAMA_BIN, PQ2_0 MODEL_FILE, Q8_0 MMPROJ) and CACHE_TYPE_*:=q8_0 — a defaulted run must land on the shipped shape, never on the retired 9B/f16 pair (the trial window proved a silent default is how a swap goes wrong) - unit Description now names the payload + KV dtype that `systemctl status` will show during the next incident - OpenWebUI decoration-row description re-keyed, with the law that the row is live DB state a swap must follow into the database
136 lines
6.3 KiB
Bash
Executable File
136 lines
6.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Serve prism-ml/Ternary-Bonsai-2-27B (PQ2_0 ternary quant) as frank-zealot on
|
||
# Dankclaw's RTX 3090 via llama.cpp (PrismML vendor build 10754, CUDA sm_86).
|
||
#
|
||
# 2026-10-17 (Frank): Bonsai-2 PQ2_0 swap at TWO 200,704-token conversations
|
||
# (--ctx-size 401408 --parallel 2) with q8_0 K + q8_0 V — f16 KV does not fit
|
||
# this geometry: the engine's own fit projected 33,198 MiB against 23,558 free
|
||
# and cudaMalloc failed on the 25,088 MiB pool. MTP stays off: this GGUF
|
||
# carries no MTP tensors (verified from its tensor-name list). Verbosity 4
|
||
# ships so the VRAM-fit forensics (KV layout, buffer sizes, graph capture)
|
||
# land in the journal.
|
||
#
|
||
# Architecture: qwen35 — 27B, 64 blocks with full_attention_interval 4 (16
|
||
# full-attention layers + 48 Gated DeltaNet linear layers), 24 heads / 4 KV
|
||
# heads × 256 dim, native 262,144, vocab 248,320. The KV pool scales with the
|
||
# 16 full layers: 34 KiB/token at q8_0 K + q8_0 V (13,328 MiB at the 401,408
|
||
# pool); the DeltaNet layers cost a fixed recurrent state per SLOT (~150 MiB —
|
||
# boot-ledger context minus KV). Boot ledger: 22,664 used / 1,461 free, with
|
||
# the Q8_0 projector's 873 MiB landing AFTER the engine's fit projection.
|
||
# The runtime is a hard lock, not a preference: the file carries 402 tensors
|
||
# of custom GGML type 142 plus prism.hadamard.* metadata, which only the
|
||
# vendor build registers — stock llama.cpp refuses it at load.
|
||
# Vision comes from the publisher's Q8_0 projector, so --mmproj is what gives
|
||
# this endpoint eyes; MMPROJ_FILE="" is the documented text-only opt-out.
|
||
#
|
||
# Sampling: llama.cpp takes it per request (the fleet harness sends the file's
|
||
# own template defaults from models.yml: thinking temp 1.0 / top_p 0.95 / top_k 20).
|
||
#
|
||
# Tunables come from frank-zealot.env unless FRANK_ZEALOT_ENV_FILE overrides.
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||
ENV_FILE="${FRANK_ZEALOT_ENV_FILE:-$SCRIPT_DIR/frank-zealot.env}"
|
||
if [[ -f "$ENV_FILE" ]]; then
|
||
set -a
|
||
# shellcheck disable=SC1090
|
||
source "$ENV_FILE"
|
||
set +a
|
||
fi
|
||
|
||
# --- defaults (used when the env file does not set them) -------------------
|
||
: "${LLAMA_BIN:=/home/dank/GitPackages/llama-prism/llama-server}"
|
||
: "${MODEL_FILE:=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-PQ2_0.gguf}"
|
||
: "${ALIAS:=frank-zealot}"
|
||
: "${BIND_ADDR:=0.0.0.0}"
|
||
: "${LISTEN_PORT:=7771}"
|
||
# ctx-size is the POOL llama.cpp divides across slots: 401,408 / 2 = 200,704
|
||
# per conversation, inside the model's trained 262,144. The harness
|
||
# contextWindow MUST quote the PER-SLOT number — a longer prompt is answered
|
||
# with a 400, never truncated.
|
||
: "${CTX_SIZE:=401408}"
|
||
: "${PARALLEL:=2}"
|
||
# Compute-buffer geometry. Ubatch is a prefill knob (decode never moves).
|
||
# 2048 is the measured CUDA optimum (MiMo era, same arch/shape: +4.6 % prefill
|
||
# at 8.9k depth, decode flat, 616 MiB). The logical batch is VRAM- and
|
||
# throughput-neutral at a fixed ubatch.
|
||
: "${BATCH_SIZE:=4096}"
|
||
: "${UBATCH_SIZE:=2048}"
|
||
# Quantized KV: q8_0 K + q8_0 V — the only pair that funds 2 × 200,704 on this
|
||
# card. 34 KiB/token at this geometry (16 full-attention layers × 4 KV heads ×
|
||
# 256 dim); 13,328 MiB at the 401,408 pool. f16 measured OOM; q4_0 is the
|
||
# fallback rung (16,392 used / 7,733 free). Explicit either way so the contract
|
||
# is visible in the argv, and --flash-attn stays required for quantized V
|
||
# (no true fp8 KV dtype exists in this build).
|
||
: "${CACHE_TYPE_K:=q8_0}"
|
||
: "${CACHE_TYPE_V:=q8_0}"
|
||
# Speculative decoding is OFF: this PQ2_0 file carries NO nextn/mtp tensors, so
|
||
# there is no in-file head (the law: MTP is a property of the file, not of the
|
||
# flags). SPEC_TYPE=none strips BOTH speculative flags (flags move as a set);
|
||
# an MTP-bearing CRACK re-export exists upstream (ManniX-ITA, +451 MB) if
|
||
# decode ever needs it — that is a new payload with its own VRAM bill, never a
|
||
# flag flip on this file.
|
||
: "${SPEC_TYPE:=none}"
|
||
: "${SPEC_DRAFT_N_MAX:=3}"
|
||
: "${SPEC_DRAFT_FILE:=}"
|
||
: "${NGL:=99}"
|
||
# Log verbosity threshold (llama.cpp -lv). 3 is the engine default (info);
|
||
# 4 adds debug detail (KV layout, buffer sizes, graph capture) which is what
|
||
# the VRAM-fit forensics need. Levels above 4 flood the journal.
|
||
: "${VERBOSITY:=3}"
|
||
# Vision projector. No colon on purpose: an explicitly empty value means
|
||
# "text-only, on purpose" and must NOT be replaced by the default path.
|
||
: "${MMPROJ_FILE=/home/dank/models/Bonsai2-PQ2_0/Ternary-Bonsai-2-27B-mmproj-Q8_0.gguf}"
|
||
|
||
# Fail loudly rather than half-loading: a missing file is a download that
|
||
# has not finished, and Restart=always would turn it into a crash loop that
|
||
# reads like a runtime fault.
|
||
for path in "$LLAMA_BIN" "$MODEL_FILE"; do
|
||
[[ -e "$path" ]] || { echo "frank-zealot: missing $path" >&2; exit 1; }
|
||
done
|
||
EXTRA_ARGS=()
|
||
if [[ -n "$MMPROJ_FILE" ]]; then
|
||
[[ -e "$MMPROJ_FILE" ]] || { echo "frank-zealot: missing $MMPROJ_FILE" >&2; exit 1; }
|
||
EXTRA_ARGS+=(--mmproj "$MMPROJ_FILE")
|
||
fi
|
||
|
||
# Speculative flags move as a set: they reach the argv ONLY when a drafter
|
||
# is named. SPEC_TYPE=none (the shipped contract) strips both — a no-spec
|
||
# baseline must not carry half a spec pair.
|
||
if [[ -n "$SPEC_TYPE" && "$SPEC_TYPE" != "none" ]]; then
|
||
EXTRA_ARGS+=(--spec-type "$SPEC_TYPE" --spec-draft-n-max "$SPEC_DRAFT_N_MAX")
|
||
if [[ -n "$SPEC_DRAFT_FILE" ]]; then
|
||
[[ -e "$SPEC_DRAFT_FILE" ]] || { echo "frank-zealot: missing $SPEC_DRAFT_FILE" >&2; exit 1; }
|
||
EXTRA_ARGS+=(--spec-draft-model "$SPEC_DRAFT_FILE")
|
||
fi
|
||
fi
|
||
|
||
# The UVM device node is created lazily by nvidia-modprobe on the first CUDA
|
||
# open after boot; make sure it exists before the server needs it.
|
||
nvidia-modprobe -u -c=0 2>/dev/null || true
|
||
|
||
# Flash attention stays pinned on: it is REQUIRED for quantized V (q8_0),
|
||
# and the build carries GGML_CUDA_FA_ALL_QUANTS=ON (since 2026-09-17), so
|
||
# the q8_0 KV contract in frank-zealot.env is a real path — without this
|
||
# flag or that build option the engine silently falls back and the
|
||
# contract becomes a lie.
|
||
ARGS=(
|
||
--model "$MODEL_FILE"
|
||
--alias "$ALIAS"
|
||
--host "$BIND_ADDR"
|
||
--port "$LISTEN_PORT"
|
||
--ctx-size "$CTX_SIZE"
|
||
--parallel "$PARALLEL"
|
||
--batch-size "$BATCH_SIZE"
|
||
--ubatch-size "$UBATCH_SIZE"
|
||
--n-gpu-layers "$NGL"
|
||
--flash-attn on
|
||
--cache-type-k "$CACHE_TYPE_K"
|
||
--cache-type-v "$CACHE_TYPE_V"
|
||
--log-verbosity "$VERBOSITY"
|
||
--jinja
|
||
--reasoning auto
|
||
--reasoning-format deepseek
|
||
)
|
||
exec "$LLAMA_BIN" "${ARGS[@]}" ${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"}
|