feat(prefix-cache): sticky tools pins + DiffPin for warm agent TTFT - #567
feat(prefix-cache): sticky tools pins + DiffPin for warm agent TTFT#567davidmroth wants to merge 4 commits into
Conversation
Pin the system+tools head on tool-heavy cold turns (Python thin-pin parity) and spare those entries during eviction so multi-chat deepen snaps cannot thrash Hermes-sized tool prefixes.
Diff the tools/system head against recent traffic, relocate small ephemeral hunks after the stable prefix+suffix, and pin that contiguous blob so session clocks no longer force a full head re-prefill.
Token-level prefix|suffix|middle peels were still applied with rearrange=0, which can scramble tool-schema JSON and empty post-tool completions. Default path is now pin-end annotate + sticky protect only.
There was a problem hiding this comment.
Pull request overview
This PR extends the server-side prefix-cache system to better support long-lived, tool-heavy agent traffic by (1) making the tools/system snapshot “sticky” via protected eviction behavior and (2) introducing the Pin-Friendly Prompt Processor (PPP / DiffPin) to improve cache reuse when small volatile text appears inside an otherwise-stable prompt head.
Changes:
- Add protected (sticky) tool-boundary pins and update eviction/snapshot-boundary selection to prefer the tools boundary until restored.
- Implement PPP (DiffPin): LCP-based
pin_endannotation and optional token-level diff/rewrite (gated), plus request-time tracking of recent tool prefixes. - Add unit tests and documentation for PPP/DiffPin behavior and the protected pin policy.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| server/test/test_server_unit.cpp | Adds unit tests for tools-boundary preference, PPP helpers, rewrite behavior, and protected-eviction behavior. |
| server/src/server/prefix_cache.h | Extends prefix-cache APIs for protected eviction, tools-boundary preference, and forced pin cuts. |
| server/src/server/prefix_cache.cpp | Implements protected eviction behavior, tools-boundary selection, forced-cut snapshotting, and direct prefix matching for non-boundary pins. |
| server/src/server/pin_friendly_prompt.h | Introduces PPP public interfaces/types for LCP pinning, diff/rewrite, and prefix memory helpers. |
| server/src/server/pin_friendly_prompt.cpp | Implements PPP (DiffPin) algorithms: LCP/suffix diffing, safe cut selection, optional rewrite, and prefix-ring maintenance. |
| server/src/server/http_server.h | Adds PPP configuration knobs and request pin-end plumbing plus recent tool-prefix storage. |
| server/src/server/http_server.cpp | Wires PPP into request handling (optional system-tail peel) and generation-cache preparation (pin-only by default; rewrite gated). |
| server/docs/TOOL_PREFIX_CACHE.md | Documents protected tools-boundary snapshot behavior and references PPP/DiffPin. |
| server/docs/PIN_FRIENDLY_PROMPT.md | Adds new PPP/DiffPin documentation explaining motivation and behavior. |
| server/CMakeLists.txt | Adds pin_friendly_prompt.cpp to the build. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Pin-Friendly Prompt Processor (PPP): LCP pin_end + optional rearrange. | ||
| // See docs/PIN_FRIENDLY_PROMPT.md. Env: DFLASH_PPP=0|1, | ||
| // DFLASH_PPP_REARRANGE=0|1, DFLASH_PPP_LCP_WINDOW=N. |
| @@ -0,0 +1,112 @@ | |||
| // Pin-Friendly Prompt Processor (PPP) | |||
| // | |||
| // Diffes the tools/system head against recent traffic, isolates the volatile | |||
There was a problem hiding this comment.
All reported issues were addressed across 10 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Cut the rewrite head at the first end-of-message marker (not after the next role-start), no-op when chat boundaries are missing, and key full-prompt snapshots by the effective tokens after a DiffPin rewrite.
|
@davidmroth thanks for the PR! One problem seems that > DFLASH_PPP=0 disables PPP annotation/rewrite, but not sticky/protected tool-boundary pinning, despite the docs saying it controlsboth. Could you gate prefer_tools_boundary on config_.ppp_enabled? |
Summary
Follow-up to #492 (native tool-prefix caching).
That work made system/tools KV reusable across growing agent turns. Two gaps
remained in multi-chat / long-lived tool-heavy agent traffic:
system+tools head and mark those entries protected so deepen snaps from
other sessions cannot thrash large tool prefixes out of the slot pool.
inside an otherwise-stable head, exact prefix match fails and the engine
re-prefills tens of thousands of tokens. DiffPin diffs the head against
recent traffic, annotates a sticky
pin_end, and (optionally) relocates theephemeral middle after the shared prefix+suffix so the contiguous stable
blob can be pinned.
No conversation-id header or client protocol change is required. Defaults keep
DiffPin annotation on and token rearrange off (safe for tool JSON):
DFLASH_PPP1DFLASH_PPP_REARRANGE0DFLASH_PPP_LCP_WINDOW/MIN_PIN/MAX_EPHEMERALDocs:
server/docs/PIN_FRIENDLY_PROMPT.md(linked fromTOOL_PREFIX_CACHE.md).usage.timingscontinues to reportcache_hit,cached_prefix_tokens,prefilled_tokens, andeffective_prompt_tokens.Why rearrange defaults off
An early DiffPin wiring applied token-level rewrite even when
rearrange=0.That could peel a mid-head island out of a large tools/system prefix
(
diff-rewrite … middle≈170on ~17–18K-token tool schemas) and scramble toolJSON enough to yield empty post-tool completions. Gating rewrite behind
DFLASH_PPP_REARRANGE(default off) keeps the default path to pin-endannotate + sticky protect only (
pin-only/no rewritein logs). Opt into rearrange when you deliberately want clock/banner float.
Measured warm reuse
Self-hosted production
dflash_server(native HTTP path), ~28-tool agentprompts in the ~17–24K token range:
On the last warm turn, ~24K prompt / 1.8 s ≈ 13K effective t/s, while only
~100 uncached tokens were actually prefilling — the KV pin is doing the work.
Example log shape with safe defaults:
This is a prefill / TTFT result, not a claim about decode throughput.
Reproduce
Against a
dflash_serverwith prefix cache enabled:Confirm warm turns report
cached_prefix_tokens > 0andrestore=trueinserver logs (
[pc] lookup hit,[server] chat CACHE … restore=true).For DiffPin specifically, send two tool-heavy turns that differ only by a small
mid-head volatile line (e.g. session clock) and confirm either a sticky pin hit
without rewrite (
pin-only/no rewrite), or — withDFLASH_PPP_REARRANGE=1—a PPP rearrange + protected pin of the stable blob.
Validation
test_server_unitdflash_serverbuild with rearrange gated off by default[ppp] enabled=1 rearrange=0prefix_lennear the tools pinpin-onlypath@davide221, Hey, thanks so much for helping out with that last PR! Just give me a shout if there's anything else you need me to do on this one to get it all wrapped up and merged.