Files
esh-pfi-infrastructure/services/mog-sec-tuning/deep_ctx_probe.py
T
vh b8435ceb6f Set mog-sec's context to a measured ceiling so it refuses instead of dying
Operator: "loading up the context killed sec again." That reproducer is what
finally made the failure legible, and it showed the previous four fixes had all
been aimed at the wrong quantity.

What the KV pool can hold and what the card can process at depth are different
numbers. Cutting context 420k to 384k to 320k, pinning the KV in bytes, and
dropping the prefill chunk from 16384 to 4096 all sized the pool. The crashes were
governed by the transient needed to process a prefill chunk against a quarter
million tokens of resident KV, which scales with depth and not with pool size.
Each change helped and none fixed it.

Bisected against the real reproducer, with a non-repeating prompt because prefix
caching would let a repeated one hash to cached blocks and never prefill deep:

  113,247 prompt tokens  SURVIVED (27 s)
  200,088 prompt tokens  SURVIVED (174 s)
  ~285,000 prompt tokens ENGINE DIED, HTTP 500, container restarted

The sustainable ceiling therefore sits between 200k and 285k with gen idle, and
gen shares the card with its load uncontrolled, so 163,840 takes about 20% margin
under the proven-good depth rather than sitting at the measured edge.

The ceiling's purpose is the refusal. Verified after the change: a 149,073-token
request serves in 41 s, and requests at both 200k and the ~285k depth that killed
the engine now return a clean 400 naming the limit in under a second with the seat
untouched. A seat that refuses what it cannot serve is strictly better than one
that dies trying.

Concurrency went 1.03x to 2.09x. The compose header's "served at native 262K" was
never actually deliverable on a shared card; it had simply not been exercised at
depth until today.

The probe is committed rather than described, so the ceiling can be re-measured
when the card's tenancy changes.
2026-09-10 16:04:16 -07:00

80 lines
3.6 KiB
Python

"""Reproduce the operator's kill: fill the context and see whether the seat survives.
"Loading up the context killed sec again" is a reproducer, and a config change that
has not been run against the reproducer is a hope rather than a fix. The crash dumps
put the failures at num_computed_tokens 151,728 and then 266,832, so the probe walks
UP through those depths and reports which one, if any, takes the seat down.
⚠ The text must be NON-REPEATING. Prefix caching is on, so a prompt built by repeating
a paragraph would hash to cached blocks after the first occurrence and never actually
prefill deep -- the probe would pass while proving nothing. Every word here comes from
a seeded RNG over a large vocabulary, so no block repeats and every token is real work.
Reports the seat's restart count before and after, because the failure mode is the
ENGINE dying: a request can return a 500 while the seat stays up, and it can also
succeed while the seat is already restarting from someone else's request. The restart
count is what distinguishes them.
"""
import json
import random
import subprocess
import sys
import time
import urllib.error
import urllib.request
SEAT = "http://10.250.50.54:8019/v1/chat/completions"
HOST = "infra-ops@10.250.50.54"
WORDS = [f"{a}{b}" for a in
"ash birch cedar dale elm fern gale hollow iron juniper kestrel larch marsh "
"north oak pike quarry rowan slate thorn upland vale willow yarrow".split()
for b in ("", "wood", "field", "stone", "water", "ridge", "moor", "gate",
"hill", "brook", "fell", "reach")]
def restarts():
out = subprocess.run(
["ssh", "-o", "ConnectTimeout=10", HOST,
"docker inspect vllm-mog-sec --format '{{.RestartCount}}'"],
capture_output=True, text=True, timeout=40)
return out.stdout.strip() or "?"
def build(n_words, seed):
r = random.Random(seed)
return " ".join(r.choice(WORDS) for _ in range(n_words))
# ~1.35 Qwen tokens per word for this vocabulary; depths chosen to bracket both crashes.
for label, n_words in [("~60k tok", 44_000), ("~150k tok (crash 1 depth)", 111_000),
("~270k tok (crash 2 depth)", 200_000)]:
before = restarts()
body = {"model": "mog-sec-27b",
"messages": [{"role": "user", "content":
"Here is a word list. Reply with only the last word of it.\n\n"
+ build(n_words, hash(label) & 0xffff)}],
"max_tokens": 16, "temperature": 0,
"chat_template_kwargs": {"enable_thinking": False}}
payload = json.dumps(body).encode()
print(f"\n== {label} ({n_words:,} words, {len(payload)/1e6:.1f} MB) "
f"restarts before={before}", flush=True)
t0 = time.time()
try:
req = urllib.request.Request(SEAT, data=payload,
headers={"Content-Type": "application/json"})
d = json.load(urllib.request.urlopen(req, timeout=900))
pt = d["usage"]["prompt_tokens"]
print(f" OK {pt:,} prompt tokens in {time.time()-t0:.0f}s "
f"answer={d['choices'][0]['message'].get('content')!r}")
except urllib.error.HTTPError as e:
print(f" HTTP {e.code} after {time.time()-t0:.0f}s: {e.read().decode()[:180]}")
except Exception as e:
print(f" {type(e).__name__} after {time.time()-t0:.0f}s: {str(e)[:180]}")
time.sleep(5)
after = restarts()
verdict = "SEAT SURVIVED" if after == before else f"*** SEAT DIED (restarts {before} -> {after})"
print(f" {verdict}")
if after != before:
sys.exit(1)
print("\nAll depths completed with no engine restart.")