prefixlens 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Star Parvin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: prefixlens
3
+ Version: 0.1.0
4
+ Summary: Cache-attribution analyzer for LLM prefix caches: per-request, per-tag, per-token divergence attribution the engine can't give you.
5
+ Author-email: Star Parvin <tidesandshots@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/starparvinai/prefixlens
8
+ Project-URL: Repository, https://github.com/starparvinai/prefixlens
9
+ Project-URL: Issues, https://github.com/starparvinai/prefixlens/issues
10
+ Project-URL: Changelog, https://github.com/starparvinai/prefixlens/blob/main/CHANGELOG.md
11
+ Keywords: llm,inference,vllm,prefix-cache,kv-cache,cache-analysis,diagnostic,sglang,trt-llm
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: System :: Monitoring
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7.0; extra == "dev"
30
+ Requires-Dist: build>=1.0; extra == "dev"
31
+ Requires-Dist: twine>=4.0; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # prefixlens
35
+
36
+ **A cache-attribution analyzer for LLM prefix caches. Tells you *which prompts* are killing your hit rate, *at which token*, and *what to change* to fix it.**
37
+
38
+ Every serving engine (vLLM, SGLang, TRT-LLM, LMCache) reports prefix-cache hit rate as a single number. When that number is bad, the engine can't tell you why. `prefixlens` can.
39
+
40
+ ---
41
+
42
+ ## The problem this exists to solve
43
+
44
+ You run vLLM in production. Two tenants share the same instance. Tenant A gets a 68% prefix-cache hit rate. Tenant B gets 0.3%. Everything looks fine in the metrics — the engine is working, both tenants have similar traffic. Something in Tenant B's prompts is breaking cache reuse, but the metrics can't tell you what.
45
+
46
+ Today, finding it means eyeballing prompts side-by-side until you spot a UUID or timestamp somewhere near the front. In a real workload that's hours of manual diffing per incident. The signal you actually want — *"the first divergent token across Tenant B's traffic is at position 47, and it's a session ID injected into the system prompt"* — doesn't exist in any tool.
47
+
48
+ `prefixlens` produces that signal, and goes one step further: it tells you what to change.
49
+
50
+ ## What it does (v0.1)
51
+
52
+ The primary output is **attribution and remediation**, not raw statistics. Statistics are the substrate; the report is the product.
53
+
54
+ **Attribution — where the cache is being broken:**
55
+ - **Position-of-first-divergent-token histogram**, per tag: for each tenant/route/session, the distribution of *how far into the prompt* cache reuse is failing. Fat tail at position 47 = something near the top of every prompt is unique.
56
+ - **Cache-killer substrings**: recurring high-entropy fragments (UUIDs, timestamps, per-request IDs) that break block boundaries. Ranked by how many misses they explain.
57
+
58
+ **Remediation — what to change:**
59
+ - **Reordering suggestions**: given the killer substrings, propose prompt structure changes ("move `session_id` from system prompt into user message") and identify which divergent positions each change would eliminate.
60
+ - **Simulated-vs-actual reconciler**: point it at a vLLM `/metrics` endpoint + a request log; it compares its simulation against what the real engine reported. Trust the analysis before acting on it.
61
+
62
+ **Substrate — how it computes any of this:**
63
+ - CPU simulation of a radix-tree prefix cache with LRU eviction, block-aligned to a configurable size (defaults to vLLM's 16). Given a JSONL of prompts + a tokenizer, it produces the same hit/miss decisions the engine would.
64
+ - Engine-agnostic: the simulator is a general radix-tree model. Calibrated against vLLM in v0.1; SGLang and TRT-LLM parity in v0.2.
65
+
66
+ Everything runs on CPU. No GPU required.
67
+
68
+ ## Usage
69
+
70
+ ```bash
71
+ $ pip install -e .
72
+ $ prefixlens analyze examples/chen_multitenant.jsonl --block-size 16 --capacity-blocks 100
73
+
74
+ prefixlens analyze — 10 requests, 40 blocks total
75
+
76
+ overall hit rate: 40.0% (16 / 40 blocks)
77
+
78
+ by tenant:
79
+ acme 80.0% (5 req, 16/20 blk)
80
+ widgets 0.0% (5 req, 0/20 blk)
81
+
82
+ divergent positions for tenant=widgets:
83
+ block 0 5 miss (100.0% unique — unique content per request)
84
+ ```
85
+
86
+ Read that bottom line the way an operator would: *every widgets request first-misses at block 0, and the token content at block 0 is different every time.* Diagnosis: something at the top of the prompt is per-request-unique — the classic session-UUID-at-position-0 shape. Restructure to move it into the user message, not the system prompt.
87
+
88
+ The overall 40% is exactly what vLLM's `/metrics` gives you — and exactly what obscured the real story in Chen's post. `prefixlens` pulls the tenants apart, and then within `widgets` locates *where* and *what kind* of divergence you're looking at:
89
+
90
+ - **`100% unique content per request`** — variable field (UUID, timestamp, request ID) at that block. Fix: restructure the prompt.
91
+ - **`shared content, thrashing`** — same tokens repeatedly missed. Fix: grow the cache; the working set is bigger than capacity.
92
+
93
+ The bundled `examples/chen_multitenant.jsonl` reproduces this in 10 requests, on CPU, in milliseconds. Add `--json` for machine-readable output.
94
+
95
+ **Coming next (see [Roadmap](#roadmap))**: text-space substring mining once the tokenizer path is wired up — turns "block 0 is unique per request" into `"session_id=<uuid>"`, plus reordering suggestions that project the hit-rate lift of each proposed fix.
96
+
97
+ Corpora with a `"prompt"` field instead of `"token_ids"` need a tokenizer — the v0.1 CLI has no `--tokenizer` flag, so pre-tokenize your corpus. See `src/prefixlens/loader.py` for the library API.
98
+
99
+ ### `prefixlens explain` — per-request diagnosis
100
+
101
+ When someone gets paged with *"request `req_abc123` was slow, why?"*:
102
+
103
+ ```bash
104
+ $ prefixlens explain traces.jsonl --request-id req_abc123 --block-size 16 --capacity-blocks 4096
105
+
106
+ prefixlens explain — req_abc123
107
+
108
+ tags: tenant=widgets, route=/v1/chat/completions
109
+ blocks: 0 hit, 4 miss, 4 total
110
+ first divergence: block 0
111
+
112
+ block-by-block trace:
113
+ block 0 MISS [9000002, 2000, 2001, 2002, …, 2012, 2013, 2014]
114
+ block 1 MISS [2015, 2016, 2017, 2018, …, 2028, 2029, 2030]
115
+ block 2 MISS [2031, 2032, 2033, 2034, …, 2044, 2045, 2046]
116
+ block 3 MISS [2047, 2048, 2049, 2050, …, 2060, 2061, 2062]
117
+
118
+ divergence context (aggregate signal at this position):
119
+ tenant=widgets: 5 miss at block 0 (100.0% unique)
120
+ ```
121
+
122
+ Two views bolted together: the per-request block-by-block trace *and* the aggregate signal for the request's tag buckets at that same position. So the reader knows both "your request diverged at block 0" *and* "you're one of 5 widgets requests with the exact same shape — this is systemic, not a one-off." Add `--json` for scripting.
123
+
124
+ ### `prefixlens validate` — the credibility anchor
125
+
126
+ Everything above is trustworthy only if our CPU sim agrees with what real vLLM would do on the same request stream. `validate` is that check:
127
+
128
+ ```bash
129
+ $ curl http://vllm:8000/metrics > metrics.txt # after your workload runs
130
+ $ prefixlens validate requests.jsonl \
131
+ --metrics-file metrics.txt \
132
+ --block-size 16 \
133
+ --capacity-blocks 4096
134
+
135
+ prefixlens validate — sim vs real vLLM /metrics
136
+
137
+ sim hit rate: 42.1% (8,412 blocks processed)
138
+ real hit rate: 43.7% (3,678 hits / 8,412 queries)
139
+ delta: 1.60 pp (tolerance ±3.0 pp)
140
+
141
+ verdict: OK
142
+ Sim is calibrated on this workload; downstream
143
+ attribution is trustworthy.
144
+ ```
145
+
146
+ Exit code 0 if `OK`, 1 if `DIVERGED` (useful in CI). `--block-size` and `--capacity-blocks` must match the real engine's config; a wrong config is the most common source of false-diverge. Add `--tolerance-pp N` to widen the threshold, or `--json` for machine output.
147
+
148
+ ## What it is not
149
+
150
+ - Not a GPU profiler. Doesn't measure kernel time. If your bottleneck is compute, not cache, this won't help.
151
+ - Not a benchmark harness. It analyzes what happened, not what would happen at 10× QPS.
152
+ - Not a replacement for the engine's built-in tooling. vLLM is building an in-tree analyzer (see [Related work](#related-work)); `prefixlens` sits on top of and around it, focused on the *interpretation* layer the engine won't ship.
153
+
154
+ ## Roadmap
155
+
156
+ - **v0.1** — CPU simulator, JSONL input, per-tag stats, position-of-first-divergent-token histogram, cache-killer substrings, reordering suggestions, simulated-vs-actual reconciler. **Target: 6 weeks from first commit.**
157
+ - **v0.2** — SGLang and TRT-LLM parity (engine-specific radix/block semantics). Live engine hooks: vLLM plugin + TRT-LLM KV event API adapter, so you can analyze production traffic without exporting first.
158
+ - **v0.3** — Reordering *validator*: given a proposed prompt change, simulate the new hit rate and produce a confidence interval before you ship the change.
159
+
160
+ ## Related work
161
+
162
+ **vLLM RFC [#47993](https://github.com/vllm-project/vllm/issues/47993)** proposes an in-tree offline prefix-cache workload analyzer (`vllm analyze-prefix-cache`), currently being implemented across PRs [#48369](https://github.com/vllm-project/vllm/pull/48369) and [#48838](https://github.com/vllm-project/vllm/pull/48838) by @harsh543 and @raravind007. The in-tree tool will provide the block-hash simulation and basic statistics — the *substrate* — using vLLM's exact hashing semantics, which is a real advantage no external tool can match.
163
+
164
+ `prefixlens` is complementary, not competitive:
165
+
166
+ | Feature | vLLM in-tree analyzer | prefixlens |
167
+ |---|---|---|
168
+ | Block-hash simulation matching engine semantics | ✅ (by construction) | ✅ (calibrated) |
169
+ | Per-tag / per-tenant breakdown | ✅ | ✅ |
170
+ | Position-of-first-divergent-token histogram | (not in v1 scope) | ✅ |
171
+ | Cache-killer substring mining | (not in scope) | ✅ |
172
+ | Reordering suggestions | (not in scope) | ✅ |
173
+ | Simulated-vs-actual reconciliation | (not in scope) | ✅ |
174
+ | Engine-agnostic (SGLang, TRT-LLM) | ❌ (vLLM only) | v0.2 |
175
+
176
+ **Related tooling:** [LMCache](https://github.com/lmcache/lmcache) exposes rich per-request cache observability and a Redis-CLI-based cache introspection tool, but not workload-level attribution. [llm-d's precise-prefix-cache-routing](https://github.com/llm-d/llm-d) and [Truefoundry's cache-aware routing](https://www.truefoundry.com/blog/kv-cache-routing-why-standard-load-balancers-break-prefix-caching-and-how-to-fix-it) address the routing layer (a different fix — moving requests, not restructuring prompts).
177
+
178
+ ## Why this tool
179
+
180
+ Multi-tenant workload observability is a solved craft in backend systems and a wide-open gap in LLM serving. The engines expose the counters they built for themselves — averages, rates, totals. What operators actually need to *fix* things are attribution and per-request breakdowns. `prefixlens` brings that discipline to the prefix cache layer.
181
+
182
+ ## Status
183
+
184
+ **v0.0.x in progress.** Radix simulator + LRU eviction + JSONL loader + per-tag aggregation + divergent-position attribution + `prefixlens analyze` + `prefixlens validate` + `prefixlens explain` (per-request block-by-block trace + aggregate divergence context) are all in and tested (115+ tests). The Chen scenario reproduces in one command against a bundled fixture. Next up: `prefixlens lint` (CI-friendly prompt-template hazard check) and text-space substring mining.
185
+
186
+ If this problem is one you also have, or if you've solved it a different way, open an issue. Real workload traces (anonymized) welcome.
@@ -0,0 +1,153 @@
1
+ # prefixlens
2
+
3
+ **A cache-attribution analyzer for LLM prefix caches. Tells you *which prompts* are killing your hit rate, *at which token*, and *what to change* to fix it.**
4
+
5
+ Every serving engine (vLLM, SGLang, TRT-LLM, LMCache) reports prefix-cache hit rate as a single number. When that number is bad, the engine can't tell you why. `prefixlens` can.
6
+
7
+ ---
8
+
9
+ ## The problem this exists to solve
10
+
11
+ You run vLLM in production. Two tenants share the same instance. Tenant A gets a 68% prefix-cache hit rate. Tenant B gets 0.3%. Everything looks fine in the metrics — the engine is working, both tenants have similar traffic. Something in Tenant B's prompts is breaking cache reuse, but the metrics can't tell you what.
12
+
13
+ Today, finding it means eyeballing prompts side-by-side until you spot a UUID or timestamp somewhere near the front. In a real workload that's hours of manual diffing per incident. The signal you actually want — *"the first divergent token across Tenant B's traffic is at position 47, and it's a session ID injected into the system prompt"* — doesn't exist in any tool.
14
+
15
+ `prefixlens` produces that signal, and goes one step further: it tells you what to change.
16
+
17
+ ## What it does (v0.1)
18
+
19
+ The primary output is **attribution and remediation**, not raw statistics. Statistics are the substrate; the report is the product.
20
+
21
+ **Attribution — where the cache is being broken:**
22
+ - **Position-of-first-divergent-token histogram**, per tag: for each tenant/route/session, the distribution of *how far into the prompt* cache reuse is failing. Fat tail at position 47 = something near the top of every prompt is unique.
23
+ - **Cache-killer substrings**: recurring high-entropy fragments (UUIDs, timestamps, per-request IDs) that break block boundaries. Ranked by how many misses they explain.
24
+
25
+ **Remediation — what to change:**
26
+ - **Reordering suggestions**: given the killer substrings, propose prompt structure changes ("move `session_id` from system prompt into user message") and identify which divergent positions each change would eliminate.
27
+ - **Simulated-vs-actual reconciler**: point it at a vLLM `/metrics` endpoint + a request log; it compares its simulation against what the real engine reported. Trust the analysis before acting on it.
28
+
29
+ **Substrate — how it computes any of this:**
30
+ - CPU simulation of a radix-tree prefix cache with LRU eviction, block-aligned to a configurable size (defaults to vLLM's 16). Given a JSONL of prompts + a tokenizer, it produces the same hit/miss decisions the engine would.
31
+ - Engine-agnostic: the simulator is a general radix-tree model. Calibrated against vLLM in v0.1; SGLang and TRT-LLM parity in v0.2.
32
+
33
+ Everything runs on CPU. No GPU required.
34
+
35
+ ## Usage
36
+
37
+ ```bash
38
+ $ pip install -e .
39
+ $ prefixlens analyze examples/chen_multitenant.jsonl --block-size 16 --capacity-blocks 100
40
+
41
+ prefixlens analyze — 10 requests, 40 blocks total
42
+
43
+ overall hit rate: 40.0% (16 / 40 blocks)
44
+
45
+ by tenant:
46
+ acme 80.0% (5 req, 16/20 blk)
47
+ widgets 0.0% (5 req, 0/20 blk)
48
+
49
+ divergent positions for tenant=widgets:
50
+ block 0 5 miss (100.0% unique — unique content per request)
51
+ ```
52
+
53
+ Read that bottom line the way an operator would: *every widgets request first-misses at block 0, and the token content at block 0 is different every time.* Diagnosis: something at the top of the prompt is per-request-unique — the classic session-UUID-at-position-0 shape. Restructure to move it into the user message, not the system prompt.
54
+
55
+ The overall 40% is exactly what vLLM's `/metrics` gives you — and exactly what obscured the real story in Chen's post. `prefixlens` pulls the tenants apart, and then within `widgets` locates *where* and *what kind* of divergence you're looking at:
56
+
57
+ - **`100% unique content per request`** — variable field (UUID, timestamp, request ID) at that block. Fix: restructure the prompt.
58
+ - **`shared content, thrashing`** — same tokens repeatedly missed. Fix: grow the cache; the working set is bigger than capacity.
59
+
60
+ The bundled `examples/chen_multitenant.jsonl` reproduces this in 10 requests, on CPU, in milliseconds. Add `--json` for machine-readable output.
61
+
62
+ **Coming next (see [Roadmap](#roadmap))**: text-space substring mining once the tokenizer path is wired up — turns "block 0 is unique per request" into `"session_id=<uuid>"`, plus reordering suggestions that project the hit-rate lift of each proposed fix.
63
+
64
+ Corpora with a `"prompt"` field instead of `"token_ids"` need a tokenizer — the v0.1 CLI has no `--tokenizer` flag, so pre-tokenize your corpus. See `src/prefixlens/loader.py` for the library API.
65
+
66
+ ### `prefixlens explain` — per-request diagnosis
67
+
68
+ When someone gets paged with *"request `req_abc123` was slow, why?"*:
69
+
70
+ ```bash
71
+ $ prefixlens explain traces.jsonl --request-id req_abc123 --block-size 16 --capacity-blocks 4096
72
+
73
+ prefixlens explain — req_abc123
74
+
75
+ tags: tenant=widgets, route=/v1/chat/completions
76
+ blocks: 0 hit, 4 miss, 4 total
77
+ first divergence: block 0
78
+
79
+ block-by-block trace:
80
+ block 0 MISS [9000002, 2000, 2001, 2002, …, 2012, 2013, 2014]
81
+ block 1 MISS [2015, 2016, 2017, 2018, …, 2028, 2029, 2030]
82
+ block 2 MISS [2031, 2032, 2033, 2034, …, 2044, 2045, 2046]
83
+ block 3 MISS [2047, 2048, 2049, 2050, …, 2060, 2061, 2062]
84
+
85
+ divergence context (aggregate signal at this position):
86
+ tenant=widgets: 5 miss at block 0 (100.0% unique)
87
+ ```
88
+
89
+ Two views bolted together: the per-request block-by-block trace *and* the aggregate signal for the request's tag buckets at that same position. So the reader knows both "your request diverged at block 0" *and* "you're one of 5 widgets requests with the exact same shape — this is systemic, not a one-off." Add `--json` for scripting.
90
+
91
+ ### `prefixlens validate` — the credibility anchor
92
+
93
+ Everything above is trustworthy only if our CPU sim agrees with what real vLLM would do on the same request stream. `validate` is that check:
94
+
95
+ ```bash
96
+ $ curl http://vllm:8000/metrics > metrics.txt # after your workload runs
97
+ $ prefixlens validate requests.jsonl \
98
+ --metrics-file metrics.txt \
99
+ --block-size 16 \
100
+ --capacity-blocks 4096
101
+
102
+ prefixlens validate — sim vs real vLLM /metrics
103
+
104
+ sim hit rate: 42.1% (8,412 blocks processed)
105
+ real hit rate: 43.7% (3,678 hits / 8,412 queries)
106
+ delta: 1.60 pp (tolerance ±3.0 pp)
107
+
108
+ verdict: OK
109
+ Sim is calibrated on this workload; downstream
110
+ attribution is trustworthy.
111
+ ```
112
+
113
+ Exit code 0 if `OK`, 1 if `DIVERGED` (useful in CI). `--block-size` and `--capacity-blocks` must match the real engine's config; a wrong config is the most common source of false-diverge. Add `--tolerance-pp N` to widen the threshold, or `--json` for machine output.
114
+
115
+ ## What it is not
116
+
117
+ - Not a GPU profiler. Doesn't measure kernel time. If your bottleneck is compute, not cache, this won't help.
118
+ - Not a benchmark harness. It analyzes what happened, not what would happen at 10× QPS.
119
+ - Not a replacement for the engine's built-in tooling. vLLM is building an in-tree analyzer (see [Related work](#related-work)); `prefixlens` sits on top of and around it, focused on the *interpretation* layer the engine won't ship.
120
+
121
+ ## Roadmap
122
+
123
+ - **v0.1** — CPU simulator, JSONL input, per-tag stats, position-of-first-divergent-token histogram, cache-killer substrings, reordering suggestions, simulated-vs-actual reconciler. **Target: 6 weeks from first commit.**
124
+ - **v0.2** — SGLang and TRT-LLM parity (engine-specific radix/block semantics). Live engine hooks: vLLM plugin + TRT-LLM KV event API adapter, so you can analyze production traffic without exporting first.
125
+ - **v0.3** — Reordering *validator*: given a proposed prompt change, simulate the new hit rate and produce a confidence interval before you ship the change.
126
+
127
+ ## Related work
128
+
129
+ **vLLM RFC [#47993](https://github.com/vllm-project/vllm/issues/47993)** proposes an in-tree offline prefix-cache workload analyzer (`vllm analyze-prefix-cache`), currently being implemented across PRs [#48369](https://github.com/vllm-project/vllm/pull/48369) and [#48838](https://github.com/vllm-project/vllm/pull/48838) by @harsh543 and @raravind007. The in-tree tool will provide the block-hash simulation and basic statistics — the *substrate* — using vLLM's exact hashing semantics, which is a real advantage no external tool can match.
130
+
131
+ `prefixlens` is complementary, not competitive:
132
+
133
+ | Feature | vLLM in-tree analyzer | prefixlens |
134
+ |---|---|---|
135
+ | Block-hash simulation matching engine semantics | ✅ (by construction) | ✅ (calibrated) |
136
+ | Per-tag / per-tenant breakdown | ✅ | ✅ |
137
+ | Position-of-first-divergent-token histogram | (not in v1 scope) | ✅ |
138
+ | Cache-killer substring mining | (not in scope) | ✅ |
139
+ | Reordering suggestions | (not in scope) | ✅ |
140
+ | Simulated-vs-actual reconciliation | (not in scope) | ✅ |
141
+ | Engine-agnostic (SGLang, TRT-LLM) | ❌ (vLLM only) | v0.2 |
142
+
143
+ **Related tooling:** [LMCache](https://github.com/lmcache/lmcache) exposes rich per-request cache observability and a Redis-CLI-based cache introspection tool, but not workload-level attribution. [llm-d's precise-prefix-cache-routing](https://github.com/llm-d/llm-d) and [Truefoundry's cache-aware routing](https://www.truefoundry.com/blog/kv-cache-routing-why-standard-load-balancers-break-prefix-caching-and-how-to-fix-it) address the routing layer (a different fix — moving requests, not restructuring prompts).
144
+
145
+ ## Why this tool
146
+
147
+ Multi-tenant workload observability is a solved craft in backend systems and a wide-open gap in LLM serving. The engines expose the counters they built for themselves — averages, rates, totals. What operators actually need to *fix* things are attribution and per-request breakdowns. `prefixlens` brings that discipline to the prefix cache layer.
148
+
149
+ ## Status
150
+
151
+ **v0.0.x in progress.** Radix simulator + LRU eviction + JSONL loader + per-tag aggregation + divergent-position attribution + `prefixlens analyze` + `prefixlens validate` + `prefixlens explain` (per-request block-by-block trace + aggregate divergence context) are all in and tested (115+ tests). The Chen scenario reproduces in one command against a bundled fixture. Next up: `prefixlens lint` (CI-friendly prompt-template hazard check) and text-space substring mining.
152
+
153
+ If this problem is one you also have, or if you've solved it a different way, open an issue. Real workload traces (anonymized) welcome.
@@ -0,0 +1,60 @@
1
+ [project]
2
+ name = "prefixlens"
3
+ version = "0.1.0"
4
+ description = "Cache-attribution analyzer for LLM prefix caches: per-request, per-tag, per-token divergence attribution the engine can't give you."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [
10
+ { name = "Star Parvin", email = "tidesandshots@gmail.com" },
11
+ ]
12
+ keywords = [
13
+ "llm",
14
+ "inference",
15
+ "vllm",
16
+ "prefix-cache",
17
+ "kv-cache",
18
+ "cache-analysis",
19
+ "diagnostic",
20
+ "sglang",
21
+ "trt-llm",
22
+ ]
23
+ classifiers = [
24
+ "Development Status :: 4 - Beta",
25
+ "Environment :: Console",
26
+ "Intended Audience :: Developers",
27
+ "Intended Audience :: System Administrators",
28
+ "Operating System :: OS Independent",
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3.10",
31
+ "Programming Language :: Python :: 3.11",
32
+ "Programming Language :: Python :: 3.12",
33
+ "Programming Language :: Python :: 3.13",
34
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
35
+ "Topic :: Software Development :: Libraries :: Python Modules",
36
+ "Topic :: System :: Monitoring",
37
+ ]
38
+ dependencies = []
39
+
40
+ [project.scripts]
41
+ prefixlens = "prefixlens.cli:main"
42
+
43
+ [project.optional-dependencies]
44
+ dev = ["pytest>=7.0", "build>=1.0", "twine>=4.0"]
45
+
46
+ [project.urls]
47
+ Homepage = "https://github.com/starparvinai/prefixlens"
48
+ Repository = "https://github.com/starparvinai/prefixlens"
49
+ Issues = "https://github.com/starparvinai/prefixlens/issues"
50
+ Changelog = "https://github.com/starparvinai/prefixlens/blob/main/CHANGELOG.md"
51
+
52
+ [build-system]
53
+ requires = ["setuptools>=77"]
54
+ build-backend = "setuptools.build_meta"
55
+
56
+ [tool.setuptools.packages.find]
57
+ where = ["src"]
58
+
59
+ [tool.pytest.ini_options]
60
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,33 @@
1
+ from prefixlens.explain import (
2
+ BlockTrace,
3
+ DivergenceContext,
4
+ RequestExplanation,
5
+ explain_request,
6
+ )
7
+ from prefixlens.loader import Tokenizer, load_jsonl
8
+ from prefixlens.metrics import VllmMetrics, parse_vllm_metrics
9
+ from prefixlens.request import Request
10
+ from prefixlens.simulator import (
11
+ DivergentPosition,
12
+ ProcessResult,
13
+ RadixCacheSimulator,
14
+ Report,
15
+ TagStats,
16
+ )
17
+
18
+ __all__ = [
19
+ "BlockTrace",
20
+ "DivergenceContext",
21
+ "DivergentPosition",
22
+ "ProcessResult",
23
+ "RadixCacheSimulator",
24
+ "Report",
25
+ "Request",
26
+ "RequestExplanation",
27
+ "TagStats",
28
+ "Tokenizer",
29
+ "VllmMetrics",
30
+ "explain_request",
31
+ "load_jsonl",
32
+ "parse_vllm_metrics",
33
+ ]