antirez / antirez/ds4

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

Open
#816 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
C
Stars
22.3k
Forks
2.1k
Avg merge
1d 3h
Merged PRs (30d)
4

Description

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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.