The TTM_TARGET_MIB guard I added on 1a6a78e was written `:"...` (no space), so
bash parsed the expansion as one word and executed `cmd :204800` as the
command `:204800`: exit 127 `command not found` on every start, before the
ttm gate ran. Caught on the deploy: the unit crash-looped 34 times in ~4 min
with exactly that journal line.
probe-frank-archon-kv.py now reads the slot count, window and KV dtype from the
deployed env on the host, so probe B exercises the capacity contract that is
actually live (4 x 200,000 today) instead of a hardcoded pair, and each probe's
marker starts with a unique word because show() scores the hit on that word.
177 lines
7.5 KiB
Python
Executable File
177 lines
7.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""probe-frank-archon-kv.py — verify the seqs x max-model-len KV contract on a
|
|
frank-archon (radiance) service. Run it ON dankserver: it talks to
|
|
127.0.0.1:7770 and builds its prompts from the repo checkout there.
|
|
No scratch container, no mask: the service under test is the service.
|
|
|
|
ssh dankserver 'python3 /home/dank/serverAppConfigs/scripts/probe-frank-archon-kv.py'
|
|
|
|
Three probes, in increasing cost:
|
|
A decode at depth — one ~51k-token chat completion, 512 tokens, greedy.
|
|
Reports the engine's own `timings` (prefill, decode, draft acceptance).
|
|
The bench law applies: >=128 generated tokens, greedy, real text.
|
|
B the capacity contract — as many CONCURRENT chats of ~197k prompt tokens as
|
|
the deployed `FRANK_ARCHON_MAX_SEQS` (read from the repo env file on this
|
|
host), each carrying a unique marker at its head. All must answer their own
|
|
marker. This is what the endpoint owes: N full-height conversations
|
|
resident at once, exact retrieval in every one of them.
|
|
C prefix-cache restore — probe A's cold prompt again, so the disk tier has
|
|
to give the blocks back (cache_n > 0, TTFT collapses).
|
|
|
|
PROBE LAW (learned the hard way, twice): radiance routes the thinking block to
|
|
`reasoning_content`, so a chat request whose budget is spent inside
|
|
`<think>` returns EMPTY `content` with `finish_reason: stop`. A probe that
|
|
reads `content` alone scores 0/2 on a healthy server. Every request here sets
|
|
chat_template_kwargs.enable_thinking=false and reads both fields.
|
|
|
|
A/B method for a shape change (no scratch container): write a variant env file
|
|
on the host, point a transient systemd drop-in at it
|
|
(`Environment=FRANK_ARCHON_ENV_FILE=...` — drop-ins are read AFTER the unit's
|
|
own Environment= lines, so they win), restart, probe, delete the drop-in,
|
|
daemon-reload, restart. `systemctl mask` is not available: the unit file in
|
|
/etc/systemd/system is a relink symlink into the checkout.
|
|
"""
|
|
import json
|
|
import glob
|
|
import time
|
|
import urllib.request
|
|
import concurrent.futures
|
|
|
|
BASE = "http://127.0.0.1:7770"
|
|
MODEL = "frank-archon"
|
|
REPO = "/home/dank/serverAppConfigs"
|
|
GLOBS = [f"{REPO}/scripts/*.sh", f"{REPO}/serverAppConfigs-docs/**/*.md"]
|
|
|
|
|
|
def post(payload, timeout=1800):
|
|
req = urllib.request.Request(
|
|
BASE + "/v1/chat/completions",
|
|
data=json.dumps(payload).encode(),
|
|
headers={"Content-Type": "application/json"},
|
|
)
|
|
t0 = time.time()
|
|
with urllib.request.urlopen(req, timeout=timeout) as r:
|
|
body = json.loads(r.read().decode())
|
|
hdr = dict(r.headers.items())
|
|
return body, time.time() - t0, hdr
|
|
|
|
|
|
def get(path, timeout=60):
|
|
with urllib.request.urlopen(BASE + path, timeout=timeout) as r:
|
|
return json.loads(r.read().decode())
|
|
|
|
|
|
def corpus(min_chars):
|
|
files = []
|
|
for g in GLOBS:
|
|
files.extend(sorted(glob.glob(g, recursive=True)))
|
|
out, total = [], 0
|
|
for f in files:
|
|
try:
|
|
t = open(f, encoding="utf-8", errors="replace").read()
|
|
except OSError:
|
|
continue
|
|
out.append(t)
|
|
total += len(t)
|
|
if total > min_chars:
|
|
break
|
|
return "".join(text for text in out)
|
|
|
|
|
|
def prompt(text, chars, salt, fact):
|
|
"""Real repo text, salted so the prefix cache cannot answer, marker planted
|
|
near the head, question at the tail."""
|
|
head = f"\n[SALT {salt}] confidential marker: {fact}\n"
|
|
body = text + text
|
|
p = (head + body * max(1, chars // len(body)))[:chars]
|
|
return p + "\n\nQuestion: what is the confidential marker in the " \
|
|
"SALT block near the top of this document? Answer with the marker " \
|
|
"alone, no other words.\n"
|
|
|
|
|
|
def chat(text, chars, salt, fact, max_tokens):
|
|
return {"model": MODEL,
|
|
"messages": [{"role": "user",
|
|
"content": prompt(text, chars, salt, fact)}],
|
|
"max_tokens": max_tokens, "temperature": 0.0,
|
|
"chat_template_kwargs": {"enable_thinking": False}}
|
|
|
|
|
|
def show(tag, j, wall, expect=None):
|
|
tim, u = j.get("timings") or {}, j.get("usage", {})
|
|
ch = (j.get("choices") or [{}])[0]
|
|
msg = ch.get("message") or {}
|
|
txt = (msg.get("content") or msg.get("reasoning_content") or "").strip()
|
|
acc = (tim["draft_n_accepted"] / tim["draft_n"]) if tim.get("draft_n") else None
|
|
line = (f"[{tag}] prompt={u.get('prompt_tokens')} gen={u.get('completion_tokens')} "
|
|
f"wall={wall:.1f}s prefill={tim.get('prompt_per_second', 0):.0f} t/s "
|
|
f"decode={tim.get('predicted_per_second', 0):.1f} t/s "
|
|
f"draft={tim.get('draft_n')}/{tim.get('draft_n_accepted')} "
|
|
f"accept={acc if acc is None else round(acc, 4)} "
|
|
f"cache_n={tim.get('cache_n')} finish={ch.get('finish_reason')}")
|
|
if expect:
|
|
hit = expect.split("-")[0] in txt.lower()
|
|
line += f" expected={expect} answered={txt[:80]!r} HIT={hit}"
|
|
print(line)
|
|
|
|
|
|
def deployed_shape():
|
|
"""The contract is the deployed env, not a constant: slot count and window
|
|
come from the checkout on this host, so the probe cannot drift from the
|
|
service it is testing (the 2026-10-13 shape is 4 x 200,000; before that
|
|
2 x 200,000, and the fp8 era 3 x 200,000)."""
|
|
shape = {}
|
|
with open(f"{REPO}/scripts/frank-archon.env") as fh:
|
|
for line in fh:
|
|
line = line.strip()
|
|
if line.startswith("FRANK_ARCHON_") and "=" in line:
|
|
key, _, val = line.partition("=")
|
|
shape[key] = val.strip().strip('"')
|
|
return (int(shape["FRANK_ARCHON_MAX_SEQS"]),
|
|
int(shape["FRANK_ARCHON_MAX_MODEL_LEN"]),
|
|
shape["FRANK_ARCHON_KV_DTYPE"])
|
|
|
|
|
|
def main():
|
|
seqs, maxlen, dtype = deployed_shape()
|
|
print(f"shape: {seqs} x {maxlen:,} tokens, kv {dtype}")
|
|
print("health:", json.dumps(get("/health")))
|
|
print("models:", [m.get("id") for m in get("/v1/models").get("data", [])])
|
|
deep = corpus(160_000)
|
|
wide = corpus(1_100_000)
|
|
|
|
# 3.28 chars/token is the measured ratio of this corpus through the
|
|
# froggeric template (646,000 chars -> 196,919 tokens at the 2026-10-11
|
|
# deploy); 98.5 % of the window leaves the response room under the ceiling.
|
|
chars = int(0.985 * maxlen * 3.28)
|
|
# Each marker's FIRST hyphen-separated word is unique, because show()
|
|
# scores the hit on that word — a shared prefix would score every slot HIT
|
|
# on any answer that says the word.
|
|
words = ["cobalt", "quartz", "basalt", "ember", "willow", "cobweb", "flint"]
|
|
facts = {f"B{i+1}": f"{words[i % len(words)]}-flag-{(i + 1) * 137}"
|
|
for i in range(seqs)}
|
|
bodies = {s: chat(wide, chars, s, f, 24) for s, f in facts.items()}
|
|
print(f"[B] {len(bodies['B1']['messages'][0]['content']):,} chars per "
|
|
f"request, x{seqs} concurrent")
|
|
|
|
# A + C first (cheap), B last (the expensive set).
|
|
for tag, salt in (("A-cold", "A1-9137"), ):
|
|
j, wall, _ = post(chat(deep, 140_000, salt, "zephyr-lantern-7", 512))
|
|
show(tag, j, wall, "zephyr-lantern-7")
|
|
t0 = time.time()
|
|
with concurrent.futures.ThreadPoolExecutor(max_workers=seqs) as ex:
|
|
futs = {s: ex.submit(post, b) for s, b in bodies.items()}
|
|
for s, fut in futs.items():
|
|
try:
|
|
j, wall, _ = fut.result()
|
|
show(f"B-{s}", j, wall, facts[s])
|
|
except Exception as e: # a preemption/timeout lands here
|
|
print(f"[B-{s}] FAILED after {time.time() - t0:.1f}s: {e!r}")
|
|
print(f"[B] total {time.time() - t0:.1f}s")
|
|
j, wall, _ = post(chat(deep, 140_000, "A1-9137", "zephyr-lantern-7", 512))
|
|
show("C-prefix-hit", j, wall, "zephyr-lantern-7")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|