Profile LLM gateway logs for prefix-cache reuse potential, and export anonymized replayable traces
Project description
llmtrafficlens
Point it at your LLM gateway's request log. It answers how much a prefix cache could save on your real traffic — before you build anything — and turns the log into an anonymized trace the standard benchmark tools can replay.
Two commands. Everything downstream of the trace is left to the tools that already do it well.
gateway log CSV ──► llmtrafficlens profile ──► report.html / .json
· request_json decision numbers
· timestamp, status
└──► llmtrafficlens export ──► trace.jsonl
AIPerf · SGLang · AIBrix
· Mooncake simulator
Install
pip install llmtrafficlens
Zero runtime dependencies. Python ≥ 3.10.
profile — the decision numbers
llmtrafficlens profile gateway.csv -o report
Input: a CSV with a request_json column holding the OpenAI-format
request body. Picked up automatically when present: response_json,
model_name, status_code, and a timestamp column (timestamp, ts,
created_at, ...; epoch or ISO-8601 — or name it with --ts-column).
Public Mooncake and qwen-bailian traces also work as input
(--format mooncake|bailian).
Output (report.html + report.json):
- Hit rate by cache size — one LRU simulation per capacity, ending with the unbounded case: your ceiling. If the ceiling is 3%, stop here. If it's 37%, the curve tells you how much cache memory it takes to approach it.
- Dual-metric prefix leaderboard — top prefixes by reuse count vs by reusable token volume. The heads differ, and only the second one matters: a 15-token greeting reused 870× is worthless to a cache; a 19K-token system prompt reused 95× is the whole prize.
- Session-identifier presence — 0% means reuse is cross-request shaped, so session-affinity routing cannot capture it and prefix-aware routing is required.
- Input/output token distributions, streaming ratio, model mix, QPS.
export — an anonymized, replayable trace
llmtrafficlens export gateway.csv --to mooncake -o trace.jsonl # or --to bailian
Each request becomes one line — no text, only structure:
{"timestamp": 1753340000123, "input_length": 1994, "output_length": 117,
"hash_ids": [4251731047194047120, 8125214104179782736, ...]}
hash_ids is a salted chained block-hash fingerprint: two requests share
the first N hashes exactly when they share their first N blocks. Replay
tools regenerate synthetic prompts from the hashes — same hash, same
synthetic block — so the prefix-sharing structure of production is
reproduced while the content is fake.
Then hand it to the tools built for replay:
# faithful replay at the recorded pace
aiperf profile --custom-dataset-type mooncake_trace --input-file trace.jsonl --fixed-schedule ...
# same trace, 2× the pace (rate sweeps without leaving replay)
aiperf profile --custom-dataset-type mooncake_trace --input-file trace.jsonl --synthesis-speedup-ratio 2.0 ...
# or SGLang
python -m sglang.bench_serving --dataset-name mooncake --dataset-path trace.jsonl ...
--to bailian adds chat_id / parent_chat_id / turn for AIPerf's
bailian_trace mode; use it when your log carries session identifiers.
Tell the consumer which block size you exported with — AIPerf defaults to
512 for mooncake and 16 for bailian (--prompt-input-tokens-block-size).
Untested claim, stated plainly: we have not round-tripped an export through AIPerf or SGLang ourselves. The formats match their documented schemas; actual replay verification is on the roadmap.
Try it on the bundled sample
The repository (not the pip package) ships a fully synthetic sample log — 360 requests, three shared system prompts with deliberately skewed popularity, sparse session keys, real timestamps:
git clone https://github.com/GMISWE/llmtrafficlens && cd llmtrafficlens
llmtrafficlens profile examples/sample_gateway.csv -o sample-report
| Metric | Value | Reading |
|---|---|---|
| Ceiling | 66.4% | worth building prefix-aware routing for |
| Top prefix by count | 16 tok × 180 reqs = 2.3% of reusable volume | popularity ≠ value: not worth pinning |
| Top prefix by volume | 2400 tok × 30 reqs = 55.6% of reusable volume | this is what routing must keep resident |
| Session-id presence | 4.2% | reuse is cross-request shaped; affinity alone can't catch it |
Regenerate the sample with python examples/generate_sample.py.
Scenarios
"Should we build prefix-cache-aware routing?" Run profile on last
week's log. Ceiling 4% → decline the proposal with evidence. Ceiling 40%
with 85% session-id presence → cheap session affinity captures most of it.
Ceiling 40% with 0% presence → reuse lives in cross-request shared
prefixes and only prefix-aware routing reaches it; attach the report to
the design doc as its quantitative basis.
"How much cache memory per worker?" Read the curve. On the public Mooncake conversation trace it runs 4.2% at 64K tokens, 5.6% at 1M, 18.5% at 4M, 27.1% at 8M, 34.2% at 16M, against a 37.4% ceiling — that workload only takes off past ~2M tokens, so a small cache buys almost nothing. Multiply tokens by per-token KV size for GB, and mind the caveats in Methodology: the curve models one global cache, and the engine's KV pool also holds in-flight requests.
"Our load test says the cache works great; production disagrees."
Hand-written benchmarks with uniform prompt groups inflate hit rates —
every group stays warm. Export your real trace and replay that instead;
--synthesis-speedup-ratio covers rate sweeps.
"A vendor wants to see our workload; legal says no." The export carries lengths, timestamps, and keyed block hashes. They can reproduce your cache behavior and tune against it; they cannot recover a word of any prompt.
Verify the math
curl -LO https://raw.githubusercontent.com/kvcache-ai/Mooncake/main/FAST25-release/traces/conversation_trace.jsonl
llmtrafficlens profile conversation_trace.jsonl --format mooncake -o mooncake-report
# → ceiling 0.3738 on the Mooncake conversation trace
Feed the same trace to the official KV Cache Hit Rate Simulator and compare its infinite-capacity ceiling. Our 37.4% is consistent with third-party analysis of this trace (a prefix-caching study reports ≈40% for the conversation workload, arXiv:2505.21919); an automated side-by-side check against the official simulator has not been run yet.
Methodology
Every number follows a published construction; deviations are listed, not hidden.
- Hit rate — replay requests in timestamp order against an LRU block cache. Unbounded capacity is the "ideal hit rate" of KVCache in the Wild (ATC'25) and the Mooncake simulator's infinite-capacity ceiling; bounded capacities give the Mooncake simulator's capacity curve. LRU is the default eviction policy in vLLM and SGLang. The simulation models a single global cache (one worker, or perfect routing): with N workers behind hash routing each sees a partition, so treat it as fleet-level guidance. The engine's KV pool also holds in-flight requests — the reuse pool is what remains.
- Block hashing — chained per-block hashing (
hash(parent, block), 16-token blocks) follows the vLLM prefix-cache block scheme and the qwen-bailian trace convention. Deviation: in approximate mode we hash fixed-size character chunks, not tokenizer output, so two prompts match only if byte-identical. - Token counts — chars/4 unless the log carries
usage(exact counts are then used and marked in the report). CJK-heavy text tokenizes closer to 1–2 chars/token: absolute counts skew low, ratios stay usable. - Coverage — the ceiling covers cross-request sharing only. Intra-session reuse is invisible unless the log carries session ids. Without a timestamp column, row order stands in: arrival stats are omitted and exports are flagged unfit for fixed-schedule replay.
Privacy
All processing is local. Reports contain aggregates only. Exports contain
lengths, timestamps, anonymized session ids, and keyed block hashes. The
hash key is generated at .ltl-salt — keep it stable across runs and
treat it as a secret.
Scope
Observability platforms (Langfuse, Helicone, Datadog LLM) count tokens and
cost but never look inside prompt structure. Trace analyzers (Mooncake
simulator, AIPerf analyze-trace) require pre-hashed traces. Load
generators and capacity simulators (AIPerf, SGLang, Vidur) consume traces
nobody produces from production logs (our survey: July 2026).
llmtrafficlens fills exactly that gap and stops there: raw gateway
logs → structure → decision numbers → a standard trace. Replay, rate
control, and deployment search belong to the tools above.
License
Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llmtrafficlens-0.3.0.tar.gz.
File metadata
- Download URL: llmtrafficlens-0.3.0.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03a6eeb9ae50c4b2cba7dffb80c048dc0e5451a3db731dd6db95bd3998a43973
|
|
| MD5 |
7eb7cf1a24ade0a110a6e9089e578585
|
|
| BLAKE2b-256 |
264083ff2c517621d00058c5f5db8c2d5b36fbbab77258a659a17402089d02aa
|
File details
Details for the file llmtrafficlens-0.3.0-py3-none-any.whl.
File metadata
- Download URL: llmtrafficlens-0.3.0-py3-none-any.whl
- Upload date:
- Size: 22.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
acde88d20cbe2c3de2abe8c1779d81079d61200c08d13968433402c458eab498
|
|
| MD5 |
089497015d0ed7cc60de758a8e76bc90
|
|
| BLAKE2b-256 |
20c5220a8d1c34a177f11585bed9cd2d143cfab50bf117699dce2491f83b472f
|