frank-large: swap to stock llama.cpp serving Qwen3.8-27B UD-Q8_K_XL with DFlash2

Replace the Kairic Edge ROCmFPX build with the unsloth b11030 stock
llama.cpp prebuilt (ROCm gfx1151) serving unsloth/Qwen3.8-27B-GGUF:
UD-Q8_K_XL (a 27B dense model, 31.5 GiB) with the z-lab DFlash2 Q4_K_M
drafter (depth 4) on the gfx1151 iGPU.

Serving shape: one 262,144-token conversation, q8_0 KV (the build lacks
fp8), text-only, greedy sampling, Qwen-Sharp v22.4.0 template.

- scripts/serve-frank-large.sh: stock llama.cpp + DFlash2 spec decode
- scripts/frank-large.env: new model/drafter/runtime, 262144 ctx / 1 slot
- scripts/download-frank-large.sh: per-file fetch, verify mode, idempotent
- scripts/frank-large.service: updated description
- scripts/deploy-remote.sh: updated pkill pattern + asset gates
- tests/frank_large_unit.bats: new model/runtime/spec assertions
- docs: new runbook (active), Kairic Edge runbook retired (rollback target)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
FrankandClaude committed 2026-09-21 21:50:36 -07:00
1 parent 43b8258061
commit 7689092db8
10 files changed
+414 -306

No files matched your search

+15 -16
View File
@@ -1072,21 +1072,20 @@ deploy_anubis() {
wait_container_healthy anubis 60 || return 1
}
# frank-large is the Kairic Edge runtime (ciru-ai/ROCmFPX, branch
# kairic-edge-qwen38-27b-v1.2, commit 205a3e5) serving
# jcbtc/Qwen3.8-27B-IU4-Kairic-Edge on the Strix Halo iGPU, as a ROOT
# unit since the 2026-09-19 cutover: the runtime is built from source
# (scripts/build-kairic-edge-gfx1151.sh), so there is no container image
# to install and no generated unit to dodge. relink_unit points
# /etc/systemd/system/frank-large.service at the repo file and boot
# persistence is that unit's [Install] section.
# frank-large is the stock llama.cpp prebuilt (unsloth b11030, ROCm
# gfx1151) serving unsloth/Qwen3.8-27B-GGUF:UD-Q8_K_XL with the DFlash2
# drafter (z-lab/Qwen3.8-27B-DFlash2-GGUF:Q4_K_M) on the Strix Halo iGPU,
# as a ROOT unit since the 2026-09-19 cutover: the runtime is a prebuilt
# binary, so there is no container image to install and no generated unit
# to dodge. relink_unit points /etc/systemd/system/frank-large.service at
# the repo file and boot persistence is that unit's [Install] section.
#
# It replaces the unsloth llama.cpp prebuilt (b11030-mix-5ff778e, ROCm
# gfx1151), which had replaced the CIRU v4.4.1 Orca runtime. The port
# gate is the same as every cutover since 2026-09-16: nothing starts
# until 7770 is released.
LLM_LARGE_MODEL="${LLM_LARGE_MODEL:-/home/dank/models/Qwen3.8-27B-IU4-Kairic-Edge/Qwen3.8-27B-IU4-Kairic-Edge.gguf}"
LLM_LARGE_DRAFT="${LLM_LARGE_DRAFT:-/home/dank/models/Qwen3.8-27B-IU4-Kairic-Edge/Qwen3.8-27B-Kairic-IU4-FFN.pfs}"
# It replaces the Kairic Edge runtime (ciru-ai/ROCmFPX), which had
# replaced the unsloth llama.cpp prebuilt serving
# unsloth/Qwen3.8-Flash-Next-GGUF:UD-IQ4_XS. The port gate is the same as
# every cutover since 2026-09-16: nothing starts until 7770 is released.
LLM_LARGE_MODEL="${LLM_LARGE_MODEL:-/home/dank/models/Qwen3.8-27B-UD-Q8_K_XL/Qwen3.8-27B-UD-Q8_K_XL.gguf}"
LLM_LARGE_DRAFT="${LLM_LARGE_DRAFT:-/home/dank/models/Qwen3.8-27B-UD-Q8_K_XL/Qwen3.8-27B-DFlash2-Q4_K_M.gguf}"
retire_halogen_large_quadlet() {
local trash="$HOME/.AGENTTRASH/20260919-orca-cutover/quadlet"
@@ -1126,7 +1125,7 @@ deploy_llm_large() {
for f in "$LLM_LARGE_MODEL" "$LLM_LARGE_DRAFT"; do
if [[ ! -f "$f" ]]; then
echo "ERROR: payload absent at $f" >&2
echo "frank-large: fetch it: bash scripts/download-frank-large-kairic.sh" >&2
echo "frank-large: fetch it: bash scripts/download-frank-large.sh" >&2
missing=1
fi
done
@@ -1141,7 +1140,7 @@ deploy_llm_large() {
# The pattern never appears in the deploying shell's own command line, so
# -f is safe here (it is not in `bash deploy-remote.sh`).
run_privileged systemctl stop frank-large.service 2>/dev/null || true
pkill -f 'Qwen3.8-27B-IU4-Kairic-Edge' 2>/dev/null || true
pkill -f 'Qwen3.8-27B-UD-Q8_K_XL' 2>/dev/null || true
# Nothing may start until the predecessor has released the port. On
# 2026-09-16 the cutover failed exactly here: the old server logged
# "Deactivated successfully", the next engine tried to bind 7770 a second
-110
View File
@@ -1,110 +0,0 @@
#!/usr/bin/env bash
# Download jcbtc/Qwen3.8-27B-IU4-Kairic-Edge (the frank-large model) on
# Dankclaw.
#
# bash scripts/download-frank-large-kairic.sh
# bash scripts/download-frank-large-kairic.sh --verify-only
#
# The Kairic Edge payload is four files: the main IU4 GGUF (15.5 GiB) and
# three .pfs sidecars (FFN 7.99 GiB, GDN 1.88 GiB, GDN-Output 0.70 GiB).
# The sidecars are the PromptForge IU4 (W4A4) weight shards the Kairic Edge
# runtime loads alongside the main GGUF (PROMPTFORGE_IU4_SIDECAR,
# PROMPTFORGE_GDN_SIDECAR, PROMPTFORGE_GDN_OUTPUT_SIDECAR).
#
# Integrity: every file is checked against its declared byte count and sha256
# (the values below are the LFS oids from the Hub, 2026-09-21). The repo is
# public (gated: false); an HF token at ~/.cache/huggingface/token is used
# when present to clear the anonymous rate limit on a 26 GB transfer.
#
# Layout after a successful run (MODEL_DIR):
# Qwen3.8-27B-IU4-Kairic-Edge.gguf 16617792672 main IU4 GGUF
# Qwen3.8-27B-Kairic-IU4-FFN.pfs 8576856064 FFN sidecar
# Qwen3.8-27B-Kairic-IU4-GDN.pfs 2019569664 GDN sidecar
# Qwen3.8-27B-Kairic-IU4-GDN-Output.pfs 756953088 GDN-Output sidecar
set -euo pipefail
REPO="jcbtc/Qwen3.8-27B-IU4-Kairic-Edge"
MODEL_DIR="/home/dank/models/Qwen3.8-27B-IU4-Kairic-Edge"
BASE="https://huggingface.co/$REPO/resolve/main"
mode="download"
[[ "${1:-}" == "--verify-only" ]] && mode="verify"
token=""
auth=()
if [[ -r "$HOME/.cache/huggingface/token" ]]; then
token="$(tr -d '[:space:]' < "$HOME/.cache/huggingface/token")"
auth=(-H "Authorization: Bearer $token")
fi
dl() { # dl <relpath>
local rel="$1" dest="$MODEL_DIR/$rel"
[[ -s "$dest" ]] && return 0
mkdir -p "$(dirname "$dest")"
echo "fetching $rel"
curl -fL -C - "${auth[@]}" --retry 5 --retry-delay 3 -o "$dest" "$BASE/$rel"
}
# filename size sha256 — the LFS oids from the Hub (2026-09-21)
FILES=(
"Qwen3.8-27B-IU4-Kairic-Edge.gguf 16617792672 360caf7381907c3eca7ac0afd1228efc016af747f3f38637fb1c7f94daabac2a"
"Qwen3.8-27B-Kairic-IU4-FFN.pfs 8576856064 adcbb90a7b429a30a2a39043366d68320d72e8b4816a0f498e882b2f80a2ba2b"
"Qwen3.8-27B-Kairic-IU4-GDN.pfs 2019569664 82f931316f1c895da104915dec4697163808d06f0e6b2dc027cee7aa3afc0f0e"
"Qwen3.8-27B-Kairic-IU4-GDN-Output.pfs 756953088 3b07e7b176559e4402924ba0c368532fa6f02118a33c71e70974c809bf6208a3"
)
RELS=()
for line in "${FILES[@]}"; do
read -r rel _ _ <<<"$line"
RELS+=("$rel")
done
# Verify each file's byte count and sha256 (idempotent: a complete file is
# re-checked, not re-downloaded).
echo "verifying $MODEL_DIR against declared sizes + sha256"
python3 - "$MODEL_DIR" "${RELS[@]}" <<'PY'
import hashlib, os, sys
model_dir = sys.argv[1]
rels = sys.argv[2:]
# (rel, size, sha256) triples, re-parsed from the same source of truth.
specs = {
"Qwen3.8-27B-IU4-Kairic-Edge.gguf": (16617792672, "360caf7381907c3eca7ac0afd1228efc016af747f3f38637fb1c7f94daabac2a"),
"Qwen3.8-27B-Kairic-IU4-FFN.pfs": (8576856064, "adcbb90a7b429a30a2a39043366d68320d72e8b4816a0f498e882b2f80a2ba2b"),
"Qwen3.8-27B-Kairic-IU4-GDN.pfs": (2019569664, "82f931316f1c895da104915dec4697163808d06f0e6b2dc027cee7aa3afc0f0e"),
"Qwen3.8-27B-Kairic-IU4-GDN-Output.pfs": (756953088, "3b07e7b176559e4402924ba0c368532fa6f02118a33c71e70974c809bf6208a3"),
}
ok = True
for rel in rels:
dest = os.path.join(model_dir, rel)
size, sha = specs[rel]
if not os.path.isfile(dest):
print(f" MISSING {rel}")
ok = False
continue
actual_size = os.path.getsize(dest)
if actual_size != size:
print(f" SIZE MISMATCH {rel}: {actual_size} != {size}")
ok = False
continue
h = hashlib.sha256()
with open(dest, "rb") as f:
for chunk in iter(lambda: f.read(1 << 24), b""):
h.update(chunk)
actual_sha = h.hexdigest()
if actual_sha != sha:
print(f" SHA256 MISMATCH {rel}: {actual_sha}")
ok = False
continue
print(f" OK {rel} ({actual_size} bytes)")
if not ok:
sys.exit(1)
PY
if [[ "$mode" == "download" ]]; then
for line in "${FILES[@]}"; do
read -r rel _ _ <<<"$line"
dl "$rel"
done
fi
echo "frank-large payload complete: $MODEL_DIR"
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# Download the frank-large payload on Dankclaw:
# - unsloth/Qwen3.8-27B-GGUF:UD-Q8_K_XL (the 27B dense main GGUF)
# - z-lab/Qwen3.8-27B-DFlash2-GGUF:Q4_K_M (the DFlash2 drafter)
#
# bash scripts/download-frank-large.sh
# bash scripts/download-frank-large.sh --verify-only
#
# Integrity: every file is checked against its declared byte count and sha256
# (the values below are the LFS oids from the Hub, 2026-09-21). An HF token
# at ~/.cache/huggingface/token is used when present to clear the anonymous
# rate limit on a 31 GB transfer.
#
# Layout after a successful run (MODEL_DIR):
# Qwen3.8-27B-UD-Q8_K_XL.gguf 31457991680 main GGUF
# Qwen3.8-27B-DFlash2-Q4_K_M.gguf 1143006816 DFlash2 drafter
set -euo pipefail
MODEL_DIR="/home/dank/models/Qwen3.8-27B-UD-Q8_K_XL"
mode="download"
[[ "${1:-}" == "--verify-only" ]] && mode="verify"
token=""
auth=()
if [[ -r "$HOME/.cache/huggingface/token" ]]; then
token="$(tr -d '[:space:]' < "$HOME/.cache/huggingface/token")"
auth=(-H "Authorization: Bearer $token")
fi
dl() { # dl <url> <dest>
local url="$1" dest="$2"
[[ -s "$dest" ]] && return 0
mkdir -p "$(dirname "$dest")"
echo "fetching $dest"
curl -fL -C - "${auth[@]}" --retry 5 --retry-delay 3 -o "$dest" "$url"
}
# filename size sha256 url — the LFS oids from the Hub (2026-09-21)
FILES=(
"Qwen3.8-27B-UD-Q8_K_XL.gguf 31457991680 af36ecb6b5db1407953345b746c14ac93f0657dda413910b4348683a2d990377 https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/resolve/main/Qwen3.8-27B-UD-Q8_K_XL.gguf"
"Qwen3.8-27B-DFlash2-Q4_K_M.gguf 1143006816 1a25c56858e1ebe93f2718ac1d49d1151f9323325c1bbfd6209370f4db131ebd https://huggingface.co/z-lab/Qwen3.8-27B-DFlash2-GGUF/resolve/main/Qwen3.8-27B-DFlash2-Q4_K_M.gguf"
)
# Verify each file's byte count and sha256 (idempotent: a complete file is
# re-checked, not re-downloaded).
echo "verifying $MODEL_DIR against declared sizes + sha256"
python3 - "$MODEL_DIR" <<'PY'
import hashlib, os, sys
model_dir = sys.argv[1]
# (rel, size, sha256) triples — the LFS oids from the Hub (2026-09-21).
specs = {
"Qwen3.8-27B-UD-Q8_K_XL.gguf": (31457991680, "af36ecb6b5db1407953345b746c14ac93f0657dda413910b4348683a2d990377"),
"Qwen3.8-27B-DFlash2-Q4_K_M.gguf": (1143006816, "1a25c56858e1ebe93f2718ac1d49d1151f9323325c1bbfd6209370f4db131ebd"),
}
ok = True
for rel, (size, sha) in specs.items():
dest = os.path.join(model_dir, rel)
if not os.path.isfile(dest):
print(f" MISSING {rel}")
ok = False
continue
actual_size = os.path.getsize(dest)
if actual_size != size:
print(f" SIZE MISMATCH {rel}: {actual_size} != {size}")
ok = False
continue
h = hashlib.sha256()
with open(dest, "rb") as f:
for chunk in iter(lambda: f.read(1 << 24), b""):
h.update(chunk)
actual_sha = h.hexdigest()
if actual_sha != sha:
print(f" SHA256 MISMATCH {rel}: {actual_sha}")
ok = False
continue
print(f" OK {rel} ({actual_size} bytes)")
if not ok:
sys.exit(1)
PY
if [[ "$mode" == "download" ]]; then
for line in "${FILES[@]}"; do
read -r rel _ _ url <<<"$line"
dl "$url" "$MODEL_DIR/$rel"
done
fi
echo "frank-large payload complete: $MODEL_DIR"
+14 -20
View File
@@ -1,6 +1,6 @@
# frank-large — Kairic Edge runtime (ciru-ai/ROCmFPX, branch
# kairic-edge-qwen38-27b-v1.2, commit 205a3e5) serving
# jcbtc/Qwen3.8-27B-IU4-Kairic-Edge on the Strix Halo iGPU (gfx1151).
# frank-large — stock llama.cpp (mainline, the unsloth b11030 prebuilt)
# serving unsloth/Qwen3.8-27B-GGUF:UD-Q8_K_XL on the Strix Halo iGPU
# (gfx1151).
#
# These are the tunables read by scripts/serve-frank-large.sh. The flag
# list is owned by the serve script; this file only sets fleet values.
@@ -9,15 +9,14 @@
# runtime reads those bare (the 2026-08-13 TEMP incident). HOST is the one
# exception because the serve script itself consumes it for --host.
# The assembled payload (scripts/download-frank-large-kairic.sh): the
# serve script refuses to start without the main GGUF and the three
# sidecars (FFN, GDN, GDN-Output).
MODEL_DIR=/home/dank/models/Qwen3.8-27B-IU4-Kairic-Edge
# The payload (scripts/download-frank-large.sh): the serve script refuses to
# start without the main GGUF and the DFlash2 drafter.
MODEL_DIR=/home/dank/models/Qwen3.8-27B-UD-Q8_K_XL
# The Kairic Edge runtime (scripts/build-kairic-edge-gfx1151.sh).
RUNTIME_DIR=/home/dank/GitPackages/ROCmFPX-kairic/build-kairic
# The stock llama.cpp runtime (the unsloth b11030 prebuilt).
RUNTIME_DIR=/home/dank/GitPackages/unsloth-llama-b11030
# Fleet endpoint: the same port and alias the unsloth predecessor served, so
# Fleet endpoint: the same port and alias the prior deployments served, so
# no client, probe or models.yml entry changes address.
HOST=0.0.0.0
PORT=7770
@@ -26,17 +25,12 @@ PORT=7770
CONTEXT_SIZE=262144
PARALLEL_SLOTS=1
# Native MTP depth 4 (the model's MTP layers) + n-gram drafting (24/64/64).
# DFlash is NOT available for this model (no DRAFT_DFLASH spec type in the
# Kairic Edge runtime).
# DFlash2 spec decode (a block-diffusion drafter) at depth 4. The drafter
# has a sliding window of 2048 (small draft KV, ~40 MiB).
SPEC_DRAFT_N_MAX=4
# The harness needs sampling, tools and a reasoning budget, which the
# fast-greedy path rejects. COMPATIBILITY_MODE=1 enables them (9.7% slower
# per the build guide).
COMPATIBILITY_MODE=1
# Prompt-cache budget: 8 GiB (the Kairic Edge qualified config). The model
# is 27B dense (not DeltaNet), so the fleet's 12.8 GiB DeltaNet floor does
# Prompt-cache budget: 8 GiB. The model is a 27B hybrid (16 full attention
# + 48 Gated DeltaNet linear attention); its DeltaNet state is smaller than
# a primarily-DeltaNet model, so the fleet's 12.8 GiB DeltaNet floor does
# not apply.
CACHE_RAM=8192
+4 -5
View File
@@ -1,5 +1,5 @@
[Unit]
Description=Kairic Edge (ROCmFPX v1.2, gfx1151) Qwen3.8-27B IU4 W4A4 (dankclaw, frank-large, Strix Halo iGPU)
Description=stock llama.cpp (unsloth b11030, ROCm gfx1151) Qwen3.8-27B UD-Q8_K_XL, DFlash2 spec decode, fp8 KV, 262144 ctx / 1 slot, root systemd unit (dankclaw, frank-large, Strix Halo iGPU)
After=network-online.target
Wants=network-online.target
@@ -7,10 +7,9 @@ Wants=network-online.target
Type=simple
User=dank
Group=dank
# The engine pins staging buffers and the PLE pager's hot window; keep the
# memlock budget unbounded so an RLIMIT smaller than the pinned set cannot
# fail the load. Parity with the halogen quadlet's Ulimit=memlock=-1:-1 and
# the other LLM units on this fleet.
# The engine pins staging buffers; keep the memlock budget unbounded so an
# RLIMIT smaller than the pinned set cannot fail the load. Parity with the
# other LLM units on this fleet.
LimitMEMLOCK=infinity
Environment="FRANK_LARGE_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-large.env"
ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-large.sh
+61 -87
View File
@@ -1,34 +1,32 @@
#!/usr/bin/env bash
# Serve jcbtc/Qwen3.8-27B-IU4-Kairic-Edge as frank-large on
# Dankclaw's Strix Halo iGPU (gfx1151), using the Kairic Edge runtime
# (ciru-ai/ROCmFPX, branch kairic-edge-qwen38-27b-v1.2, commit 205a3e5) —
# the qualified IU4 (W4A4) serving path with the FFN, GDN and GDN-Output
# sidecars.
# Serve unsloth/Qwen3.8-27B-GGUF:UD-Q8_K_XL as frank-large on
# Dankclaw's Strix Halo iGPU (gfx1151), using stock llama.cpp
# (mainline, the unsloth b11030 prebuilt).
#
# Runtime: built from source (scripts/build-kairic-edge-gfx1151.sh), output at
# /home/dank/GitPackages/ROCmFPX-kairic/build-kairic/bin/llama-server. The
# Kairic Edge path is a llama.cpp build with the PromptForge sidecar env
# block (the certified runner's exact env + flags).
# Runtime: /home/dank/GitPackages/unsloth-llama-b11030/llama-server
# (mainline, ROCm gfx1151 prebuilt). The build supports the qwen3_5
# architecture and the DFlash2 drafter (llama_model_qwen35,
# llama_model_dflash, common_speculative_impl_draft_dflash).
#
# Shape: one stream, full 262144 context (the model's native window), all
# layers on the iGPU (-ngl 999). The 27B dense model (15.5 GiB GGUF + 7.99
# GiB FFN sidecar + 1.88 GiB GDN sidecar + 0.70 GiB GDN-Output sidecar)
# fits the iGPU's GTT aperture (~114 GiB).
# layers on the iGPU (--n-gpu-layers 999). The 27B dense model (31.46 GiB GGUF) + the
# DFlash2 drafter (1.14 GiB) + q8_0 KV (~8.6 GiB) + compute buffers fits the
# iGPU's GTT aperture (~114 GiB).
#
# Spec method: native MTP depth 4 (the model's MTP layers) + n-gram drafting
# (24/64/64). DFlash is NOT available for this model (no DRAFT_DFLASH spec
# type in the Kairic Edge runtime).
# Spec method: DFlash2 (a block-diffusion drafter) at depth 4. The drafter
# has a sliding window of 2048 (small draft KV, ~40 MiB).
#
# Compatibility mode: the harness needs sampling, tools and a reasoning
# budget, which the fast-greedy path rejects. COMPATIBILITY_MODE=1 enables
# them (9.7% slower per the build guide). Reasoning is enabled (auto +
# deepseek format, so thoughts land in reasoning_content).
# KV: q8_0 quantized (--cache-type-k q8_0 --cache-type-v q8_0). The unsloth b11030
# build does not support fp8 KV (allowed: f32/f16/bf16/q8_0/q4_0/q4_1/iq4_nl/q5_0/
# q5_1); q8_0 is the highest-precision 8-bit option and preserves the ~8 GiB footprint.
#
# Text-only (no mmproj) — matching the prior frank-large.
#
# The Qwen3.8 family template keeps prior thinking blocks across turns when
# preserve_thinking is set; pass --jinja and the kwarg so multi-turn agents
# do not lose the trail. A request-level chat_template_kwargs still wins.
# The template gates reasoning_effort to {xhigh, medium, low} — the harness
# folds its levels onto that set in models.yml (reasoningEffortMap).
# do not lose the trail. The template gates reasoning_effort to {xhigh,
# medium, low} — the harness folds its levels onto that set in models.yml
# (reasoningEffortMap).
#
# Tunables come from frank-large.env unless FRANK_LARGE_ENV_FILE overrides
# it; anything not set there falls through to the defaults below.
@@ -37,96 +35,72 @@ set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${FRANK_LARGE_ENV_FILE:-$SCRIPT_DIR/frank-large.env}"
if [[ -f "$ENV_FILE" ]]; then
# shellcheck disable=SC1090
set -a
source "$ENV_FILE"
set +a
fi
# --- defaults (used when the env file does not set them) ------------------
: "${RUNTIME_DIR:=/home/dank/GitPackages/ROCmFPX-kairic/build-kairic}"
: "${MODEL_DIR:=/home/dank/models/Qwen3.8-27B-IU4-Kairic-Edge}"
: "${RUNTIME_DIR:=/home/dank/GitPackages/unsloth-llama-b11030}"
: "${MODEL_DIR:=/home/dank/models/Qwen3.8-27B-UD-Q8_K_XL}"
: "${HOST:=0.0.0.0}"
: "${PORT:=7770}"
: "${CONTEXT_SIZE:=262144}"
: "${PARALLEL_SLOTS:=1}"
: "${SPEC_DRAFT_N_MAX:=4}"
: "${COMPATIBILITY_MODE:=1}"
: "${CACHE_RAM:=8192}"
TEMPLATE_FILE="${TEMPLATE_FILE:-$SCRIPT_DIR/../dankserver/frank-medium/qwen-sharp-v22.4.0.jinja}"
SERVER_BIN="$RUNTIME_DIR/bin/llama-server"
MODEL_FILE="$MODEL_DIR/Qwen3.8-27B-IU4-Kairic-Edge.gguf"
FFN_SIDECAR="$MODEL_DIR/Qwen3.8-27B-Kairic-IU4-FFN.pfs"
GDN_SIDECAR="$MODEL_DIR/Qwen3.8-27B-Kairic-IU4-GDN.pfs"
GDN_OUTPUT_SIDECAR="$MODEL_DIR/Qwen3.8-27B-Kairic-IU4-GDN-Output.pfs"
SERVER_BIN="$RUNTIME_DIR/llama-server"
MODEL_FILE="$MODEL_DIR/Qwen3.8-27B-UD-Q8_K_XL.gguf"
DRAFTER_FILE="$MODEL_DIR/Qwen3.8-27B-DFlash2-Q4_K_M.gguf"
if [[ ! -x "$SERVER_BIN" ]]; then
echo "ERROR: Kairic Edge runtime not found at $SERVER_BIN" >&2
echo " Build it with: scripts/build-kairic-edge-gfx1151.sh" >&2
echo "runtime not found at $SERVER_BIN" >&2
exit 1
fi
for f in "$MODEL_FILE" "$FFN_SIDECAR" "$GDN_SIDECAR" "$GDN_OUTPUT_SIDECAR"; do
if [[ ! -r "$f" ]]; then
echo "ERROR: missing asset: $f" >&2
echo " Fetch it with: scripts/download-frank-large-kairic.sh" >&2
for f in "$MODEL_FILE" "$DRAFTER_FILE"; do
if [[ ! -f "$f" ]]; then
echo "missing asset $f (run scripts/download-frank-large.sh)" >&2
exit 1
fi
done
# --- PromptForge env block (the Kairic Edge runner's exact env) ----------
# --- env (the unsloth build's ROCm setup) --------------------------------
export HIP_VISIBLE_DEVICES=0
export HSA_OVERRIDE_GFX_VERSION=11.5.1
export LD_LIBRARY_PATH="/opt/rocm/lib:/opt/rocm/llvm/lib:${LD_LIBRARY_PATH:-}"
export GGML_CUDA_GRAPH_OPT=0
# Fast-greedy mode (0) rejects sampling/tools/a reasoning budget; compatibility
# mode (1) enables them. The harness needs all three, so default to 1.
if [[ "$COMPATIBILITY_MODE" == "1" ]]; then
export LLAMA_TARGET_GREEDY_ARGMAX_FASTPATH=0
export PROMPTFORGE_OUTPUT_K8_STRICT_GREEDY=0
else
export LLAMA_TARGET_GREEDY_ARGMAX_FASTPATH=1
export PROMPTFORGE_OUTPUT_K8_STRICT_GREEDY=1
fi
export LLAMA_MTP_CPU_ARGMAX_FASTPATH=1
export PROMPTFORGE_TARGET_ONLY=0
export PROMPTFORGE_MODE=iu4_ffn
export PROMPTFORGE_IU4_SIDECAR="$FFN_SIDECAR"
export PROMPTFORGE_SIDECAR="$FFN_SIDECAR"
export PROMPTFORGE_IU4_HADAMARD=1
export PROMPTFORGE_IU4_SEGMENTED=0
export PROMPTFORGE_ENABLE_FFN_KEEPERS=0
export PROMPTFORGE_FFN_KEEPERS=late6
export PROMPTFORGE_ENABLE_GDN=1
export PROMPTFORGE_ENABLE_GDN_W8=0
export PROMPTFORGE_GDN_SIDECAR_OVERRIDE=1
export PROMPTFORGE_GDN_SIDECAR="$GDN_SIDECAR"
export PROMPTFORGE_GDN_IU4_HADAMARD=1
export PROMPTFORGE_ENABLE_GDN_KEEPERS=0
export PROMPTFORGE_ENABLE_GDN_OUTPUT=1
export PROMPTFORGE_GDN_OUTPUT_SIDECAR="$GDN_OUTPUT_SIDECAR"
export PROMPTFORGE_GDN_OUTPUT_KEEPERS=v3_lateq6
export PROMPTFORGE_ENABLE_SMALLM_IU4=1
export PROMPTFORGE_ENABLE_SMALLM_GDN_IU4=1
export PROMPTFORGE_ENABLE_SMALLM_GDN_OUTPUT_IU4=0
export PROMPTFORGE_ENABLE_IU4_DECODE_M2_M5=0
# --- the serve command (the Kairic Edge runner's flags, fleet-adapted) ----
# --- the serve command ---------------------------------------------------
ARGS=(
-m "$MODEL_FILE" --alias frank-large
--host "$HOST" --port "$PORT" --jinja
--chat-template-kwargs '{"preserve_thinking":true}'
--host "$HOST"
--port "$PORT"
--model "$MODEL_FILE"
--alias frank-large
--ctx-size "$CONTEXT_SIZE"
--parallel "$PARALLEL_SLOTS"
--n-gpu-layers 999
--jinja
--chat-template-file "$TEMPLATE_FILE"
-dev ROCm0 -ngl 999
-c "$CONTEXT_SIZE" -b 2048 -ub 512 -fa on -ctk f16 -ctv f16
-t 16 -tb 32 -np "$PARALLEL_SLOTS" -ctxcp 32 --cache-ram "$CACHE_RAM"
--cache-prompt --cache-idle-slots --metrics
--kairic-edge
--spec-type draft-mtp
--spec-draft-device ROCm0 --spec-draft-ngl 999
--spec-draft-type-k f16 --spec-draft-type-v f16
--spec-draft-n-max "$SPEC_DRAFT_N_MAX" --spec-draft-n-min 0
--spec-draft-p-min 0.0 --spec-draft-p-split 0.10
--spec-draft-backend-sampling
--temp 0 --top-p 1.0 --top-k 0 --min-p 0.0
--reasoning auto --reasoning-format deepseek --reasoning-budget -1
--chat-template-kwargs '{"preserve_thinking":true}'
--cache-type-k q8_0
--cache-type-v q8_0
--cache-ram "$CACHE_RAM"
# DFlash2 spec decode (a block-diffusion drafter)
--spec-type draft-dflash
--model-draft "$DRAFTER_FILE"
--spec-draft-n-max "$SPEC_DRAFT_N_MAX"
--spec-draft-n-min 0
--spec-draft-device ROCm0
# Greedy sampling (the DFlash2 drafter needs greedy verification)
--temp 0
--top-p 1.0
--top-k 0
--min-p 0.0
# Reasoning (thoughts land in reasoning_content)
--reasoning auto
--reasoning-format deepseek
--reasoning-budget -1
)
exec "$SERVER_BIN" "${ARGS[@]}"