Files
serverAppConfigs/tests/frank_small_unit.bats
T
Frank dab483c79f frank-large: cutover to vLLM TheRock gfx1151, MiMo-V2.6 BF16 + cross-model MTP
Engine: digest-pinned docker.io/kyuz0/vllm-therock-gfx1151 (vLLM 0.30,
TheRock ROCm 10, Triton GDN kernels); the serve script gates the image by
REPO DIGEST, the payload (config + index + exact shard count), the spec
draft (BOTH weight layouts: single model.safetensors for dflash, sharded
index for the Qwen3.5-9B MTP head), and whole /dev/dri, sweeps the bench
identity vllm-mimo, then exec podman wait so a container death trips
Restart=always. Shape: 4 x 262144 at util 0.45 (KV grows only on the 8
full-attention layers), MTP 2 draft tokens.

Measured 2026-10-01 window (frank-large stopped): base 13.1 t/s decode
(~94% of the LPDDR5x BF16 roof), DFlash-8 17.4, MTP-2 19.8 with 71%/44%
per-position acceptance - the cross-model head works because vLLM reads the
draft from a separate path while MiMo ships zero mtp.* tensors. Spec arms
do not preserve greedy bf16 text (batched-verify reduction order).

The seam was rewritten around the new laws (18 tests): unit self-healing,
digest gating, engine wiring, the measured spec table, the retired gufo
gates, the deployer MainPID check. frank_small port test follows the
PORT -> FRANK_LARGE_PUBLISH rename. Rollback: tag frank-large-gufo-q4kxl-20261001.
2026-10-01 18:48:32 -07:00

136 lines
6.6 KiB
Bash
Executable File

#!/usr/bin/env bats
# Seam: the frank-small SGLang root unit on dankclaw.
# frank-small is the RTX 3090 endpoint; since 2026-10-01 it serves
# letechlead/MiMo-V2.6-Distill-Qwen-9B-INT8-W8A16-AutoRound (qwen3_5 hybrid,
# INT8 W8A16 AutoRound, in-checkpoint vision) via SGLang in a
# python3.12 venv. This seam pins what the flags alone do not say: boot
# persistence for a NATIVE runtime (no generated quadlet is involved), the
# repo-relative paths the unit executes, the separate client port, the
# no-spec-decode gate (the checkpoint ships NO mtp.* tensors, so a NEXTN
# draft would be an uninitialised ghost model), the 8-shard payload gate,
# and the KV budget: fp8 KV at 0.98 utilization whose 4-slot grid must fit
# the card's 24 GiB.
load helpers
UNIT="$REPO_ROOT/scripts/frank-small.service"
SERVE="$REPO_ROOT/scripts/serve-frank-small.sh"
ENV_FILE="$REPO_ROOT/scripts/frank-small.env"
DOWNLOAD="$REPO_ROOT/scripts/download-frank-small.sh"
REMOTE_DEPLOY="$REPO_ROOT/scripts/deploy-remote.sh"
ENV_LARGE="$REPO_ROOT/scripts/frank-large.env"
@test "frank-small unit, serve script, env and download script exist in scripts/" {
[ -f "$UNIT" ]
[ -f "$SERVE" ]
[ -f "$ENV_FILE" ]
[ -f "$DOWNLOAD" ]
}
@test "frank-small is boot-persistent, self-healing and unprivileged" {
grep -q '^WantedBy=multi-user.target$' "$UNIT"
grep -q '^Restart=on-failure$' "$UNIT"
grep -q '^RestartSec=' "$UNIT"
grep -q '^User=dank$' "$UNIT"
grep -q '^LimitMEMLOCK=infinity$' "$UNIT"
}
@test "frank-small runs the repo's own serve script with the repo's env" {
grep -q '^ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-small.sh$' "$UNIT"
grep -q 'FRANK_SMALL_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-small.env' "$UNIT"
}
@test "frank-small serves the MiMo-V2.6 checkpoint from the self-contained SGLang venv" {
grep -q '^SGLANG_BIN=/home/dank/GitPackages/sglang-cu128/bin/sglang$' "$ENV_FILE"
grep -q '^MODEL_DIR=/home/dank/models/MiMo-V2.6-Distill-Qwen-9B-INT8-W8A16-AutoRound$' "$ENV_FILE"
grep -q -- '--model-path "$MODEL_DIR"' "$SERVE"
# The AutoRound checkpoint declares its quant method; SGLang must load it
# as auto-round, not re-quantize online.
grep -q '^QUANTIZATION=auto-round$' "$ENV_FILE"
grep -q -- '--quantization "$QUANTIZATION"' "$SERVE"
}
@test "frank-small runs WITHOUT speculative decoding (no MTP weights shipped)" {
# The config declares mtp_num_hidden_layers=1 but the safetensors index
# carries zero mtp.* tensors (base and INT8 conversion alike, verified
# 2026-10-01). SGLang would draft with an uninitialised 4.24 GiB model
# at 0% acceptance; the serve script must not pass any speculative flag.
! grep -q -- '--speculative-' "$SERVE"
! grep -q '^SPEC_' "$ENV_FILE"
}
@test "frank-small carries the fleet's image path, and only it" {
# The vision tower is baked into the safetensors (qwen3_5_vision): no
# separate projector file, and the serve script must not look for one.
! grep -q '^MMPROJ_PATH=' "$ENV_FILE"
! grep -q -- '--mmproj' "$SERVE"
! grep -q '^ENABLE_VISION=1$' "$ENV_LARGE"
}
@test "frank-small's slot grid is fp8 KV and fits the measured card budget" {
local ctx running kv ssmdtype mambasize required
ctx="$(sed -nE 's/^CONTEXT_LENGTH=([0-9]+)$/\1/p' "$ENV_FILE")"
running="$(sed -nE 's/^MAX_RUNNING=([0-9]+)$/\1/p' "$ENV_FILE")"
kv="$(sed -nE 's/^KV_DTYPE=(.+)$/\1/p' "$ENV_FILE")"
ssmdtype="$(sed -nE 's/^MAMBA_SSM_DTYPE=(.+)$/\1/p' "$ENV_FILE")"
mambasize="$(sed -nE 's/^MAX_MAMBA_CACHE_SIZE=([0-9]+)$/\1/p' "$ENV_FILE")"
[ -n "$ctx" ] && [ -n "$running" ] && [ -n "$kv" ]
# fp8 KV is the only dtype whose 16 KiB/token makes four slots fit;
# 64 tokens per MiB of pool.
case "$kv" in fp8*) ;; *) return 1 ;; esac
# The DeltaNet ssm state pool is the hidden fit cost: the engine
# autofills mamba slots to spend a fixed ~5.1 GB budget, so dtype
# alone does not shrink it — the SLOT COUNT must be pinned (bf16,
# bounded). These two gates are what make the measured pool below
# exist; change either and re-measure the pool at boot.
[ "$ssmdtype" = "bfloat16" ]
[ -n "$mambasize" ] && [ "$mambasize" -ge "$running" ] && [ "$mambasize" -le 64 ]
grep -q -- '--mamba-ssm-dtype "$MAMBA_SSM_DTYPE"' "$SERVE"
grep -q -- '--max-mamba-cache-size "$MAX_MAMBA_CACHE_SIZE"' "$SERVE"
# The slot window is inside the model's native 262144 window.
[ "$ctx" -le 262144 ]
# The grid must equal the pinned pool. The engine's autofit picks a
# bigger pool (687,388 tokens) but leaves ~0.53 GB — Triton's
# deferred kernel loads take most of it and eager-prefill activations
# then OOM on the first long request. 606,208 (4 x 151,552) is the
# verified ceiling: 170k single request and 4 concurrent 54k requests
# run against it (2026-10-01). A weights or mamba-flag change moves
# this number; re-measure before trusting the grid.
pinned_pool=606208
required=$((running * ctx))
[ "$required" -le "$pinned_pool" ]
grep -q -- '--max-total-tokens "$MAX_TOTAL_TOKENS"' "$SERVE"
grep -q -- '--disable-prefill-cuda-graph' "$SERVE"
}
@test "frank-small runs on its own port beside frank-large's" {
grep -q '^LISTEN_PORT=7771$' "$ENV_FILE"
grep -q '^ALIAS=frank-small$' "$ENV_FILE"
grep -q -- '--port "$LISTEN_PORT"' "$SERVE"
grep -q -- '--host "$BIND_ADDR"' "$SERVE"
# The two endpoints cannot share a port: frank-large publishes
# 7770:7770 through podman, this one binds 7771 natively. (The
# frank-large vLLM cutover renamed PORT to FRANK_LARGE_PUBLISH.)
grep -q '^FRANK_LARGE_PUBLISH=7770:7770$' "$ENV_LARGE"
! grep -q '^FRANK_LARGE_PUBLISH=7771' "$ENV_LARGE"
}
@test "the deploy handler installs the root unit and refuses a missing payload" {
grep -q 'deploy_llm_small' "$REMOTE_DEPLOY"
grep -q 'relink_unit frank-small' "$REMOTE_DEPLOY"
grep -q 'systemctl enable frank-small.service' "$REMOTE_DEPLOY"
grep -q 'systemctl restart frank-small.service' "$REMOTE_DEPLOY"
grep -q 'MiMo-V2.6-Distill-Qwen-9B-INT8-W8A16-AutoRound' "$REMOTE_DEPLOY"
grep -q 'refusing to start a unit that cannot load' "$REMOTE_DEPLOY"
grep -q '127.0.0.1:7771/health' "$REMOTE_DEPLOY"
}
@test "frank-small's serve script refuses to start on a half-finished download" {
# The payload is 8 safetensors shards; the serve script counts them and
# the download script pins each one's LFS sha256.
grep -q 'frank-small: missing' "$SERVE"
grep -q 'expected 8 safetensors shards' "$SERVE"
grep -q 'model-00001-of-00008.safetensors' "$DOWNLOAD"
grep -q 'model-00008-of-00008.safetensors' "$DOWNLOAD"
}