Transparent proxy for OpenAI-API compatible endpoints with SQLite logging to debug prefix caching behavior.
# Install
npm install
# Start proxy
npm start
# Point Harness to the proxy
export OPENAI_BASE_URL=http://localhost:8787
# Run your Harness tool as normal — all traffic gets loggedHarness → Proxy (localhost:8787) → OpenAI-API endpoint (127.0.0.1:8000)
↓
cache-hunter.db
Set via environment variables (or persisted from the UI):
TARGET_HOST(default:127.0.0.1)TARGET_PORT(default:8000)PROXY_PORT(default:8787)WEB_PORT(default:4000)
id- UUID for correlationtimestamp- Unix msmethod- HTTP methodpath- Request pathheaders- JSON stringbody- Full request body (JSON string)cache_salt- Extracted if present in bodyclient_ip- Client IP address
request_id- FK to requests.idtimestamp- Unix msstatus_code- HTTP statusheaders- JSON stringbody- Full response body (JSON string)duration_ms- Total request durationprompt_tokens- From usage.prompt_tokenscompletion_tokens- From usage.completion_tokenstotal_tokens- From usage.total_tokens
node query-examples.jsnode analyze-cache.jsnpm run analyzenpm run demo
npm run treeThis provides:
- Latency trends over time
- Prefix hash analysis
- Conversational chain detection
- Cache invalidation indicators
- CSV export for visualization
sqlite3 cache-hunter.db
-- Recent requests
SELECT datetime(timestamp/1000, 'unixepoch', 'localtime') as time,
path, duration_ms, prompt_tokens
FROM responses
ORDER BY timestamp DESC
LIMIT 10;
-- Find requests with similar prefixes
SELECT r1.id, r2.id, substr(r1.body, 1, 100) as prefix
FROM requests r1
JOIN requests r2 ON r2.timestamp > r1.timestamp
WHERE r1.path = '/v1/chat/completions'
AND r2.path = '/v1/chat/completions'
AND r2.body LIKE r1.body || '%'
ORDER BY r2.timestamp DESC
LIMIT 5;
-- Latency per token (high values = potential cache misses)
SELECT datetime(timestamp/1000, 'unixepoch', 'localtime') as time,
prompt_tokens,
duration_ms,
round(duration_ms * 1.0 / prompt_tokens, 2) as ms_per_token
FROM responses
WHERE prompt_tokens > 50
ORDER BY ms_per_token DESC;vLLM's prefix caching is transparent - it doesn't expose cache hit/miss signals. To detect caching behavior:
Cache hits should show lower ms/token for requests with similar prefixes:
SELECT prompt_tokens, duration_ms,
round(duration_ms * 1.0 / prompt_tokens, 2) as ms_per_token
FROM responses
WHERE prompt_tokens > 100
ORDER BY ms_per_token;Find requests that share prefixes:
SELECT r1.body as req1, r2.body as req2
FROM requests r1, requests r2
WHERE r2.body LIKE r1.body || '%'
AND length(r1.body) > 100
AND length(r1.body) < length(r2.body);Look for latency patterns over time:
SELECT datetime(timestamp/1000, 'unixepoch', 'localtime') as time,
duration_ms, prompt_tokens
FROM responses
ORDER BY timestamp;- ✅ 100% Transparent: Forwards all requests as-is
- ✅ SSE Streaming: Supports
/v1/chat/completionsstreaming - ✅ Async Logging: Non-blocking SQLite writes
- ✅ Correlation IDs:
x-proxy-request-idheader for tracing - ✅ Token Metrics: Logs prompt/completion/total tokens
- ✅ Timing Data: Precise duration measurements
- ✅ Context Verification: Hash-based tree to verify conversation coherence
- No cache signals: vLLM doesn't expose cache hit/miss
- Network latency: Duration includes localhost→vLLM network time
- Memory queue: In-memory write queue (flushed every 100ms or 50 requests)
# Remove all logs
rm cache-hunter.db- Proxy intercepts all HTTP requests to the upstream endpoint
- Captures request body, headers, timestamp
- Forwards transparently to the target (127.0.0.1:8000)
- Captures response body, headers, duration, token counts
- Logs to SQLite asynchronously (batched writes)
- Adds correlation ID header (
x-proxy-request-id)
# Watch mode
npm run dev
# Direct TypeScript execution
npx tsx src/index.ts