#!/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[@]}"}