llm-cachecheck 0.2.1__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,5 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ .DS_Store
5
+ dist/
@@ -0,0 +1,27 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aditi Patodiya
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.
22
+
23
+ Scope note: the measurement dataset in this repository (the results/,
24
+ results-repair/ and results-revision/ directories, revision/orders/,
25
+ findings.json, repair_findings.json, and the JSON result files in analysis/) is
26
+ licensed separately under the Creative Commons Attribution 4.0
27
+ International License (CC BY 4.0).
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.5
2
+ Name: llm-cachecheck
3
+ Version: 0.2.1
4
+ Summary: Measure how often the prompt cache changes greedy answers on your LLM server (llama.cpp, plus an experimental two-server mode for vLLM and SGLang)
5
+ Project-URL: Homepage, https://github.com/aditi-p31/cache-divergence-study/tree/main/cachecheck
6
+ Project-URL: Source, https://github.com/aditi-p31/cache-divergence-study
7
+ Project-URL: Paper, https://doi.org/10.1109/ACCESS.2026.3749922
8
+ Project-URL: Preprint, https://arxiv.org/abs/2609.04748
9
+ Project-URL: Issues, https://github.com/aditi-p31/cache-divergence-study/issues
10
+ Author: Aditi Patodiya
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: inference,kv cache,llama.cpp,llm,nondeterminism,prefix caching,prompt cache,reproducibility,sglang,vllm
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: Science/Research
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: Software Development :: Testing
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+
25
+ # llm-cachecheck
26
+
27
+ On PyPI this tool is `llm-cachecheck`. The PyPI package named `cachecheck` is
28
+ a different project.
29
+
30
+ How often does the prompt cache change your server's answers?
31
+
32
+ Serving stacks reuse the cached key-value state of a shared prompt prefix. A
33
+ cache hit and a recomputation run different operations, so with greedy
34
+ decoding a small numeric change can flip a token. After that the answer goes
35
+ its own way. This is known behavior: the llama.cpp server README says that
36
+ `cache_prompt` "can cause nondeterministic results". What varies is how often
37
+ it happens on your model, weights and build. `cachecheck` measures that.
38
+
39
+ It sends short two-turn conversations with greedy decoding and compares the
40
+ second answer on two paths:
41
+
42
+ - **cache path:** turn 2 reuses the state the server wrote while it generated
43
+ turn 1
44
+ - **recompute path:** turn 2 is processed from scratch
45
+
46
+ Each path runs twice. A probe counts as valid only when all of these hold:
47
+
48
+ - both paths repeat on their own
49
+ - the server's own counts show that the cache hit covered the turn-1 answer
50
+ - the server's own counts show that the recompute path reused nothing
51
+
52
+ ## Run it
53
+
54
+ Python 3.8 or newer, no dependencies. Install it from PyPI:
55
+
56
+ ```bash
57
+ pip install llm-cachecheck
58
+ llm-cachecheck --help
59
+ ```
60
+
61
+ Or download the single file
62
+ [`cachecheck.py`](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck/cachecheck.py)
63
+ and run `python cachecheck.py` in place of `llm-cachecheck`. Both take the
64
+ same options. A full run
65
+ sends 120 requests.
66
+
67
+ **llama.cpp.** The cache is switched per request. Start the server with one
68
+ slot, because the default is several slots that share a cache. Turn the
69
+ host-memory prompt cache off to match the study's controlled runs:
70
+
71
+ ```bash
72
+ llama-server -m model.gguf -np 1 --cache-ram 0 --port 8080
73
+ llm-cachecheck --base-url http://127.0.0.1:8080
74
+ ```
75
+
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 GPU:
79
+
80
+ ```bash
81
+ vllm serve Qwen/Qwen2.5-7B-Instruct --port 8000 --gpu-memory-utilization 0.45 \
82
+ --generation-config vllm --enable-prompt-tokens-details
83
+ vllm serve Qwen/Qwen2.5-7B-Instruct --port 8001 --gpu-memory-utilization 0.45 \
84
+ --generation-config vllm --enable-prompt-tokens-details --no-enable-prefix-caching
85
+ llm-cachecheck --mode two-servers --base-url http://127.0.0.1:8000 \
86
+ --nocache-url http://127.0.0.1:8001 --model Qwen/Qwen2.5-7B-Instruct
87
+ ```
88
+
89
+ For SGLang, add `--enable-cache-report` to both servers and
90
+ `--disable-radix-cache` to the second one.
91
+
92
+ The two-server mode is covered by the unit tests against a simulated server.
93
+ It has not yet been run against a real vLLM or SGLang server. If you run it,
94
+ an [issue](https://github.com/aditi-p31/cache-divergence-study/issues) with what you saw is welcome.
95
+
96
+ Two server processes can also differ for other reasons. To check that, start
97
+ both with prefix caching off and run with `--skip-hit-check`. Every probe
98
+ should come out the same.
99
+
100
+ Options: `--n` sets the number of probes (default 20), `--max-tokens` the
101
+ answer length (default 256), `--out` the JSON report path.
102
+
103
+ Models that think before answering need thinking turned off for this check.
104
+ Their templates often drop past thinking from the history, which shortens the
105
+ cache hit, and the tool marks those probes as inconclusive.
106
+
107
+ ## Reading the result
108
+
109
+ A real run on Apple Silicon (Metal) with Qwen2.5-3B-Instruct at Q4_K_M,
110
+ llama.cpp b12138, `-np 1 --cache-ram 0`, default options:
111
+
112
+ ```
113
+ probe 1/20: same
114
+ ...
115
+ probe 10/20: DIFFERENT (from character 578)
116
+ ...
117
+ probe 13/20: DIFFERENT (from character 235)
118
+ ...
119
+ probe 15/20: DIFFERENT (from character 376)
120
+ ...
121
+ 3 of 20 valid probes gave a different turn-2 answer on the cache path.
122
+ ```
123
+
124
+ All 20 probes were valid. On every probe the server reported that the cache
125
+ hit covered all of turn 1 and its answer, and that the recompute path reused
126
+ nothing. The full report (made with a pre-release build of 0.2.0) is in
127
+ [`examples/qwen2.5-3b-q4km_metal_b12138.json`](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck/examples/qwen2.5-3b-q4km_metal_b12138.json).
128
+ It took about 15 minutes on a laptop.
129
+
130
+ | Exit status | Meaning |
131
+ |---|---|
132
+ | 0 | No valid probe differed, and at least 80% of probes were valid |
133
+ | 1 | At least one valid probe differed |
134
+ | 2 | Inconclusive: too few valid probes, or a request failed |
135
+
136
+ The JSON report keeps every answer, the finish reasons, token counts, cached
137
+ token counts and the server build.
138
+
139
+ The rate is specific to these short probes. It is not comparable to the
140
+ episode rates below, where one agent episode chains about ten requests.
141
+
142
+ ## What to do about a difference
143
+
144
+ For runs that must repeat, turn prefix reuse off and record that setting with
145
+ your results:
146
+
147
+ - llama.cpp: `"cache_prompt": false` per request, or `--no-cache-prompt` for
148
+ the server
149
+ - vLLM: `--no-enable-prefix-caching`
150
+ - SGLang: `--disable-radix-cache`
151
+
152
+ A difference is not a bug in your model. It shows that the answer depends on
153
+ the server's cache state as well as on the request.
154
+
155
+ ## Background and citation
156
+
157
+ `cachecheck` adapts the paired cache-on and cache-off design of the study in
158
+ [cache-divergence-study repository](https://github.com/aditi-p31/cache-divergence-study).
159
+ The study used Qwen2.5-7B-Instruct on llama.cpp with greedy
160
+ decoding, 80 BFCL multi-turn agent episodes and the host-memory prompt cache
161
+ off. The share of episodes whose trajectory changed between a cache hit and a
162
+ recomputation was 37.4% with F16 weights and 82.5% with Q4_K_M weights, as a
163
+ mean over ten server histories.
164
+
165
+ If `cachecheck` helps your work, please cite the paper:
166
+
167
+ ```bibtex
168
+ @article{patodiya2026samerequest,
169
+ title = {Same Request, Different Answer: Quantization Amplifies
170
+ Cache-Induced Divergence in LLM Serving},
171
+ author = {Patodiya, Aditi},
172
+ journal = {IEEE Access},
173
+ year = {2026},
174
+ doi = {10.1109/ACCESS.2026.3749922},
175
+ note = {Early Access},
176
+ url = {https://doi.org/10.1109/ACCESS.2026.3749922}
177
+ }
178
+ ```
179
+
180
+ MIT License. See `LICENSE`.
@@ -0,0 +1,156 @@
1
+ # llm-cachecheck
2
+
3
+ On PyPI this tool is `llm-cachecheck`. The PyPI package named `cachecheck` is
4
+ a different project.
5
+
6
+ How often does the prompt cache change your server's answers?
7
+
8
+ Serving stacks reuse the cached key-value state of a shared prompt prefix. A
9
+ cache hit and a recomputation run different operations, so with greedy
10
+ decoding a small numeric change can flip a token. After that the answer goes
11
+ its own way. This is known behavior: the llama.cpp server README says that
12
+ `cache_prompt` "can cause nondeterministic results". What varies is how often
13
+ it happens on your model, weights and build. `cachecheck` measures that.
14
+
15
+ It sends short two-turn conversations with greedy decoding and compares the
16
+ second answer on two paths:
17
+
18
+ - **cache path:** turn 2 reuses the state the server wrote while it generated
19
+ turn 1
20
+ - **recompute path:** turn 2 is processed from scratch
21
+
22
+ Each path runs twice. A probe counts as valid only when all of these hold:
23
+
24
+ - both paths repeat on their own
25
+ - the server's own counts show that the cache hit covered the turn-1 answer
26
+ - the server's own counts show that the recompute path reused nothing
27
+
28
+ ## Run it
29
+
30
+ Python 3.8 or newer, no dependencies. Install it from PyPI:
31
+
32
+ ```bash
33
+ pip install llm-cachecheck
34
+ llm-cachecheck --help
35
+ ```
36
+
37
+ Or download the single file
38
+ [`cachecheck.py`](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck/cachecheck.py)
39
+ and run `python cachecheck.py` in place of `llm-cachecheck`. Both take the
40
+ same options. A full run
41
+ sends 120 requests.
42
+
43
+ **llama.cpp.** The cache is switched per request. Start the server with one
44
+ slot, because the default is several slots that share a cache. Turn the
45
+ host-memory prompt cache off to match the study's controlled runs:
46
+
47
+ ```bash
48
+ llama-server -m model.gguf -np 1 --cache-ram 0 --port 8080
49
+ llm-cachecheck --base-url http://127.0.0.1:8080
50
+ ```
51
+
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 GPU:
55
+
56
+ ```bash
57
+ vllm serve Qwen/Qwen2.5-7B-Instruct --port 8000 --gpu-memory-utilization 0.45 \
58
+ --generation-config vllm --enable-prompt-tokens-details
59
+ vllm serve Qwen/Qwen2.5-7B-Instruct --port 8001 --gpu-memory-utilization 0.45 \
60
+ --generation-config vllm --enable-prompt-tokens-details --no-enable-prefix-caching
61
+ llm-cachecheck --mode two-servers --base-url http://127.0.0.1:8000 \
62
+ --nocache-url http://127.0.0.1:8001 --model Qwen/Qwen2.5-7B-Instruct
63
+ ```
64
+
65
+ For SGLang, add `--enable-cache-report` to both servers and
66
+ `--disable-radix-cache` to the second one.
67
+
68
+ The two-server mode is covered by the unit tests against a simulated server.
69
+ It has not yet been run against a real vLLM or SGLang server. If you run it,
70
+ an [issue](https://github.com/aditi-p31/cache-divergence-study/issues) with what you saw is welcome.
71
+
72
+ Two server processes can also differ for other reasons. To check that, start
73
+ both with prefix caching off and run with `--skip-hit-check`. Every probe
74
+ should come out the same.
75
+
76
+ Options: `--n` sets the number of probes (default 20), `--max-tokens` the
77
+ answer length (default 256), `--out` the JSON report path.
78
+
79
+ Models that think before answering need thinking turned off for this check.
80
+ Their templates often drop past thinking from the history, which shortens the
81
+ cache hit, and the tool marks those probes as inconclusive.
82
+
83
+ ## Reading the result
84
+
85
+ A real run on Apple Silicon (Metal) with Qwen2.5-3B-Instruct at Q4_K_M,
86
+ llama.cpp b12138, `-np 1 --cache-ram 0`, default options:
87
+
88
+ ```
89
+ probe 1/20: same
90
+ ...
91
+ probe 10/20: DIFFERENT (from character 578)
92
+ ...
93
+ probe 13/20: DIFFERENT (from character 235)
94
+ ...
95
+ probe 15/20: DIFFERENT (from character 376)
96
+ ...
97
+ 3 of 20 valid probes gave a different turn-2 answer on the cache path.
98
+ ```
99
+
100
+ All 20 probes were valid. On every probe the server reported that the cache
101
+ hit covered all of turn 1 and its answer, and that the recompute path reused
102
+ nothing. The full report (made with a pre-release build of 0.2.0) is in
103
+ [`examples/qwen2.5-3b-q4km_metal_b12138.json`](https://github.com/aditi-p31/cache-divergence-study/blob/main/cachecheck/examples/qwen2.5-3b-q4km_metal_b12138.json).
104
+ It took about 15 minutes on a laptop.
105
+
106
+ | Exit status | Meaning |
107
+ |---|---|
108
+ | 0 | No valid probe differed, and at least 80% of probes were valid |
109
+ | 1 | At least one valid probe differed |
110
+ | 2 | Inconclusive: too few valid probes, or a request failed |
111
+
112
+ The JSON report keeps every answer, the finish reasons, token counts, cached
113
+ token counts and the server build.
114
+
115
+ The rate is specific to these short probes. It is not comparable to the
116
+ episode rates below, where one agent episode chains about ten requests.
117
+
118
+ ## What to do about a difference
119
+
120
+ For runs that must repeat, turn prefix reuse off and record that setting with
121
+ your results:
122
+
123
+ - llama.cpp: `"cache_prompt": false` per request, or `--no-cache-prompt` for
124
+ the server
125
+ - vLLM: `--no-enable-prefix-caching`
126
+ - SGLang: `--disable-radix-cache`
127
+
128
+ A difference is not a bug in your model. It shows that the answer depends on
129
+ the server's cache state as well as on the request.
130
+
131
+ ## Background and citation
132
+
133
+ `cachecheck` adapts the paired cache-on and cache-off design of the study in
134
+ [cache-divergence-study repository](https://github.com/aditi-p31/cache-divergence-study).
135
+ The study used Qwen2.5-7B-Instruct on llama.cpp with greedy
136
+ decoding, 80 BFCL multi-turn agent episodes and the host-memory prompt cache
137
+ off. The share of episodes whose trajectory changed between a cache hit and a
138
+ recomputation was 37.4% with F16 weights and 82.5% with Q4_K_M weights, as a
139
+ mean over ten server histories.
140
+
141
+ If `cachecheck` helps your work, please cite the paper:
142
+
143
+ ```bibtex
144
+ @article{patodiya2026samerequest,
145
+ title = {Same Request, Different Answer: Quantization Amplifies
146
+ Cache-Induced Divergence in LLM Serving},
147
+ author = {Patodiya, Aditi},
148
+ journal = {IEEE Access},
149
+ year = {2026},
150
+ doi = {10.1109/ACCESS.2026.3749922},
151
+ note = {Early Access},
152
+ url = {https://doi.org/10.1109/ACCESS.2026.3749922}
153
+ }
154
+ ```
155
+
156
+ MIT License. See `LICENSE`.