Skip to content

Prompt cache internals

The prompt cache chooses its tier for each architecture and exposes counters that show whether reuse works. The user guide is Prompt cache.

Which tier serves which architecture

The cache routes each model by its cache stack's shape once, at the first probe, and logs the result as APC tier:, or as APC OFF for this model when no tier serves it. That log line is the source of truth for a model. model_apc_mode in mlx_vlm.apc picks the block or exact tier, and _ckpt_active in gmlx/spec/engine.py moves an exact-mode stack with gated-delta or sliding-window layers to the checkpoint tier.

Cache shape Example Tier
Plain KV, dense or MoE llama block
Recurrent layers with KV layers qwen35 ckpt
Sliding-window attention gemma4 ckpt
CacheList or pure recurrent falcon-h1 exact
A stack that no tier can split minimax-m3 with its indexer None, with a logged warning.

Hits happen on every tier, and the tiers differ in storage layout. Under kvarn KV, a dense model uses the exact tier, because the block tier cannot split kvarn's 128-token records. A checkpoint-tier stack keeps its tier and stores kvarn records. Entries and disk skeletons are keyed to the kvarn width and tail, so a restart that switches between stock and kvarn misses instead of reading a stale format.

The cache layers

A request passes through five layers in order. Their switches are all on by default, but only the prefix layer works with cache: off, and the other four need cache.enabled. The prefix layer and the drafter sidecar exist only for speculative models, and checkpoints only for checkpoint-tier models.

  • Prefix layer: An in-memory LRU holds post-prefill KV and hidden state. A request that shares a prefix with an earlier one skips that prefill.
  • Shared pools: The block, exact and disk lookups fill the prompt cache before prefill, so a request can warm-start from memory or from the SSD tier. The longest match wins.
  • Retirement: At request finish the whole sequence, prompt plus reply, is stored back, so a conversation's next turn warm-starts past the whole finished turn.
  • Drafter sidecar: A native MTP head keeps a separate KV. A warm target with an empty drafter KV decodes at degraded acceptance, so a small sidecar entry saves the drafter KV beside the target entry, and a warm hit restores both.
  • Checkpoints: Checkpoint-tier models save checkpoints along a prefill and while generating, plus targeted ones at the end of the system prompt, one token before the prompt end and at the predicted next-turn boundary. The system-prompt checkpoint lets parallel agents that share a prompt restore from it. A batched prefill takes no checkpoints, so prompt prefill on these models runs one request at a time.

Each layer's switches are listed in the prompt cache section of Debug switches.

Checkpoint-tier counters

GET /v1/cache/stats carries the ckpt_* fields for each model. They change only on checkpoint-tier architectures, so all zeros on a block-tier or exact-tier model is normal. On a checkpoint-tier model they are the fields to judge prefix reuse from. Ratios built on the stock counters mislead there, because a checkpoint lookup counts a hit on success but records nothing on a miss.

This table gives each counter and what a change in it means.

Field Meaning
ckpt_stores Each prefix saved for reuse adds one. Zero after a few requests on a checkpoint-tier model means nothing is being cached.
ckpt_hits, ckpt_matched_tokens These count warm starts from a saved prefix and the prompt tokens they skipped.
ckpt_declines Each save the server skips counts under its reason. When every request lands under one reason, reuse does not work for that traffic.
ckpt_missed_adoptions Each request that matched a saved prefix but could not use it adds one. Any growth indicates a bug.
ckpt_pool_evictions Each prefix discarded to make room adds one. Evictions are normal on long sessions.
ckpt_skeleton_writes Each save mirrored to the disk tier adds one. Zero with disk on means a restart starts cold.
sidecar_writes Each drafter cache entry saved beside its target entry adds one.
retire_fallback_suppressed Each retirement store skipped because the predicted next-turn render diverged adds one. The turn checkpoint covers it.

The server warns once per model when requests armed for checkpoints store nothing, and once when lookups match a saved prefix but adopt nothing. Either warning means prefix reuse does not work for that model, so file an issue with the /v1/cache/stats snapshot.