pi-prefix-cache

pi-prefix-cache

Reuses Pi's stable harness prefix (system-prompt sections, tool schemas, chat_template_kwargs) in a llama-server KV-cache slot across sessions, and can persist that prefix to disk so a restarted server (or a new Pi install) resumes in seconds instead of re-ingesting the 5–20k-token prefix.

See README.html for full documentation.

[ICO]NameLast modifiedSizeDescription

[PARENTDIR]Parent Directory  -  
[TXT]README.md2026-10-09 14:03 15K 
[DIR]old-sidecar-patch-version/2026-10-09 14:02 -  
[   ]pi-prefix-cache.tgz2026-10-09 14:03 19K 
[DIR]pi-prefix-cache/2026-10-09 14:03 -  

pi-prefix-cache — Full Documentation

pi-prefix-cache — Pi KV prefix checkpoint extension

How it works

Per provider request:

  1. Extract the stable prefix from the developer message (everything before the earliest variable-section boundary). Three markers are recognised, whichever comes first: The cwd boundary matters: without it, a project that has no AGENTS.md has no variable marker at all on pi >= 0.87 and the extension silently no-ops.
  2. Compute a fingerprint (SHA-256 over model id, model file identity (size+mtime), server id, pi version, extension version, tools[], chat_template_kwargs, stable prefix text).
  3. If this process already warmed that fingerprint, check the slot is still alive (GET /slots, nonzero prompt counter). A server restart clears the slot — the extension detects it and falls back to restore/re-warm instead of a false in-process hit.
  4. Otherwise (miss):
  5. The normal Pi request proceeds and reuses the slot's KV cache.

After each response, the extension polls the slot's last-prompt counters and logs the observed reuse of the normal request.

Requirements

Install

Install from PiFFA Store

Download and unpack the extension, then copy it into Pi's extensions directory:

wget https://store.piffa.net/lm/picache/pi-prefix-cache.tgz
tar xzf pi-prefix-cache.tgz
mkdir -p ~/.pi/agent/extensions
cp -r pi-prefix-cache ~/.pi/agent/extensions/

Manual install

  1. Copy the directory so Pi auto-loads it:
    cp -r pi-prefix-cache ~/.pi/agent/extensions/
    (or per-invocation: pi -e /path/to/pi-prefix-cache/index.ts)
  2. Set the env vars in your shell profile (read once at Pi start). Minimal production set:
    export PI_PREFIX_CACHE_BASE_URL=http://localhost:8080/v1
    export PI_PREFIX_CACHE_PERSIST=1
    export PI_PREFIX_CACHE_SLOT_DIR=/home/eaman/llama/slot_caches

Environment variables

VariableDefaultWhat it does
PI_PREFIX_CACHE_BASE_URL(unset)llama-server API base, e.g. http://localhost:8080/v1. Unset → observe-only: the extension logs the fingerprint and skips all warm-up/restore/save. Must point at the server that serves the model Pi is using.
PI_PREFIX_CACHE_SLOT0Slot id that holds the shared prefix. Warm-up, restore, save and erase all target this slot, and the normal Pi request must land on the same one. With -np 1 leave it 0.
PI_PREFIX_CACHE_PERSISToff"1" enables disk save/restore. Off → runtime-only reuse (prefix lost when the server restarts). On → cold misses restore pi-prefix-<fp16>.bin (then verify) and save a new checkpoint after a cold build. Requires llama-server ≥ mainline c811cb8f for the restore to help on hybrid models.
PI_PREFIX_CACHE_SLOT_DIR(unset)The server's --slot-save-path directory. The extension cannot discover it over HTTP, so it must be told. Only used for stale-checkpoint cleanup. Unset (or persistence off) → no cleanup.
PI_PREFIX_CACHE_CLEANUP1"0" disables the one-time-per-process stale-checkpoint cleanup. Cleanup only ever deletes files matching pi-prefix-[0-9a-f]{16}.bin (+ their .ckpt sidecar) — manually named files are never touched.
PI_PREFIX_CACHE_TTL_DAYS30Cleanup keeps any checkpoint whose lastUsed (from the metadata index) is within this many days, even if its fingerprint is no longer current.
PI_PREFIX_CACHE_KEEP_RECENT3Cleanup always keeps this many most-recently-used checkpoints regardless of TTL — a margin so a Pi downgrade or A/B model switch doesn't force an immediate cold rebuild.
PI_PREFIX_CACHE_MAX_GB0 (off)Hard cap on total checkpoint storage (.bin + .ckpt). After the TTL/recent pass, oldest kept files are deleted (never the current session's checkpoint) until under the cap.
PI_PREFIX_CACHE_SERVER_ID(empty)Optional llama-server build/runtime identity, included in the fingerprint (the server exposes no JSON build endpoint, so it is env-provided). Bump it when you deploy a new llama-server build, e.g. export PI_PREFIX_CACHE_SERVER_ID=ddddf03d-slckv2. Forces one clean rebuild after a server upgrade.
PI_PREFIX_CACHE_DIR~/.cache/pi/llama-prefix/Where the metadata index (index.json) lives. Data only — policy comes from the env vars above.
PI_PREFIX_CACHE_LOG~/.cache/pi/llama-prefix/debug.logDiagnostics log file (timestamped [prefix-cache] lines). pi ≥ 1.0 runs the TUI fullscreen by default, so the extension no longer writes to stdout — raw console output would land on top of the input area. Empty string disables file logging.
PI_PREFIX_CACHE_DEBUGoff"1" also echoes log lines to stdout (for non-interactive runs).

All numeric env vars fall back to their defaults when unset or invalid.

Provider gate (v1.2.0)

When PI_PREFIX_CACHE_BASE_URL is set, the extension acts only for models whose provider baseUrl in models.json (agent dir: $PI_CODING_AGENT_DIR or ~/.pi/agent) equals it — it can only warm its own server. Other models (remote providers, other local servers) are skipped with a log line: no warm-up, no restore/save, no slot polling. If models.json is missing or unreadable, all requests are skipped (safe direction: never warm a server the request cannot be attributed to).

Expected logs

Where logs go (v1.2.2+): all [prefix-cache] lines are written to the log file (PI_PREFIX_CACHE_LOG), not stdout — pi ≥ 1.0's fullscreen TUI renders raw console writes on top of the input area. In the TUI you get exactly one notification per session (the first request's outcome), rendered via ctx.ui.notify():

⚡ prefix-cache: WARM — restored 2335 tok from disk (1453 ms)   # checkpoint restore verified
⚡ prefix-cache: WARM — slot already hot                        # slot warm in this process
prefix-cache: COLD — warmed 2335 tok, checkpoint saved          # cold build (warning style)
prefix-cache: disabled this session (fail-open): <error>        # on failure (error style)

All lines below are prefixed [prefix-cache] (log file). First run for a fingerprint:

[prefix-cache] fingerprint=16ba1104…
[prefix-cache] no checkpoint (restore HTTP 400: …) → cold build
[prefix-cache] cache=MISS → warming slot=0 (cold)
[prefix-cache] warm-up done: HTTP 200 prompt_n=1228 cache_n=0
[prefix-cache] SAVED pi-prefix-16ba1104….bin: n_saved=…
[prefix-cache] normal request proceeding (source=warm-up)
[prefix-cache] observed normal request: prompt_n=1360 cache_n=1096 (reuse 81%)

Later sessions (same fingerprint):

[prefix-cache] RESTORE pi-prefix-16ba1104….bin: n_restored=…
[prefix-cache] RESTORE verified: identical 1224/1228, divergent probe 1096/1240
[prefix-cache] normal request proceeding (source=restore)

Within one Pi process, subsequent requests log cache=HIT (slot N already warmed for this fingerprint).

Verify server-side reuse with LLAMA_SERVER_SLOTS_DEBUG=1: restored context checkpoint (pos_min = …, … n_past = …).

Files

FileRole
index.tsExtension entrypoint. before_provider_request → extract stable prefix, fingerprint, warm/restore slot on miss; after_provider_response → observed-reuse diagnostics. Fail-open; never mutates the payload.
prefix.tsSplits the developer message into stable content and variable tail at the earliest of \n<project_context> / \n<cwd> / \nCurrent working directory: (trailing whitespace stripped). The \n<cwd> marker covers pi >= 0.87, where the plain-text cwd line was replaced by a <cwd> section.
fingerprint.tsCanonical JSON (sorted keys, array order preserved) + SHA-256 over format tag, model id, pi version, extension version, tools[], chat_template_kwargs, stable prefix.
warmup.tsAll llama-server HTTP details: warmSlot() (prefix-only request, n_predict: 0, slot: N), getSlotStats() (GET /slots), saveSlotFile() / restoreSlotFile() / eraseSlotFile() (POST /slots/{id}?action=save|restore|erase). All calls are timeout-bounded (120 s warm/save/restore/erase, 5 s stats).
cache-store.tsMetadata index (~/.cache/pi/llama-prefix/index.json) + cleanupStale() pruning pi-prefix-[0-9a-f]{16}.bin(+.ckpt) files whose fingerprint is no longer active. Keep set = current fingerprint ∪ index entries used within TTL ∪ N most recent. Manually named files are never touched.

Disk persistence — server requirement

/slots save|restore on mainline ≥ c811cb8f is sufficient: upstream #26004 ("server: preserve context checkpoints across slot save/restore") appends the server-side slot.prompt.checkpoints chain to the slot save file itself (the SCKP appendix) and rebuilds it on restore, with size-bounded reads, draft-context mismatch handling, and a fallback to full re-prefill when a restored checkpoint fails to load. No llama.cpp patch is required — the extension is fully patch-free on such builds.

Migration notes

Legacy patches (only relevant for servers that cannot upgrade to ≥ c811cb8f) are no longer shipped in this repo — they were removed from the tree and are recoverable from git history (commit 180ec5a: kv_prefix_checkpoint_sidecar_9adc7f42.patch, SLCK v2 sidecar, base 9adc7f42; prompt_cache_best_match_9adc7f42.patch). An older …_ddddf03d.patch (base ddddf03d) has an external copy at /home/eaman/llama/bug/patches/. Do not apply any of these to mainline.

Verified (2026-10-08, 9B MTP test server, port 8081)

Full cold-save/restore-verify cycle, non-interactive Pi sessions:

BinaryCold build + saveRestoreVerify (identical / divergent probe)
pure mainline c811cb8f, unpatchedSAVED 1712 tok (116 ms)85 ms1708/4 / probe cache_n = 1702 > 0
cumulative eaman, sidecar-stripped (b8531ac0; binary reports the stale 699d7417/build-2182 version string, discriminated by .so code strings)SAVED 1712 tok (58 ms)84 ms1708/4 / probe cache_n = 1702 > 0

Server: --ctx-checkpoints 8 -ub 128 --checkpoint-min-step 1024 --slot-save-path ... + tools-first --chat-template-file. The divergent probe cache_n > 0 on the unpatched binary is the acceptance criterion: the checkpoint chain survives the disk round-trip via the native SCKP appendix alone.

Notes & limitations