omnicache / docs · technical report
Technical report · OMC-TR-2026-01 · v3.1.0

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.

Version
3.1.0 @ 80a987e
Date
24 Sep 2026
Samples
30k micro · 4.8k e2e
Status
Preliminary

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.

9.1µs
L1 exact-hit lookup, p50
p99 34.9 µs · n=10,000
0.18ms
L2 near-duplicate hit, p50
p99 0.37 ms · n=10,000
0/ 20
False matches on unrelated prompts
max similarity 0.539
97.5%
Recall on template paraphrases
39 / 40 · 0 wrong answers

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:

  1. A description of the lookup pipeline and its intent-gated thresholds, taken from the source (§2).
  2. A reproducible latency characterisation of each cache path at 10,000 samples, replacing the 5-sample CLI benchmark (§4.1).
  3. An end-to-end measurement of four synthetic agent workloads, in-process and over loopback TCP (§4.2–4.3).
  4. 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.

Figure 1. Request path through the proxy
Stages run in order; the first that can answer returns. Latencies are the measured p50s from §4.1.
Coding agentClaude Code · Cursor
omnicache · 127.0.0.1:8000server/gateway.py
GATEIntent classifiersets threshold
L1Exact match9.1 µs p50
L2Near-duplicate0.18 ms p50
TOOLSTool replaySHA + mtime check
Singleflight: identical in-flight requests share one upstream call
Tenant namespaces: org_id isolates entries
Snapshot: SQLite WAL, restored at start
Provider APImiss only · ● +0.19 ms

Reading 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.

#ClassTrigger (first match wins)L2 threshold
1structured_schemaJSON schema requestedbypass
2agent_toolsTool definitions presentbypass
3creative_bypasstemperature > 0.85bypass
4code_generation```, def f(, function f(, class X:, SELECT…FROM, import x0.98
5math_calculationArithmetic, or calculate / solve / evaluate0.98
6deep_reasoningstep by step, prove that, derive…0.98
7sql_database_queryDDL / DML keywords0.98
8structured_jsonreturn json, format as json…0.95
9numeric_indexed_queryAny integer in the prompt0.90
10conversational_qaEverything 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

Hardware4 vCPU Intel Xeon @ 2.10 GHz, 15 GB RAM, cloud VM (shared tenancy)
SoftwareLinux 6.18, Python 3.11.15, OmniCache 3.1.0 at commit 80a987e, installed with pip install -e .
Cache configDefaults: in-memory storage (no Redis), FastHashEmbedder 512-d, threshold 0.75
UpstreamNone. 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

  1. Lookup microbenchmark. The same DualTierCache.lookup calls as omnicache benchmark, with 10,000 timed samples per path after 500 warm-up calls, timed with time.perf_counter_ns. The CLI defaults to 5 iterations, where p95 and p99 both equal the maximum. The harness is listed in Appendix A.
  2. End-to-end load test. benchmarks.load_test with four generators from benchmarks/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 against omnicache start.
  3. 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

Playground mode excludes the provider. The load tests send 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.

Figure 2. Lookup latency distribution (ECDF)
Share of lookups completed within x. Log scale · n=10,000 per path · p1–p99 shown.
Data table
Pathminp50p90p95p99p99.9maxmean ± sd
L1 exact0.00800.00910.01250.01610.03490.06900.4350.0104 ± 0.0070
L2 near-dup0.16580.18020.25000.27250.36921.09246.0040.1976 ± 0.0955
Miss0.17680.19030.25540.31330.40770.96015.8910.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)READMECLI, 5 iter × 3 runsThis report, n=10k
L1 exact0.06090.0208 – 0.02760.0091
L2 near-duplicate1.02320.1959 – 0.21980.1802
Tool API replay0.30520.0125 – 0.0177not re-run
Git / file replay—0.0229 – 0.0308not 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.

Figure 3. End-to-end latency per workload
Median of 3 runs · n=200 per run · concurrency 8 · log scale · provider excluded
Data table
WorkloadTransportrpshit %p50p95p99

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.

Figure 4. Share of requests answered by each tier
In-process, run 1 (coldest) · n=200 per workload
Data table
WorkloadDatasetL1 exactL2 near-dupMissHit 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).

Figure 5. Similarity of probes to their nearest stored prompt
8 stored prompts · 40 paraphrases · 20 unrelated prompts · vertical jitter only
Data table
GroupPrompt (truncated)SimilarityOutcome

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.json uses 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.

ClaimSourceWhat we foundStatus
Sub-millisecond L1 / L2 lookups (0.061 / 1.02 ms)README0.0091 / 0.180 ms p50 at n=10k. Faster than claimed.Reproduced
Intent gating prevents wrong reuseREADME, code0 / 20 false matches, 0 / 39 wrong answers, on a synthetic setReproduced
High hit rates on agent workloadslatest_report.json88–100% cold, 98.5–100% warm, on synthetic generators onlySynthetic only
"Replay in 0.08 ms"Landing pageTrue 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" cacheREADME, landingThe default embedder is lexical n-gram hashing. Neural backends are opt-in.Needs wording
1,850 ms cloud baseline and the derived speed-upsbenchmarks/reporter.pyHard-coded constant (also a 4,200 ms p99 and ~25 rps)Assumed
"98.5% cost cut"benchmarks/reporter.pyTemplate text, not computed from a runAssumed
Latency 6,130 → 1,450 ms (−76%), tokens 3,280 → 680 (−79%)Landing pageIllustrative session; no run in the repo produces theseNot measured
40–70% lower API billREADMENeeds recorded sessions against a real providerNot 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

  1. core/vector_cache.py: DualTierCache.lookup, classify_intent
  2. core/embeddings.py: FastHashEmbedder, AutoEmbedder, FastSemanticEmbedder
  3. benchmarks/scenarios.py, benchmarks/load_test.py: workload generators and load driver
  4. benchmarks/reporter.py, benchmarks/latest_report.json: committed report and baseline constants
  5. server/gateway.py, server/cli.py: HTTP entry point, omnicache benchmark