Stateless chat clients never extend the live KV session on Flash/Metal: measurements, the anchor that saves them, and the replay recipe that fixes it
- Vorherrschende Sprache
- C
- Sterne
- 22.3k
- Forks
- 2.1k
- Ø Merge
- 1 T. 3 Std.
- Gemergte PRs (30 T.)
- 4
Beschreibung
This is an analysis with numbers, not a bug report. It may be worth a paragraph in the README's client recipes; happy to send that as a PR if wanted.
**Setup.** Mac Studio M3 Ultra 512 GB, Metal, DeepSeek V4 Flash MXFP4 0731, `84cc882`, `--batched-session 4 --ctx 300000 --kv-disk-dir ... --kv-disk-space-mb 16384`. Client: a Telegram group bot on OpenAI chat/completions, `think:false`. Each request is the room's transcript (system + memories + N user turns), ~7-9k tokens, plus whatever arrived since the last call. Verdicts run every few seconds when the room is active.
**Observation 1: the live session was never reused.** 787 out of 787 requests logged `live kv cache miss ... reason=token-mismatch`, e.g. `live=6752 prompt=6776 common=6676`. After a call the slot holds `prompt + sampled output`; the next prompt is `prompt + new user turns`, which shares `common` = the old prompt and then diverges, and `generate_job` takes the live session only on `common == old_pos && prompt.len >= old_pos`. The rewind path (`live_prefix_rewind_target`) is GLM-only and covers exact-prefix retries (#698/#710), not this. So every chat request fell through to disk.
**Observation 2: on disk, only the cold anchor can help such a client**, because evict dumps end in sampled output too. The cold anchor cuts at `kv_cache_chat_anchor_pos` (last user turn before the first assistant turn), so in a room where the bot spoke early it sits at ~5k of ~7k tokens: prefill from the anchor is ~1.7-3k tokens (~5-8 s), and once the disk was full the anchor was evicted on every store (#814).
**Observation 3: replaying the assistant turn byte-for-byte fixes it completely**, on both API shapes, without engine changes:
- tool call: replay `{"role":"assistant","tool_calls":[{id, function:{name, arguments}}]}` + a `tool` result; ds4 re-renders the exact sampled DSML from the tool-id map → `cached_tokens` = prior prompt + generation, e.g. `ctx=8228..8277:49`, only the new turn prefilled.
- plain text: replay `{"role":"assistant","content": }` → same, `ctx=7527..7558:31`.
Warm request cost went from ~17-20 s (full prefill) to 2.3-6 s depending on output length; `cached_tokens` in `usage` is the direct witness. The bot now stores its own last output and sends it back as the assistant turn, which is trivial once you know it is required.
**Observation 4, for anyone choosing an output shape:** the same short structured decision costs ~155 generated tokens as a DSML tool call (a 159-char argument object, ~105 tokens of `<|DSML|...>` scaffolding) versus ~45 tokens as one JSON line written as plain assistant text; at 44 t/s that is 3.5 s vs 1 s per call. With replay both shapes extend the live session equally.
**Small prefill cost, for reference:** a live-session extension of N new tokens costs ~0.3 s + 3.4 ms/token here (23 → 0.31 s, 102 → 0.63 s, 342 → 1.47 s), the ~0.3 s being the MoE expert scan (any batch beyond ~20 tokens touches nearly every expert), with a step at 32 tokens (tile). Raising `callback_split` in `metal_graph_prefill_layer_major` from 32 tokens changed nothing, so it is not the per-layer command buffers.
**Suggestion (docs).** A short "keeping the live session warm from a stateless chat client" note: send back the previous assistant turn exactly as received; check `usage.prompt_tokens_details.cached_tokens` ≈ prior prompt+completion; prefer plain-text structured output over tool calls when the output is short and no tool result is needed. And a line next to `kv_cache_chat_anchor_pos` explaining that for a client that cannot replay, the anchor is the only disk hit it will ever get.
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.