Architecture
┌─────────────────┐ ┌──────────────────┐ ┌────────────────┐
│ Client / agent │ ───► │ toki (daemon) │ ───► │ llama-server │
│ (OpenAI API) │ │ │ │ (ROCm/HIP) │
└─────────────────┘ └──────────────────┘ └────────────────┘
│ embedding + index │
│ SQLite + blobs │
└────────────────────┘
tokid (the daemon) is a small FastAPI service that sits in front of llama-server. It intercepts requests and runs the 5-step recycling algorithm:
- Embed the incoming prompt.
- Find the most similar cached prompt via cosine similarity.
- Verify exact prefix match on token IDs.
- If hit: load the saved KV state via
?action=restoreand decode only the novel tokens. - If miss: full prefill, persist KV state via
?action=savefor the future.
Targets
- ROCm + llama-server (first-class, current focus)
- AMD Ryzen AI MAX+ 395 APU with UMA (
GGML_HIP_UMA=ON) - gfx1151 (RDNA 3.5)
- Up to 128 GB unified memory
How KV save/restore works (no patch needed)
As of llama.cpp b3524 (mid-2025), the upstream llama-server already exposes the endpoints we need behind the --slot-save-path flag:
POST /slots/{id_slot}?action=save body: {"filename": "..."}
POST /slots/{id_slot}?action=restore body: {"filename": "..."}
POST /slots/{id_slot}?action=erase body: {"filename": "..."}
The Debian-packaged llama-server 9413 already has these. LlamaServerBackend uses them directly. The patches/ directory contains a historical patch for users on older llama.cpp and a README.md explaining the situation.
Architectural improvements
Beyond the basic cache, the tool ships with:
- Tree-structured KV cache, KV tensors are tracked in BLOCK_SIZE chunks. Two prompts sharing a prefix share the same on-disk blocks (content-addressed by token hash). Provides a fast tree-hit lookup that bypasses embedding entirely.
- Persistent slot pool, the top-N most-frequently-used prefixes get dedicated llama-server slots that never clear. Skips the save/load cycle entirely for hot prefixes.
- Honest hit definition (
MIN_REUSE_FOR_HIT = 10), a "hit" requires at least 10 tokens of prefix reuse. Below that, the save/load overhead exceeds the prefill savings, so it's reported as asoft_hitinstead of inflating the hit rate. - Prefix-length-weighted similarity (
BruteForceIndex.search), among candidates within 5% cosine similarity, prefers the one with the longest token list. Dampens the "cosine finds a 1-token lookalike" pathology. - Substantive divergence classification, output divergence is classified into identical / whitespace-only / restore-noise / substantive via sentence-transformers semantic similarity. Reports the percentage of divergent pairs that are real (different meaning) vs cosmetic.
- Embedding chunking fallback (
LlamaServerBackend.embed), long prompts are split into overlapping segments and re-averaged, with a deterministic hash-based fallback when embedding fails entirely.
In-memory cache (current behavior)
Prefix reuse is in-memory only: the engine routes similar prompts to the same llama-server slot and lets the slot's KV cache serve the shared prefix (cache_prompt=true). This is lossless, outputs are byte-identical to a no-cache run. Disk persistence via ?action=save/?action=restore was removed after benchmarks showed 88% substantive divergence from baseline outputs; the .trkv sidecar layer remains in the codebase but is dormant.
Next: API reference, endpoints and auth.