OmniCache: local exact and near-duplicate caching for LLM coding agents
How the proxy works, what it costs per request, how often it hits on synthetic agent workloads, and which of the published numbers we could reproduce.
Abstract
OmniCache is a local HTTP proxy that sits between a coding agent (Claude Code, Cursor, Aider) and the model provider. It answers repeated requests from an exact-match tier (L1), a near-duplicate vector tier (L2) and a Git-aware tool-output replayer. An intent classifier raises the match threshold for requests where a wrong reuse is costly. On a 4-vCPU Xeon we measure a median lookup cost of 9.1 µs for an L1 hit and 0.18 ms for an L2 hit, over 10,000 samples each. A miss adds 0.19 ms. The L2 tier matched 39 of 40 template paraphrases with no wrong answers. It rejected 20 of 20 unrelated prompts; the closest reached a similarity of 0.539. Seen end to end over loopback HTTP, lookup cost is a small fraction of request time: the Python server stack dominates. At concurrency 8, median latency is 10–14 ms. One workload (copilot) shows an unexplained slowdown to a 144 ms median. The headline savings figures in the project materials rest on an assumed 1,850 ms cloud baseline and synthetic workloads. We mark them as unverified.
1Introduction
Autonomous coding agents repeat themselves. Within one session they re-read the same files, re-list the same directories and re-ask near-identical questions after a retry or a restart. Each repeat is a full round trip to the provider, billed per input token. Most of those tokens are context that has not changed since the last turn.
OmniCache runs on the developer's machine at 127.0.0.1:8000. The agent is pointed at it with ANTHROPIC_BASE_URL or OPENAI_BASE_URL. It serves a stored response when it can prove the request is a repeat, and forwards to the provider otherwise. This report makes four contributions:
- A description of the lookup pipeline and its intent-gated thresholds, taken from the source (§2).
- A reproducible latency characterisation of each cache path at 10,000 samples, replacing the 5-sample CLI benchmark (§4.1).
- An end-to-end measurement of four synthetic agent workloads, in-process and over loopback TCP (§4.2–4.3).
- A match-quality experiment and an audit of every public performance claim against what we measured (§4.4, §7).
2System design
Every request passes through the stages in Figure 1, in order. The first stage that can answer does, so a request touches only as much of the pipeline as it needs. Concurrent identical requests are coalesced (singleflight): if eight agents ask the same thing at once, one upstream call is made. Entries are namespaced per tenant (org_id) and snapshotted to SQLite in WAL mode, so the cache survives a restart.
org_id isolates entriesReading the figure. An L1 hit costs a hash lookup. An L2 hit costs one embedding and an ANN search. A miss pays the full L1 + L2 lookup (0.19 ms) before forwarding. Tool replay returns a stored tool result only when the file's Git blob SHA and modification time are unchanged.
2.1Near-duplicate matching
L2 embeds the last user message and compares it to stored entries by cosine similarity. The default backend (EMBEDDER_BACKEND=auto) is FastHashEmbedder. It projects word tokens and character n-grams into a 512-dimensional hashed vector, after stop-word removal and a small synonym map [2]. This is lexical, not neural. It catches rewordings that share vocabulary. It will not match true paraphrases with different words. Neural (onnx, 384-d) and quantized backends exist, but they are opt-in and were not evaluated here.
2.2Intent-gated thresholds
A fixed threshold forces a bad trade-off. If it is low enough to catch rewordings, it will also return a cached function for a prompt that differs in one argument. OmniCache classifies each request with ordered rules and picks a threshold per class (Table 1) [1]. The first matching rule wins. Exact L1 matches are unaffected by the gate.
| # | Class | Trigger (first match wins) | L2 threshold |
|---|---|---|---|
| 1 | structured_schema | JSON schema requested | bypass |
| 2 | agent_tools | Tool definitions present | bypass |
| 3 | creative_bypass | temperature > 0.85 | bypass |
| 4 | code_generation | ```, def f(, function f(, class X:, SELECT…FROM, import x | 0.98 |
| 5 | math_calculation | Arithmetic, or calculate / solve / evaluate | 0.98 |
| 6 | deep_reasoning | step by step, prove that, derive… | 0.98 |
| 7 | sql_database_query | DDL / DML keywords | 0.98 |
| 8 | structured_json | return json, format as json… | 0.95 |
| 9 | numeric_indexed_query | Any integer in the prompt | 0.90 |
| 10 | conversational_qa | Everything else (SIMILARITY_THRESHOLD) | 0.75 |
Table 1. Intent classes from DualTierCache.classify_intent [1]. "Bypass" sets the threshold to 1.01, which disables L2 for that request.
3Methodology
3.1Environment
| Hardware | 4 vCPU Intel Xeon @ 2.10 GHz, 15 GB RAM, cloud VM (shared tenancy) |
| Software | Linux 6.18, Python 3.11.15, OmniCache 3.1.0 at commit 80a987e, installed with pip install -e . |
| Cache config | Defaults: in-memory storage (no Redis), FastHashEmbedder 512-d, threshold 0.75 |
| Upstream | None. Misses are served locally in playground mode (see §3.3). |
Table 2. Test environment. All runs used one machine with no other benchmark load.
3.2Experiments
- Lookup microbenchmark. The same
DualTierCache.lookupcalls asomnicache benchmark, with 10,000 timed samples per path after 500 warm-up calls, timed withtime.perf_counter_ns. The CLI defaults to 5 iterations, where p95 and p99 both equal the maximum. The harness is listed in Appendix A. - End-to-end load test.
benchmarks.load_testwith four generators frombenchmarks/scenarios.py: copilot, agent_swarm, burst and multi_tenant. Each ran with n=200 and concurrency 8, three times, in two configurations: in-process ASGI (--url internal) and over loopback TCP againstomnicache start. - Match quality. We stored the 8 base prompts from
scenarios.py. We then issued 40 paraphrases (5 templates × 8 prompts) and 20 unrelated prompts, and recorded status, similarity and whether the returned answer belonged to the right prompt.
3.3Controls and caveats
x-dashboard-playground: true with no API key, so misses are answered by a local stub. End-to-end latencies here are therefore proxy overhead only. They are not what a user would see on a miss, and they are not a comparison against a real provider.The TCP server restored 63 entries from its SQLite snapshot at start-up. Runs 2 and 3 are therefore warmer than run 1. We report the median of three runs and give the in-process run 1 separately as the coldest case.
4Results
4.1Lookup latency by path
Figure 2 shows the full latency distribution for each path. L1 is about 20× cheaper than L2 at the median. The distribution is tight: 99% of L1 hits finish within 35 µs. A miss costs slightly more than an L2 hit because it runs the full L2 search and finds nothing.
Data table
| Path | min | p50 | p90 | p95 | p99 | p99.9 | max | mean ± sd |
|---|---|---|---|---|---|---|---|---|
| L1 exact | 0.0080 | 0.0091 | 0.0125 | 0.0161 | 0.0349 | 0.0690 | 0.435 | 0.0104 ± 0.0070 |
| L2 near-dup | 0.1658 | 0.1802 | 0.2500 | 0.2725 | 0.3692 | 1.0924 | 6.004 | 0.1976 ± 0.0955 |
| Miss | 0.1768 | 0.1903 | 0.2554 | 0.3133 | 0.4077 | 0.9601 | 5.891 | 0.2091 ± 0.0822 |
Takeaway. Cache lookups cost microseconds to a fraction of a millisecond. Against a provider round trip of hundreds of milliseconds or more, the 0.19 ms added to every miss is negligible. Tails above p99.9 (up to 6 ms) are most likely garbage-collection pauses and VM scheduling; we did not confirm the cause. They are in the table, not the plot.
Table 3 compares our numbers with the project README and with the stock CLI. Every path is faster here than the README reports: 6.7× for L1 and 5.7× for L2. The README does not state its hardware, so we cannot say whether this is the machine or a code change. The 5-iteration CLI gets medians right but cannot estimate tails.
| Path (p50, ms) | README | CLI, 5 iter × 3 runs | This report, n=10k |
|---|---|---|---|
| L1 exact | 0.0609 | 0.0208 – 0.0276 | 0.0091 |
| L2 near-duplicate | 1.0232 | 0.1959 – 0.2198 | 0.1802 |
| Tool API replay | 0.3052 | 0.0125 – 0.0177 | not re-run |
| Git / file replay | — | 0.0229 – 0.0308 | not re-run |
Table 3. Lookup medians across sources. The CLI's p95 for L2 (1.19–1.24 ms) is really its maximum, because 5 samples cannot resolve a 95th percentile.
4.2End-to-end latency
Moving from a function call to an HTTP request changes the picture (Figure 3). In-process through the ASGI app, a cached request has a median of 0.6–1.1 ms. Over loopback TCP with 8 concurrent clients, the median rises to 10–14 ms. A single client with no concurrency sees 1.6–1.9 ms for an exact hit. Most of the end-to-end time is therefore the Python HTTP stack and queueing, not the cache.
Data table
| Workload | Transport | rps | hit % | p50 | p95 | p99 |
|---|
Anomaly. Copilot over TCP has a median of 144 ms (runs: 135, 180, 144). That is 10× the other workloads, although single-client exact hits on the same server take under 2 ms. Copilot is the only workload with many L2 hits under the strict 0.98 code gate. Our hypothesis is contention in the L2 path under concurrency. We have not isolated it. In-process copilot was also bimodal: run 1 had p95 2.5 ms, runs 2–3 about 38 ms.
4.3Hit composition
Figure 4 splits each workload's requests by the tier that answered. Burst is almost entirely exact repeats. Agent swarm leans on L2, because its agents rephrase shared tasks. Multi-tenant has no L2 hits by design: tenants cannot see each other's entries. Its misses are first requests in each new namespace.
Data table
| Workload | Dataset | L1 exact | L2 near-dup | Miss | Hit rate |
|---|
Caution. These workloads are generated to contain repetition, so their hit rates describe the generators, not real agent sessions. The warm TCP view also includes 63 entries restored from the snapshot.
4.4Match quality
A near-duplicate cache is only useful if it never returns the wrong answer. Figure 5 plots the similarity of every probe to its nearest stored entry. The two groups are separated by a wide empty band. The highest unrelated prompt ("Write a Python function to reverse a linked list", 0.539) sits well below the lowest paraphrase (0.944).
Data table
| Group | Prompt (truncated) | Similarity | Outcome |
|---|
Result. Precision 100% (0 false matches out of 20). Recall 97.5% (39 of 40), and all 39 hits returned the correct stored answer. The one gated-out paraphrase says "calculate", which triggers the math_calculation class and its 0.98 threshold (it scored 0.963). All five-point dips come from one template, "Kindly …". The other four templates ("Please", "Could you", "Help me", "Can you") consist only of stop words and score 0.9999, which makes them an easy test.
5Discussion
The cache is not the bottleneck. At 9 µs for L1 and 0.18 ms for L2, lookup cost is at least two orders of magnitude below any network round trip. Optimisation effort is better spent on the HTTP path: that is where an agent's cached request spends roughly 90% or more of its 1.7 ms (single client) to 14 ms (8 concurrent clients).
"Replay in 0.08 ms" describes the lookup, not the request. The figure is in the right range for an in-process lookup; we measured lower. What the agent experiences is an HTTP response. Its latency is 20–175× that number, depending on concurrency. Both numbers are good. Only the second one is user-visible.
Gating works, and costs a little recall. The strict classes rejected one legitimate paraphrase and accepted no false ones. For a tool whose failure mode is silently wrong code, that is the right trade-off. The keyword rules are coarse, though. "Calculate" in a SQL question makes it a math question.
Savings are a property of the workload. Cost saved is roughly the hit rate times the cost of the calls avoided. Neither can be measured on synthetic generators. The 40–70% bill reduction in the README is plausible for agent sessions dominated by file re-reads. It remains a hypothesis until it is measured on recorded sessions.
6Limitations and threats to validity
- No real provider. Misses were served by a local stub, so miss latency and cost were not measured.
- Synthetic workloads. Hit rates come from generators built to repeat. No recorded agent sessions were replayed.
- Easy paraphrase set. Four of five templates differ from the stored prompt only by stop words, so recall is an upper bound. A set of human-written paraphrases is needed.
- Lexical embedder only. Results apply to the default
FastHashEmbedder. The ONNX and quantized backends were not tested. - One machine. A shared-tenancy cloud VM. Tail latencies (p99.9 and above) are sensitive to noisy neighbours.
- Warm snapshots. The TCP server restored 63 entries at start, which inflates TCP hit rates relative to a cold start.
- Open anomaly. The copilot TCP slowdown (§4.2) is unexplained.
- Small committed samples. The repository's
latest_report.jsonuses n=40 per workload, where p99 equals the maximum. It agrees with our TCP medians (6.5–8.9 ms vs 9.7–13.9 ms) but should not be cited for tails.
7Claims audit
Every performance number in the landing page, README and benchmark reporter, checked against this report. "Assumed" means the number is a constant in code or copy, not a measurement.
| Claim | Source | What we found | Status |
|---|---|---|---|
| Sub-millisecond L1 / L2 lookups (0.061 / 1.02 ms) | README | 0.0091 / 0.180 ms p50 at n=10k. Faster than claimed. | Reproduced |
| Intent gating prevents wrong reuse | README, code | 0 / 20 false matches, 0 / 39 wrong answers, on a synthetic set | Reproduced |
| High hit rates on agent workloads | latest_report.json | 88–100% cold, 98.5–100% warm, on synthetic generators only | Synthetic only |
| "Replay in 0.08 ms" | Landing page | True for the lookup (0.009–0.18 ms). The HTTP response takes 1.7 ms (single client) to 14 ms (c=8). | Lookup only |
| "Semantic" cache | README, landing | The default embedder is lexical n-gram hashing. Neural backends are opt-in. | Needs wording |
| 1,850 ms cloud baseline and the derived speed-ups | benchmarks/reporter.py | Hard-coded constant (also a 4,200 ms p99 and ~25 rps) | Assumed |
| "98.5% cost cut" | benchmarks/reporter.py | Template text, not computed from a run | Assumed |
| Latency 6,130 → 1,450 ms (−76%), tokens 3,280 → 680 (−79%) | Landing page | Illustrative session; no run in the repo produces these | Not measured |
| 40–70% lower API bill | README | Needs recorded sessions against a real provider | Not measured |
Table 4. Claim status. The status labels are text and do not rely on colour.
8Reproducibility
Everything in this report can be regenerated from a clean checkout in about ten minutes.
# install
$ python -m venv .venv && . .venv/bin/activate && pip install -e .
# §4.1 lookup microbenchmark (CLI, 5 iterations; see Appendix A for n=10k)
$ omnicache benchmark
# §4.2 end-to-end, in-process (repeat 3x)
$ python -m benchmarks.load_test -n 200 -c 8 --url internal --quiet --export-json e2e.json
# §4.2 end-to-end, loopback TCP (repeat 3x)
$ omnicache start &
$ python -m benchmarks.load_test -n 200 -c 8 --url http://127.0.0.1:8000 --quiet --export-json tcp.json
Appendix A · Lookup harness
import time, statistics
from core.vector_cache import DualTierCache
N, WARM = 10_000, 500
cache = DualTierCache()
stored = {"model": "claude-3-5-sonnet", "temperature": 0.0,
"messages": [{"role": "user", "content": "Write a python fast fourier transform algorithm"}]}
cache.store(stored, {"choices": [{"message": {"role": "assistant", "content": "import numpy as np..."}}]})
variant = {**stored, "messages": [{"role": "user", "content": "Write a python fast fourier transform (FFT) algorithm in numpy"}]}
novel = lambda i: {**stored, "messages": [{"role": "user", "content": f"Unrelated question {i}: explain B-tree node splits"}]}
def sample(make):
xs, statuses = [], set()
for i in range(WARM + N):
p = make(i)
t0 = time.perf_counter_ns()
status = cache.lookup(p)[0] # HIT_EXACT | HIT_SEMANTIC | MISS | BYPASS
dt = (time.perf_counter_ns() - t0) / 1e6
if i >= WARM: xs.append(dt); statuses.add(status)
xs.sort()
return statuses, {q: xs[int(len(xs) * q)] for q in (.5, .9, .95, .99, .999)}
for name, fn in [("l1", lambda i: stored), ("l2", lambda i: variant), ("miss", novel)]:
print(name, *sample(fn))
—References
core/vector_cache.py:DualTierCache.lookup,classify_intentcore/embeddings.py:FastHashEmbedder,AutoEmbedder,FastSemanticEmbedderbenchmarks/scenarios.py,benchmarks/load_test.py: workload generators and load driverbenchmarks/reporter.py,benchmarks/latest_report.json: committed report and baseline constantsserver/gateway.py,server/cli.py: HTTP entry point,omnicache benchmark