llm-cachecheck 0.2.3__tar.gz → 0.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: llm-cachecheck
3
- Version: 0.2.3
3
+ Version: 0.3.0
4
4
  Summary: Measure how often the prompt cache changes greedy answers on your LLM server (llama.cpp, vLLM and SGLang)
5
5
  Project-URL: Homepage, https://github.com/aditi-p31/cache-divergence-study/tree/main/cachecheck
6
6
  Project-URL: Source, https://github.com/aditi-p31/cache-divergence-study
@@ -46,7 +46,8 @@ second answer on two paths:
46
46
  Each path runs twice. A probe counts as valid only when all of these hold:
47
47
 
48
48
  - both paths repeat on their own
49
- - the server's own counts show that the cache hit covered the turn-1 answer
49
+ - the server's own counts show that the cache hit covered the turn-1 answer,
50
+ to within `--hit-tolerance` tokens and at least past the turn-1 prompt
50
51
  - the server's own counts show that the recompute path reused nothing
51
52
 
52
53
  ## Run it
@@ -61,8 +62,8 @@ llm-cachecheck --help
61
62
  Or download the single file
62
63
  [`cachecheck.py`](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck/cachecheck.py)
63
64
  and run `python cachecheck.py` in place of `llm-cachecheck`. Both take the
64
- same options. A full run
65
- sends 120 requests.
65
+ same options. Each conversation takes six requests, so a run of the 20
66
+ built-in conversations sends 120.
66
67
 
67
68
  **llama.cpp.** The cache is switched per request. Start the server with one
68
69
  slot, because the default is several slots that share a cache. Turn the
@@ -73,10 +74,44 @@ llama-server -m model.gguf -np 1 --cache-ram 0 --port 8080
73
74
  llm-cachecheck --base-url http://127.0.0.1:8080
74
75
  ```
75
76
 
76
- **vLLM, SGLang or another OpenAI-compatible server.** The cache is a server
77
- setting, so serve the same model twice, once with prefix caching on and once
78
- with it off. Both servers must report cached tokens. For vLLM on one 24 GB GPU
79
- (a 3B model, so two copies fit):
77
+ **vLLM or SGLang, one server.** Start the server with prefix caching on (the
78
+ default) and with cached-token reporting on. The tool sends every request with
79
+ a `cache_salt`. The two turns of the cache path share one salt, so turn 2
80
+ reuses the state of turn 1. Each recompute request gets a salt that no other
81
+ request has used, so it cannot reuse any cached state.
82
+
83
+ ```bash
84
+ vllm serve Qwen/Qwen2.5-3B-Instruct --port 8000 \
85
+ --generation-config vllm --enable-prompt-tokens-details
86
+ llm-cachecheck --mode one-server --base-url http://127.0.0.1:8000 \
87
+ --model Qwen/Qwen2.5-3B-Instruct
88
+ ```
89
+
90
+ For SGLang, add `--enable-cache-report` and `--sampling-defaults openai`.
91
+ On real vLLM 0.33.0 and SGLang 0.5.23 servers (SGLang with CUDA graphs disabled), the recompute answers in this mode had the same text as those of a second server with prefix caching off, on all 20 conversations in every run ([details](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck-0.3-validation/README.md)).
92
+
93
+ The server must honour `cache_salt`. If it does not, the recompute requests
94
+ reuse cached state, the tool marks those probes as inconclusive and says so,
95
+ and the two-server setup below still works. The server must also report cached
96
+ tokens, so `--skip-hit-check` is not available in this mode.
97
+
98
+ This mode compares a cache hit with a cache miss on one server. From reading
99
+ the vLLM 0.33.0 and SGLang 0.5.23 source, a miss on a standard attention model,
100
+ served without a KV connector such as LMCache, takes the same code path as on a
101
+ server with prefix caching off. For some setups, for example models with Mamba
102
+ or sliding-window layers, or speculative decoding, turning prefix caching on
103
+ also changes how the server handles requests that miss. To compare with
104
+ caching off there, use two servers.
105
+
106
+ Run it on a server that gets no other traffic, such as a staging copy. On a
107
+ busy server, batching with other requests can change answers by itself. The
108
+ tool's repeat checks mark many such probes as inconclusive, but they cannot
109
+ rule out a difference caused by other traffic.
110
+
111
+ **vLLM, SGLang or another OpenAI-compatible server, two servers.** The cache
112
+ is a server setting, so serve the same model twice, once with prefix caching
113
+ on and once with it off. Both servers must report cached tokens. For vLLM on
114
+ one 24 GB GPU (a 3B model, so two copies fit):
80
115
 
81
116
  ```bash
82
117
  vllm serve Qwen/Qwen2.5-3B-Instruct --port 8000 --gpu-memory-utilization 0.45 \
@@ -97,14 +132,16 @@ URLs would reach the same server.
97
132
  The tool then reports the recompute path reusing cached tokens and marks every
98
133
  probe invalid, so check the server logs if that happens.
99
134
 
100
- Tested on real servers on 2026-11-11 and 11-12 (UTC; one RTX 4090; the tool's 20
101
- conversations; details in the [currency note](https://github.com/aditi-p31/cache-divergence-study/blob/main/CURRENCY_CHECK_2026-11.md)):
135
+ Tested on real servers (one RTX 4090; the tool's 20 conversations). The two-server and llama.cpp rows are from 2026-11-11 and 11-12 UTC ([currency note](https://github.com/aditi-p31/cache-divergence-study/blob/main/CURRENCY_CHECK_2026-11.md)); the one-server rows are from 2026-11-14 UTC ([details](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck-0.3-validation/README.md)):
102
136
 
103
137
  | Server | Model | Conversations with a different turn-2 answer | Both caches off |
104
138
  |---|---|---|---|
105
139
  | vLLM 0.33.0, two servers | Qwen2.5-3B-Instruct, BF16 | 14 of 20, in each of 5 runs | 0 of 20 |
106
140
  | SGLang 0.5.23, two servers (CUDA graphs disabled) | Qwen2.5-3B-Instruct, BF16 | 12 of 20, in each of 2 runs | 0 of 20 |
107
141
  | llama.cpp v0.9.0 | Qwen2.5-7B-Instruct, Q4_K_M | 11 of 20, in each of 2 runs | |
142
+ | vLLM 0.33.0, one server | Qwen2.5-3B-Instruct, BF16 | 14 of 20, in each of 2 runs | |
143
+ | SGLang 0.5.23, one server (CUDA graphs disabled) | Qwen2.5-3B-Instruct, BF16 | 12 of 20, in each of 2 runs | |
144
+ | SGLang 0.5.23, one server, default settings | Qwen2.5-3B-Instruct, BF16 | 12 of 20, in each of 2 runs | |
108
145
 
109
146
  If you run it, an [issue](https://github.com/aditi-p31/cache-divergence-study/issues) with what you saw is welcome.
110
147
 
@@ -112,13 +149,56 @@ Two server processes can also differ for other reasons. To check that, start
112
149
  both with prefix caching off and run with `--skip-hit-check`. Every probe
113
150
  should come out the same.
114
151
 
115
- Options: `--n` sets the number of probes (default 20), `--max-tokens` the
116
- answer length (default 256), `--out` the JSON report path.
152
+ Options: `--probes` reads your own conversations (next section), `--n` sets
153
+ the number of probes (default: all), `--max-tokens` the answer length
154
+ (default 256), `--out` the JSON report path. If your server caches in blocks or
155
+ pages larger than 32 tokens, set `--hit-tolerance` to that size, or cache hits
156
+ can look too short. With large pages, also ask for longer turn-1 answers, so
157
+ the hit can reach past the prompt.
117
158
 
118
159
  Models that think before answering need thinking turned off for this check.
119
160
  Their templates often drop past thinking from the history, which shortens the
120
161
  cache hit, and the tool marks those probes as inconclusive.
121
162
 
163
+ ## Run it on your own conversations
164
+
165
+ The built-in conversations are short and generic. To check the kind of
166
+ prompts your application sends, put your own in a JSON Lines file, one
167
+ conversation per line, and pass it with `--probes`:
168
+
169
+ ```bash
170
+ llm-cachecheck --base-url http://127.0.0.1:8080 --probes my_conversations.jsonl
171
+ ```
172
+
173
+ Each line has one of two forms
174
+ ([example file](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck/examples/conversations_example.jsonl)):
175
+
176
+ ```json
177
+ {"id": "returns-1", "turn1": "A customer wants to return shoes bought 40 days ago. ...", "turn2": "Rewrite the reply so it offers store credit instead."}
178
+ {"id": "sql-1", "messages": [{"role": "system", "content": "You are a careful SQL assistant."}, {"role": "user", "content": "Write a query for the ten largest orders."}], "followup": "Change it so it only counts orders created in 2025."}
179
+ ```
180
+
181
+ - `turn1` is the first user message and `turn2` the follow-up.
182
+ - In the longer form, `messages` is the conversation up to and including the
183
+ turn-1 user message, so it can carry your system prompt and earlier turns.
184
+ `followup` is the turn-2 user message.
185
+ - `id` is optional and is copied into the report.
186
+ - Text messages only, with the roles system, developer, user and assistant.
187
+ Tool calls and images are not supported.
188
+
189
+ The tool generates the turn-1 answer itself and builds turn 2 on it, as with
190
+ the built-in conversations, and it always decodes greedily. A file that holds
191
+ one JSON array of these objects also works.
192
+
193
+ Let turn 1 ask for an answer of a few sentences. The tool counts a cache hit
194
+ only when it reaches past the turn-1 prompt into the answer. A server that
195
+ caches in blocks (vLLM uses 16 tokens by default) can stop the hit at the
196
+ prompt when the answer is short, and the probe is then inconclusive.
197
+
198
+ The JSON report saves every answer, so keep it private if your conversations
199
+ are. The printed result block holds no conversation text: only the counts
200
+ and the run settings, including the model name.
201
+
122
202
  ## Reading the result
123
203
 
124
204
  A real run on Apple Silicon (Metal) with Qwen2.5-3B-Instruct at Q4_K_M,
@@ -148,6 +228,9 @@ It took about 15 minutes on a laptop.
148
228
  | 1 | At least one valid probe differed |
149
229
  | 2 | Inconclusive: too few valid probes, or a request failed |
150
230
 
231
+ A usage error (a bad or conflicting option) also exits with status 2, before
232
+ any request is sent and without writing a report.
233
+
151
234
  The JSON report keeps every answer, the finish reasons, token counts, cached
152
235
  token counts and the server build.
153
236
 
@@ -22,7 +22,8 @@ second answer on two paths:
22
22
  Each path runs twice. A probe counts as valid only when all of these hold:
23
23
 
24
24
  - both paths repeat on their own
25
- - the server's own counts show that the cache hit covered the turn-1 answer
25
+ - the server's own counts show that the cache hit covered the turn-1 answer,
26
+ to within `--hit-tolerance` tokens and at least past the turn-1 prompt
26
27
  - the server's own counts show that the recompute path reused nothing
27
28
 
28
29
  ## Run it
@@ -37,8 +38,8 @@ llm-cachecheck --help
37
38
  Or download the single file
38
39
  [`cachecheck.py`](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck/cachecheck.py)
39
40
  and run `python cachecheck.py` in place of `llm-cachecheck`. Both take the
40
- same options. A full run
41
- sends 120 requests.
41
+ same options. Each conversation takes six requests, so a run of the 20
42
+ built-in conversations sends 120.
42
43
 
43
44
  **llama.cpp.** The cache is switched per request. Start the server with one
44
45
  slot, because the default is several slots that share a cache. Turn the
@@ -49,10 +50,44 @@ llama-server -m model.gguf -np 1 --cache-ram 0 --port 8080
49
50
  llm-cachecheck --base-url http://127.0.0.1:8080
50
51
  ```
51
52
 
52
- **vLLM, SGLang or another OpenAI-compatible server.** The cache is a server
53
- setting, so serve the same model twice, once with prefix caching on and once
54
- with it off. Both servers must report cached tokens. For vLLM on one 24 GB GPU
55
- (a 3B model, so two copies fit):
53
+ **vLLM or SGLang, one server.** Start the server with prefix caching on (the
54
+ default) and with cached-token reporting on. The tool sends every request with
55
+ a `cache_salt`. The two turns of the cache path share one salt, so turn 2
56
+ reuses the state of turn 1. Each recompute request gets a salt that no other
57
+ request has used, so it cannot reuse any cached state.
58
+
59
+ ```bash
60
+ vllm serve Qwen/Qwen2.5-3B-Instruct --port 8000 \
61
+ --generation-config vllm --enable-prompt-tokens-details
62
+ llm-cachecheck --mode one-server --base-url http://127.0.0.1:8000 \
63
+ --model Qwen/Qwen2.5-3B-Instruct
64
+ ```
65
+
66
+ For SGLang, add `--enable-cache-report` and `--sampling-defaults openai`.
67
+ On real vLLM 0.33.0 and SGLang 0.5.23 servers (SGLang with CUDA graphs disabled), the recompute answers in this mode had the same text as those of a second server with prefix caching off, on all 20 conversations in every run ([details](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck-0.3-validation/README.md)).
68
+
69
+ The server must honour `cache_salt`. If it does not, the recompute requests
70
+ reuse cached state, the tool marks those probes as inconclusive and says so,
71
+ and the two-server setup below still works. The server must also report cached
72
+ tokens, so `--skip-hit-check` is not available in this mode.
73
+
74
+ This mode compares a cache hit with a cache miss on one server. From reading
75
+ the vLLM 0.33.0 and SGLang 0.5.23 source, a miss on a standard attention model,
76
+ served without a KV connector such as LMCache, takes the same code path as on a
77
+ server with prefix caching off. For some setups, for example models with Mamba
78
+ or sliding-window layers, or speculative decoding, turning prefix caching on
79
+ also changes how the server handles requests that miss. To compare with
80
+ caching off there, use two servers.
81
+
82
+ Run it on a server that gets no other traffic, such as a staging copy. On a
83
+ busy server, batching with other requests can change answers by itself. The
84
+ tool's repeat checks mark many such probes as inconclusive, but they cannot
85
+ rule out a difference caused by other traffic.
86
+
87
+ **vLLM, SGLang or another OpenAI-compatible server, two servers.** The cache
88
+ is a server setting, so serve the same model twice, once with prefix caching
89
+ on and once with it off. Both servers must report cached tokens. For vLLM on
90
+ one 24 GB GPU (a 3B model, so two copies fit):
56
91
 
57
92
  ```bash
58
93
  vllm serve Qwen/Qwen2.5-3B-Instruct --port 8000 --gpu-memory-utilization 0.45 \
@@ -73,14 +108,16 @@ URLs would reach the same server.
73
108
  The tool then reports the recompute path reusing cached tokens and marks every
74
109
  probe invalid, so check the server logs if that happens.
75
110
 
76
- Tested on real servers on 2026-11-11 and 11-12 (UTC; one RTX 4090; the tool's 20
77
- conversations; details in the [currency note](https://github.com/aditi-p31/cache-divergence-study/blob/main/CURRENCY_CHECK_2026-11.md)):
111
+ Tested on real servers (one RTX 4090; the tool's 20 conversations). The two-server and llama.cpp rows are from 2026-11-11 and 11-12 UTC ([currency note](https://github.com/aditi-p31/cache-divergence-study/blob/main/CURRENCY_CHECK_2026-11.md)); the one-server rows are from 2026-11-14 UTC ([details](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck-0.3-validation/README.md)):
78
112
 
79
113
  | Server | Model | Conversations with a different turn-2 answer | Both caches off |
80
114
  |---|---|---|---|
81
115
  | vLLM 0.33.0, two servers | Qwen2.5-3B-Instruct, BF16 | 14 of 20, in each of 5 runs | 0 of 20 |
82
116
  | SGLang 0.5.23, two servers (CUDA graphs disabled) | Qwen2.5-3B-Instruct, BF16 | 12 of 20, in each of 2 runs | 0 of 20 |
83
117
  | llama.cpp v0.9.0 | Qwen2.5-7B-Instruct, Q4_K_M | 11 of 20, in each of 2 runs | |
118
+ | vLLM 0.33.0, one server | Qwen2.5-3B-Instruct, BF16 | 14 of 20, in each of 2 runs | |
119
+ | SGLang 0.5.23, one server (CUDA graphs disabled) | Qwen2.5-3B-Instruct, BF16 | 12 of 20, in each of 2 runs | |
120
+ | SGLang 0.5.23, one server, default settings | Qwen2.5-3B-Instruct, BF16 | 12 of 20, in each of 2 runs | |
84
121
 
85
122
  If you run it, an [issue](https://github.com/aditi-p31/cache-divergence-study/issues) with what you saw is welcome.
86
123
 
@@ -88,13 +125,56 @@ Two server processes can also differ for other reasons. To check that, start
88
125
  both with prefix caching off and run with `--skip-hit-check`. Every probe
89
126
  should come out the same.
90
127
 
91
- Options: `--n` sets the number of probes (default 20), `--max-tokens` the
92
- answer length (default 256), `--out` the JSON report path.
128
+ Options: `--probes` reads your own conversations (next section), `--n` sets
129
+ the number of probes (default: all), `--max-tokens` the answer length
130
+ (default 256), `--out` the JSON report path. If your server caches in blocks or
131
+ pages larger than 32 tokens, set `--hit-tolerance` to that size, or cache hits
132
+ can look too short. With large pages, also ask for longer turn-1 answers, so
133
+ the hit can reach past the prompt.
93
134
 
94
135
  Models that think before answering need thinking turned off for this check.
95
136
  Their templates often drop past thinking from the history, which shortens the
96
137
  cache hit, and the tool marks those probes as inconclusive.
97
138
 
139
+ ## Run it on your own conversations
140
+
141
+ The built-in conversations are short and generic. To check the kind of
142
+ prompts your application sends, put your own in a JSON Lines file, one
143
+ conversation per line, and pass it with `--probes`:
144
+
145
+ ```bash
146
+ llm-cachecheck --base-url http://127.0.0.1:8080 --probes my_conversations.jsonl
147
+ ```
148
+
149
+ Each line has one of two forms
150
+ ([example file](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck/examples/conversations_example.jsonl)):
151
+
152
+ ```json
153
+ {"id": "returns-1", "turn1": "A customer wants to return shoes bought 40 days ago. ...", "turn2": "Rewrite the reply so it offers store credit instead."}
154
+ {"id": "sql-1", "messages": [{"role": "system", "content": "You are a careful SQL assistant."}, {"role": "user", "content": "Write a query for the ten largest orders."}], "followup": "Change it so it only counts orders created in 2025."}
155
+ ```
156
+
157
+ - `turn1` is the first user message and `turn2` the follow-up.
158
+ - In the longer form, `messages` is the conversation up to and including the
159
+ turn-1 user message, so it can carry your system prompt and earlier turns.
160
+ `followup` is the turn-2 user message.
161
+ - `id` is optional and is copied into the report.
162
+ - Text messages only, with the roles system, developer, user and assistant.
163
+ Tool calls and images are not supported.
164
+
165
+ The tool generates the turn-1 answer itself and builds turn 2 on it, as with
166
+ the built-in conversations, and it always decodes greedily. A file that holds
167
+ one JSON array of these objects also works.
168
+
169
+ Let turn 1 ask for an answer of a few sentences. The tool counts a cache hit
170
+ only when it reaches past the turn-1 prompt into the answer. A server that
171
+ caches in blocks (vLLM uses 16 tokens by default) can stop the hit at the
172
+ prompt when the answer is short, and the probe is then inconclusive.
173
+
174
+ The JSON report saves every answer, so keep it private if your conversations
175
+ are. The printed result block holds no conversation text: only the counts
176
+ and the run settings, including the model name.
177
+
98
178
  ## Reading the result
99
179
 
100
180
  A real run on Apple Silicon (Metal) with Qwen2.5-3B-Instruct at Q4_K_M,
@@ -124,6 +204,9 @@ It took about 15 minutes on a laptop.
124
204
  | 1 | At least one valid probe differed |
125
205
  | 2 | Inconclusive: too few valid probes, or a request failed |
126
206
 
207
+ A usage error (a bad or conflicting option) also exits with status 2, before
208
+ any request is sent and without writing a report.
209
+
127
210
  The JSON report keeps every answer, the finish reasons, token counts, cached
128
211
  token counts and the server build.
129
212