conproxy
Retrieval cache for agentic RAG — lower cost, faster search.
conproxy sits in front of your search backends. LLM caches skip re-generating answers. conproxy skips re-running the search — embed, rerank, upstream — when agents hit the same (or near-same) query again.
Why it pays
- Cost — hits skip another embed call and managed-vector read
- Speed — cache hits ~138× faster than miss path on the agentic live bench (hit p50 ~0.1 ms vs miss ~13.8 ms; ~89.5% exact hit rate) — benchmarks
When to use
- LLM agents re-querying the same corpora (retry loops, multi-agent fanout, tool-call storms)
- Multi-backend retrieval (cascading, federating, or migrating across stores)
- Hitting embed / managed-vector cost or latency under agentic load
When not to use
- A one-off script with no TTL / semantic / multi-backend needs — the in-process
Engineis the lightweight path - LLM-response caching (that's GPTCache or RedisVL SemanticCache territory)
- Cross-org mTLS peer replication (not planned; use a mesh sidecar)
The problem
LLM caches (GPTCache, RedisVL SemanticCache) skip re-generating answers, but agents still re-rerank, re-embed, and re-query the same corpora on retries, multi-agent fanout, and tool-call storms. Every repeated retrieval costs an embed call and a managed-vector read. conproxy caches the retrieval leg itself.
Consider conproxy if…
- Multiple agents or tool loops hit the same corpus
- Embed or managed-vector $ is visible
- You want one MCP/HTTP search façade over ES / Qdrant / pgvector / Meilisearch / Pinecone / Milvus
- You need measured hit rate / false-hit gate, not vibes (
make bench-hitrate)
Skip conproxy if…
- You only need an LLM-response cache → use GPTCache / RedisVL
- A one-line memoize hash covers your duplicates (no TTL / semantic / upstreams)
- You need write-path CDC / multi-region invalidation today (not shipped; track correctness doc)
- One tiny backend, no agent loops, no cost pressure
vs alternatives
| Need | Prefer |
|---|---|
| Cache LLM answers | GPTCache / RedisVL SemanticCache |
| Cache search/retrieval under agents | conproxy |
| One process, no daemon, single MCP server | Engine (in-process query core) |
| Multi-backend cascade / MCP tune / dry-run scope | conproxy |
| LLM-side semantic cache for prompts | LangChain cache / provider-level caching |
At a glance
| Category | Retrieval-leg cache for agentic RAG |
| Not | LLM answer cache (GPTCache / RedisVL) |
| Pays when | Agents re-query — hits skip embed + upstream |
| Proof | ~89.5% exact hit rate; hit p50 ~0.1 ms vs miss |
| Integrate | MCP conproxy mcp · HTTP/gRPC · Python SDK · Engine |
FAQ
- What is conproxy? A caching proxy in front of search backends. Caches retrieval results, not LLM tokens.
- How is it different from GPTCache / RedisVL SemanticCache? Those cache LLM answers. conproxy caches embed + search results for agents re-querying the same corpora.
- When does it pay? Retries, multi-agent fanout, tool-call storms. Cost + latency win on every hit.
- How do I try it? See Use it — Docker, Python, Rust, MCP, or a curl.
- How do I prove it on my data?
make bench-hitratefor synthetic traces;make bench-hitrate-replay QUERIES=path/to/trace.txtfor your real query log.
One MCP endpoint, any backend, cost + latency on hits, false-hit gated semantic tier. Benchmarks reproducible.
agent ──► MCP / HTTP / gRPC ──► conproxy ──► backends
│ cache (exact + semantic, coalesce)
│ cascade (priority fallback + RRF)
│ federation (local-first confidence merge)
│ scope (lexical Jaccard + embed band)
└─ metrics · audit · distill
Works with Elasticsearch, OpenSearch, Qdrant, pgvector, Meilisearch, Pinecone, Milvus.
→ Benchmarks · Python SDK + LangChain / LlamaIndex · CONTRIBUTING
Use it
Five ways in. One copy-paste each; docs for the rest.
Python Engine
In-process query core — no daemon, no gRPC. Same request path as the daemon. docs/engine.md · docs/sdk-python.md.
=
= await
# result.cache_status: 1=hit, 2=miss, 3=stale, 4=frozen
Always await query(). Talk to a running daemon instead: ConproxyClient(grpc_url="http://localhost:9999").
Rust Engine
Same Engine, in-process. docs/engine.md.
use ;
let engine = builder
.config_toml
.build?;
let result = engine
.query
.await?;
gRPC client crate: cargo add conproxy-sdk.
Docker daemon
Shared cache in front of a backend — the default when multiple agents share one cache. Full walkthrough.
Local binary (no Docker):
Compose (proxy + Meilisearch): examples/docker-compose/ · docs/docker-compose.md.
Helm: helm install conproxy oci://ghcr.io/jmcgrath207/charts/conproxy --version 0.2.1
release = mcp + persistence + embed-api + pgvector. Flags: docs/feature-flags.md.
MCP
release already includes the MCP server. Point Claude Desktop or opencode at conproxy mcp. docs/mcp-integration.md.
opencode (~/.config/opencode/opencode.jsonc):
{ "mcp": { "conproxy": { "type": "local", "command": ["conproxy", "mcp"], "enabled": true } } }
Start a daemon first (conproxy start --config … --daemon) so search has an upstream.
gRPC / HTTP
Any language. Default container ports: gRPC :9999, HTTP :10000. docs/api-reference.md.
Features
In-process Engine
- Same
execute_querypath as the daemon — no gRPC, no peer, no daemon (docs/engine.md) pip install conproxy(Python) /cargo add conproxy(Rust); the wheel links the query core, not a thin client- Memory budget:
max_memory/memory_fractionbounds the cache in-process - Optional read-only dashboard (
dashboard_listen) — health, stats, metrics, cache, contexts - Use the daemon instead when multiple agents should share one cache
Agentic cache
- In-memory cache with TTL, jitter, and background refresh; S3-FIFO eviction
- Hit path skips embed + upstream — cost and latency win on every hit; coalesce collapses concurrent duplicates
- Semantic tier with τ-frontier and measured false-hit rate (≤1% gate)
- Request coalescing (singleflight) to collapse concurrent duplicates
- Negative caching for errors; serve-stale-while-refresh
One endpoint, any backend
- MCP server (stdio):
search, dry-run tune suite, dashboard-parity status tools (health, overview, cache_status, pool_status, circuit_status, metrics_status, contexts_status, peer_status, tokio_status, cache_entries) - 7 upstream adapters: Elasticsearch, OpenSearch, Qdrant, pgvector, Meilisearch, Pinecone, Milvus
- Runtime query-mode probe (TextNative vs VectorOnly) per adapter
- Context-rooted multi-tenancy (
[contexts.<id>]): per-agent API keys, rate limits, isolated cache, scope phrases
Tune
- Scope loop — dry-run Score C filter/boost/rerank +
min_similaritysweeps on supplied or live hits; phrase suggest; run compare/select - What-if probes — cache TTL hit/stale/miss, cascade leg selection, federated merge weights, embed batch shape, rate-limit allow/deny, warm-plan ETA — session-scoped, no backend write
- Build local — ship prod — one call:
tune_workflow(open → search → tune → optionalapply_tune+ hot-reload); or exportcontexts.<id>.scopeTOML/JSON to paste by hand
Cascade & federation
- Cascade — priority-ordered fallback chain; quality gate (
min_score_threshold,min_results); optional RRF fusion for equal-priority legs - Federation — local-first with confidence-gated remote fallback; configurable merge modes
Correctness you can gate
make bench-hitratefamily (exact / sem / onnx / live) with PASS / FAIL-CORE / FAIL-TRUST verdicts- τ-frontier + false-hit rate per workload (only public frontier in the space, as far as we know)
- TTL sweep + what-if CDC model — measures stale rate vs healing options
- MCP
benchmark— live query vs tuned scope diff; improved / degraded / unchanged (pairs with Tune)
Resilience
- 3-state upstream health tracking (Healthy / Degraded / Offline)
- Circuit breaker with configurable thresholds
- Exponential backoff with jitter; adaptive timeout from P99
- Connection pooling (pgbouncer-style semaphore); degradation ladder (PAUSE / RESUME)
Export & observability
conproxy distill— dump cache to Markdown (+ optional JSON sidecar) for LLM ingestion; per-context filtering, tier selection, post-process hook- Prometheus metrics endpoint, audit log, per-upstream + per-context stats
- TokIO runtime introspection:
GET /debug/tokio(aggregates) andGET /debug/tokio/dump(task backtraces; requiresRUSTFLAGS=--cfg tokio_unstable) - Grafana dashboard
Integrations
- gRPC + HTTP APIs; MCP server for stdio clients (Claude Desktop, opencode)
- Python SDK with LangChain / LlamaIndex adapters (docs/sdk-python.md)
- systemd service management
- Experimental: P2P cache replication via CDC (LWW by wall timestamp; optional
shared_secret, no mTLS — see feature flags before relying on it for production fan-out)
Supported Upstreams
| Backend | Type | Query Mode | Score Range | Status |
|---|---|---|---|---|
| Elasticsearch | elasticsearch |
text_native |
0–100+ (normalized to 0–1) | Shipped |
| OpenSearch | opensearch |
text_native |
0–100+ (normalized to 0–1) | Shipped (ES adapter; container proof Wave 1) |
| Qdrant | qdrant |
text_native |
0–1 | Shipped |
| Meilisearch | meilisearch |
text_native |
0–1 (_rankingScore) |
Shipped |
| pgvector | pgvector |
vector_only |
0–1 | Shipped (pgvector feature) |
| Pinecone | pinecone |
vector_only |
0–1 | Experimental (less e2e proof) |
| Milvus | milvus |
vector_only |
0–1 | Experimental (less e2e proof) |
Minimal Configuration
Context-rooted (canonical). Cache, scope, and routing live on the context, not on a global [proxy]:
# .conproxy/conproxy.toml
[]
= "127.0.0.1:9090"
[]
= "http://localhost:6333"
= "qdrant"
= 30
[]
= true
[[]]
= "qdrant"
[]
= 300 # 5 min fresh
= 600 # 10 min stale (serves while refreshing)
= 10000
Multi-leg cascade and federated variants: see examples/multi-upstream-cascade.toml and examples/federated-search.toml.
Documentation
| Document | Description |
|---|---|
| Tour | Feature walkthrough with cascade/federation emphasis |
| Quickstart | 60-second install + first cached query (Docker required) |
| Configuration | Full context-rooted TOML config reference |
| Multi-Upstream | Cascade, federation, RRF, score normalization |
| Architecture | Internal design and data flow |
| CLI Reference | All commands and flags |
| API Reference | HTTP + gRPC admin + distill endpoints |
| MCP Integration | Setup for Claude Desktop, opencode, and other stdio clients; tune tools |
| Distill | Cache export for LLM ingestion |
| Deployment | Production setup and monitoring |
| Docker Compose | Side-by-side conproxy + backend stack (example) |
| Feature Flags | Compile-time features |
| Python SDK | Native client + LangChain/LlamaIndex adapters |
| Engine | In-process query core (Rust + Python) |
Feature Flags
| Flag | What it enables | External deps |
|---|---|---|
mcp |
MCP server (stdio transport — Claude Desktop, opencode) | rmcp, schemars |
embed-api |
Embedder trait + OpenAI/Cohere/HuggingFace APIs (no ONNX) | — |
embed |
Local ONNX embedding (implies embed-api) |
ort, tokenizers, ndarray |
persistence |
Disk-backed cache (redb) | redb |
pgvector |
pgvector adapter | tokio-postgres |
linux-sandbox |
seccomp sandbox (Linux only) | caps, nix |
e2e |
E2E proxy tests (requires running instance) | — |
load-test |
Load testing infra | rlt, rand, rand_distr, hdrhistogram |
integration-tests |
Integration tests against real backends (Docker) | testcontainers |
dhat-heap |
DHAT heap profiling | dhat |
tokio-console |
tokio-console (async task inspector, TUI) | console-subscriber |
tokio-taskdump |
Handle::dump() task backtraces (requires RUSTFLAGS=--cfg tokio_unstable) |
— |
tokio-console-snap |
console_snap headless dump bin (CI-friendly) |
console-api |
Meta-features: release = mcp + persistence + embed-api + pgvector (ONNX + sandbox opt-in). test = release + load-test + dhat-heap.
See Feature Flags for recommended combinations and build instructions.
License
MIT