The 2026-10-09 wire aliases become the service names everywhere: systemd units, serve scripts, env files and FRANK_* variables, deploy-plan/deploy-remote service keys, container names, Prometheus job names + alert rules, Grafana dashboard queries, bats seams, runbook filenames, homepage/AGENTS lore, and historical prose. Rollback tags renamed in lockstep. Residue in the tree: zero. Retired frank-medium/small/npu names are history and stay. Known casualty: scripts/patches/*.patch context lines were rewritten; those retired-era vLLM-fork patches no longer apply byte-exact.
133 lines
6.2 KiB
Bash
Executable File
133 lines
6.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Serve HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive (Q6_K) as
|
||
# frank-zealot on Dankclaw's RTX 3090 via llama.cpp (master build, CUDA sm_86).
|
||
#
|
||
# 2026-10-07 (Frank): Qwen3.5-9B swap at TWO 200,704-token conversations
|
||
# (--ctx-size 401408 --parallel 2) with EXACT f16 KV — the 9B rung frees the
|
||
# room the 26B MoE Gemma4 shape lacked, so quantized V retires. MTP stays off:
|
||
# this GGUF carries no MTP tensors at all (verified from its tensor-name
|
||
# list), so there is no in-file head to draft with. Verbosity 4 ships so the
|
||
# VRAM-fit forensics (KV layout, buffer sizes, graph capture) land in the journal.
|
||
#
|
||
# Architecture: qwen35 — dense 9B, 32 layers with full_attention_interval 4
|
||
# (8 full-attention layers at 3,7,...,31 + 24 Gated DeltaNet linear layers),
|
||
# 16 heads / 4 KV heads × 256 dim, native 262,144, vocab 248,320. The KV pool
|
||
# scales with the 8 full layers: 32 KiB/token at f16 K + f16 V (12.25 GiB at
|
||
# the 401,408 pool); the DeltaNet layers cost a fixed recurrent state per
|
||
# slot. Feasibility is priced from the MiMo-V2.6-Distill-Qwen-9B boots (same
|
||
# geometry, same card, same shape): 21,276/2,849 at ubatch 1024, 21,846/2,279
|
||
# at ubatch 2048 — the boot ledger, not arithmetic, is the referee.
|
||
# Vision comes from the publisher's own BF16 projector (the model is natively
|
||
# multimodal), 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
|
||
# publisher recipe from models.yml: thinking temp 0.6 / 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-master/build-cuda/bin/llama-server}"
|
||
: "${MODEL_FILE:=/home/dank/models/Qwen35-9B-HauhauCS-Q6K/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-Q6_K.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}"
|
||
# Exact (unquantized) KV: f16 K + f16 V, restoring the MiMo-era contract that
|
||
# the 26B Gemma4 shape could not afford. 32 KiB/token at this geometry
|
||
# (8 full-attention layers × 4 KV heads × 256 dim); 12.25 GiB at the 401,408
|
||
# pool. Explicit either way so the contract is visible in the argv, and
|
||
# --flash-attn stays required for any quantized-V fallback (q8_0 = llama.cpp's
|
||
# FP8 class; no true fp8 KV dtype exists in this build).
|
||
: "${CACHE_TYPE_K:=f16}"
|
||
: "${CACHE_TYPE_V:=f16}"
|
||
# Speculative decoding is OFF: this Q6_K 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);
|
||
# naming a drafter requires SPEC_DRAFT_FILE alongside SPEC_TYPE — the Gemma4
|
||
# assistant on disk is arch gemma4-assistant and cannot drafter-match qwen35.
|
||
: "${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/Qwen35-9B-HauhauCS-Q6K/mmproj-Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-BF16.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[@]}"}
|