unalloc 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.
- {unalloc-0.2.3 → unalloc-0.3.0}/CHANGELOG.md +15 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/PKG-INFO +59 -7
- {unalloc-0.2.3 → unalloc-0.3.0}/README.md +58 -6
- {unalloc-0.2.3 → unalloc-0.3.0}/pyproject.toml +1 -1
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/__init__.py +1 -1
- unalloc-0.3.0/src/unalloc/cli.py +415 -0
- unalloc-0.3.0/src/unalloc/core/provenance.py +103 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/sources/anthropic.py +2 -2
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/sources/base.py +10 -2
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/sources/litellm.py +2 -2
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/sources/openai.py +2 -2
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/sources/opencost.py +2 -2
- {unalloc-0.2.3 → unalloc-0.3.0}/tests/test_docs_build.py +5 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/tests/test_fetch.py +40 -0
- unalloc-0.3.0/tests/test_provenance.py +157 -0
- unalloc-0.2.3/src/unalloc/cli.py +0 -252
- {unalloc-0.2.3 → unalloc-0.3.0}/.gitignore +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/LICENSE +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/core/__init__.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/core/attribute.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/core/models.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/core/normalize.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/core/reconcile.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/fixtures/anthropic_cost_report.json +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/fixtures/litellm_spend.json +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/fixtures/openai_costs.json +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/fixtures/opencost_allocation.json +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/render/__init__.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/render/table.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/src/unalloc/sources/__init__.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/tests/test_attribute.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/tests/test_cli.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/tests/test_normalize.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/tests/test_paper_lint.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/tests/test_paper_numbers.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/tests/test_reconcile.py +0 -0
- {unalloc-0.2.3 → unalloc-0.3.0}/tests/test_sources.py +0 -0
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 — 2026-10-04
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `report --json` carries a `provenance` block: unalloc version, window (live runs), alias-table digest, one entry per requested source with its status, payload SHA-256, byte count and row count, and a digest of the result body.
|
|
7
|
+
- `report --keep-inputs DIR` writes the exact payloads the run parsed, named like the bundled fixtures, so the run can be replayed without the network.
|
|
8
|
+
- `unalloc verify RESULT --inputs DIR` rebuilds a report from retained inputs. Exit 0 when it reproduces; 3 when an input is missing or its bytes changed at the same path; 4 when the inputs match but the result does not, naming a version or alias-table change; 5 when the recorded result does not conserve its own total.
|
|
9
|
+
- Every command warns on stderr when the ledger is partial, naming each source that failed or had no input.
|
|
10
|
+
- `Source.fetch_payload()` returns the raw provider payload; `fetch()` is now `parse(fetch_payload())` on the base class.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- A failed live source was printed to stderr and then dropped, so `report --json` gave no sign that its percentage covered only some sources. It is now recorded as `failed` in the provenance block.
|
|
14
|
+
- With `--fixtures` and a missing fixture file, the "skipping" notice went to stdout and made `report --json` output invalid JSON. It goes to stderr.
|
|
15
|
+
|
|
16
|
+
Attribution results are unchanged: every fixture report from 0.2.3 reproduces exactly.
|
|
17
|
+
|
|
3
18
|
## 0.2.3 — 2026-09-21
|
|
4
19
|
|
|
5
20
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: unalloc
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Find the AI spend nobody owns. Joins OpenCost allocation data with LLM provider bills and reports what's unattributed.
|
|
5
5
|
Project-URL: Homepage, https://github.com/timurista/unalloc
|
|
6
6
|
Project-URL: Issues, https://github.com/timurista/unalloc/issues
|
|
@@ -47,11 +47,11 @@ Description-Content-Type: text/markdown
|
|
|
47
47
|
[](https://github.com/timurista/unalloc/actions/workflows/ci.yml)
|
|
48
48
|
[](https://github.com/timurista/unalloc/blob/main/LICENSE)
|
|
49
49
|
[](https://github.com/timurista/unalloc/blob/main/pyproject.toml)
|
|
50
|
-
[](https://arxiv.org/abs/2609.24991)
|
|
51
51
|
[](https://timurista.github.io/unalloc/explorer.html)
|
|
52
52
|
[](https://doi.org/10.5281/zenodo.22761012)
|
|
53
53
|
|
|
54
|
-
**Project site: [timurista.github.io/unalloc](https://timurista.github.io/unalloc/)**, with the findings, case studies, [paper (
|
|
54
|
+
**Project site: [timurista.github.io/unalloc](https://timurista.github.io/unalloc/)**, with the findings, case studies, [paper (arXiv:2609.24991)](https://arxiv.org/abs/2609.24991), [blog post](https://timurista.github.io/unalloc/blog/who-pays-for-the-kv-cache.html), [GPU addendum](https://timurista.github.io/unalloc/blog/gpu-validation.html) and [ledger explorer](https://timurista.github.io/unalloc/explorer.html).
|
|
55
55
|
|
|
56
56
|
OpenCost tells you what your Kubernetes workloads cost. Your provider dashboard tells you what your OpenAI and Anthropic calls cost. Getting both into one view is possible today — OpenCost has an OpenAI plugin — but the answer still depends on a join nobody checks: the same dollar can arrive through both your gateway and your provider bill, and the labels that say who owns it are set per workload, not per pod template. `unalloc` pulls all four sources into one normalized ledger, joins them on a label dimension you choose, and reports the number your finance team keeps asking for: how much of this month's AI spend can't be attributed to any team — and how much of the rest was only rescued by a fallback key.
|
|
57
57
|
|
|
@@ -167,6 +167,42 @@ Configure sources by environment variable:
|
|
|
167
167
|
|
|
168
168
|
Restrict the run with `--source` to avoid double counting: if your traffic goes through LiteLLM, prefer `--source opencost --source litellm` and skip the direct provider adapters, since the same tokens appear in both. The [end-to-end case study](#case-studies-and-paper) measures exactly how much that costs you if you forget.
|
|
169
169
|
|
|
170
|
+
## Reproducing a result
|
|
171
|
+
|
|
172
|
+
A percentage that ends up in a finance deck will be questioned later, after the
|
|
173
|
+
source data has moved on. A path is not enough to answer that: a fixture or an
|
|
174
|
+
export can be rewritten under the same name, and a live billing API answers a
|
|
175
|
+
re-run with today's data. So `report --json` carries a `provenance` block, and
|
|
176
|
+
`--keep-inputs` retains the exact payloads it parsed:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
unalloc report -D team --json --keep-inputs runs/2026-09/ > runs/2026-09.json
|
|
180
|
+
unalloc verify runs/2026-09.json --inputs runs/2026-09/
|
|
181
|
+
# reproduced: 73.5% unallocated of $66,630.38, result sha256:…
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The provenance block records the unalloc version, the window (live runs only;
|
|
185
|
+
fixtures carry their own dates), a digest of the label alias table, and one
|
|
186
|
+
entry per requested source: its status, the SHA-256 of the payload bytes, and
|
|
187
|
+
the rows it produced. A source that failed or had no input is recorded as
|
|
188
|
+
`failed` or `missing` rather than dropped, and every command warns on stderr
|
|
189
|
+
when the ledger is partial, because the percentage then covers the loaded
|
|
190
|
+
sources only.
|
|
191
|
+
|
|
192
|
+
`verify` exits with a code that says which boundary moved:
|
|
193
|
+
|
|
194
|
+
| Exit | Meaning |
|
|
195
|
+
| --- | --- |
|
|
196
|
+
| 0 | Reproduced: same inputs, same result digest |
|
|
197
|
+
| 3 | Cannot reconstruct: a retained input is missing, or its bytes changed |
|
|
198
|
+
| 4 | Inputs match but the result differs: a rule or the tool changed in between |
|
|
199
|
+
| 5 | The recorded result does not conserve its own total (a dollar counted twice or dropped) |
|
|
200
|
+
|
|
201
|
+
A digest proves byte identity, not correctness. It cannot tell you a meter was
|
|
202
|
+
accurate or a rule was fair, and it can only rebuild a result when the bytes
|
|
203
|
+
it names were kept. Retained payloads contain your billing data; store them
|
|
204
|
+
like the invoices they came from.
|
|
205
|
+
|
|
170
206
|
## How it works
|
|
171
207
|
|
|
172
208
|
```
|
|
@@ -193,7 +229,7 @@ Two design choices worth calling out:
|
|
|
193
229
|
|
|
194
230
|
## Case studies and paper
|
|
195
231
|
|
|
196
|
-
Six studies push real (or realistically simulated) inference workloads through unalloc's own adapters, including a run with vLLM on an NVIDIA H100. They exposed nine defects in the tool along the way, all fixed with regression tests, and they're written up as a paper: **[paper/unalloc-case-studies.pdf](https://github.com/timurista/unalloc/blob/main/paper/unalloc-case-studies.pdf)
|
|
232
|
+
Six studies push real (or realistically simulated) inference workloads through unalloc's own adapters, including a run with vLLM on an NVIDIA H100. They exposed nine defects in the tool along the way, all fixed with regression tests, and they're written up as a paper: **[Who Pays for the KV Cache? (arXiv:2609.24991)](https://arxiv.org/abs/2609.24991)**, a preprint that has not been peer reviewed. The PDF built from this repository is at [paper/unalloc-case-studies.pdf](https://github.com/timurista/unalloc/blob/main/paper/unalloc-case-studies.pdf). For the readable version, see the [blog post](https://timurista.github.io/unalloc/blog/who-pays-for-the-kv-cache.html) and the [GPU addendum](https://timurista.github.io/unalloc/blog/gpu-validation.html) on the [project site](https://timurista.github.io/unalloc/).
|
|
197
233
|
|
|
198
234
|
| Study | What runs | Headline |
|
|
199
235
|
| --- | --- | --- |
|
|
@@ -245,6 +281,7 @@ What `unalloc` does is narrower: read all four sources yourself — OpenCost, a
|
|
|
245
281
|
- [ ] Per-feature unit economics using the `quantity`/`unit` fields already carried on `CostRow`
|
|
246
282
|
- [ ] Azure OpenAI and Bedrock adapters
|
|
247
283
|
- [x] `--budget` mode: exit non-zero when unallocated share crosses a threshold, for CI
|
|
284
|
+
- [x] Result provenance and `unalloc verify`: rebuild an earlier report from retained inputs, or say why it can't be
|
|
248
285
|
- [ ] `--exclude label=value` on a source, so gateway traffic can be dropped from provider billing instead of whole sources
|
|
249
286
|
- [ ] Warn when distinct raw label keys collide on one canonical key
|
|
250
287
|
|
|
@@ -259,16 +296,31 @@ ruff check . && pytest
|
|
|
259
296
|
|
|
260
297
|
## Citing
|
|
261
298
|
|
|
262
|
-
If you use
|
|
299
|
+
If you use the findings, please cite the paper, a preprint on arXiv ([arXiv:2609.24991](https://arxiv.org/abs/2609.24991), cs.DC):
|
|
300
|
+
|
|
301
|
+
```bibtex
|
|
302
|
+
@misc{urista_kvcache_2026,
|
|
303
|
+
author = {Urista, Timothy},
|
|
304
|
+
title = {Who Pays for the {KV} Cache? Attributing Shared {AI} Inference Spend Across {Kubernetes} and {LLM} Provider Bills},
|
|
305
|
+
year = {2026},
|
|
306
|
+
eprint = {2609.24991},
|
|
307
|
+
archivePrefix = {arXiv},
|
|
308
|
+
primaryClass = {cs.DC},
|
|
309
|
+
doi = {10.48550/arXiv.2609.24991},
|
|
310
|
+
url = {https://arxiv.org/abs/2609.24991}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
If you use the software itself, cite the archived release. GitHub's "Cite this repository" button produces the same entry from [`CITATION.cff`](https://github.com/timurista/unalloc/blob/main/CITATION.cff).
|
|
263
315
|
|
|
264
|
-
- **All versions:** [doi:10.5281/zenodo.22761012](https://doi.org/10.5281/zenodo.22761012), which always resolves to the latest release — currently 0.2.3
|
|
316
|
+
- **All versions:** [doi:10.5281/zenodo.22761012](https://doi.org/10.5281/zenodo.22761012), which always resolves to the latest release — currently 0.3.0. The paper describes 0.2.3; 0.3.0 adds result provenance and `unalloc verify` and changes no attribution result
|
|
265
317
|
- **Version 0.2.1:** [doi:10.5281/zenodo.22761013](https://doi.org/10.5281/zenodo.22761013)
|
|
266
318
|
|
|
267
319
|
```bibtex
|
|
268
320
|
@software{urista_unalloc_2026,
|
|
269
321
|
author = {Urista, Timothy},
|
|
270
322
|
title = {unalloc: find the AI spend nobody owns},
|
|
271
|
-
version = {0.
|
|
323
|
+
version = {0.3.0},
|
|
272
324
|
year = {2026},
|
|
273
325
|
doi = {10.5281/zenodo.22761012},
|
|
274
326
|
url = {https://github.com/timurista/unalloc}
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
[](https://github.com/timurista/unalloc/actions/workflows/ci.yml)
|
|
6
6
|
[](https://github.com/timurista/unalloc/blob/main/LICENSE)
|
|
7
7
|
[](https://github.com/timurista/unalloc/blob/main/pyproject.toml)
|
|
8
|
-
[](https://arxiv.org/abs/2609.24991)
|
|
9
9
|
[](https://timurista.github.io/unalloc/explorer.html)
|
|
10
10
|
[](https://doi.org/10.5281/zenodo.22761012)
|
|
11
11
|
|
|
12
|
-
**Project site: [timurista.github.io/unalloc](https://timurista.github.io/unalloc/)**, with the findings, case studies, [paper (
|
|
12
|
+
**Project site: [timurista.github.io/unalloc](https://timurista.github.io/unalloc/)**, with the findings, case studies, [paper (arXiv:2609.24991)](https://arxiv.org/abs/2609.24991), [blog post](https://timurista.github.io/unalloc/blog/who-pays-for-the-kv-cache.html), [GPU addendum](https://timurista.github.io/unalloc/blog/gpu-validation.html) and [ledger explorer](https://timurista.github.io/unalloc/explorer.html).
|
|
13
13
|
|
|
14
14
|
OpenCost tells you what your Kubernetes workloads cost. Your provider dashboard tells you what your OpenAI and Anthropic calls cost. Getting both into one view is possible today — OpenCost has an OpenAI plugin — but the answer still depends on a join nobody checks: the same dollar can arrive through both your gateway and your provider bill, and the labels that say who owns it are set per workload, not per pod template. `unalloc` pulls all four sources into one normalized ledger, joins them on a label dimension you choose, and reports the number your finance team keeps asking for: how much of this month's AI spend can't be attributed to any team — and how much of the rest was only rescued by a fallback key.
|
|
15
15
|
|
|
@@ -125,6 +125,42 @@ Configure sources by environment variable:
|
|
|
125
125
|
|
|
126
126
|
Restrict the run with `--source` to avoid double counting: if your traffic goes through LiteLLM, prefer `--source opencost --source litellm` and skip the direct provider adapters, since the same tokens appear in both. The [end-to-end case study](#case-studies-and-paper) measures exactly how much that costs you if you forget.
|
|
127
127
|
|
|
128
|
+
## Reproducing a result
|
|
129
|
+
|
|
130
|
+
A percentage that ends up in a finance deck will be questioned later, after the
|
|
131
|
+
source data has moved on. A path is not enough to answer that: a fixture or an
|
|
132
|
+
export can be rewritten under the same name, and a live billing API answers a
|
|
133
|
+
re-run with today's data. So `report --json` carries a `provenance` block, and
|
|
134
|
+
`--keep-inputs` retains the exact payloads it parsed:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
unalloc report -D team --json --keep-inputs runs/2026-09/ > runs/2026-09.json
|
|
138
|
+
unalloc verify runs/2026-09.json --inputs runs/2026-09/
|
|
139
|
+
# reproduced: 73.5% unallocated of $66,630.38, result sha256:…
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The provenance block records the unalloc version, the window (live runs only;
|
|
143
|
+
fixtures carry their own dates), a digest of the label alias table, and one
|
|
144
|
+
entry per requested source: its status, the SHA-256 of the payload bytes, and
|
|
145
|
+
the rows it produced. A source that failed or had no input is recorded as
|
|
146
|
+
`failed` or `missing` rather than dropped, and every command warns on stderr
|
|
147
|
+
when the ledger is partial, because the percentage then covers the loaded
|
|
148
|
+
sources only.
|
|
149
|
+
|
|
150
|
+
`verify` exits with a code that says which boundary moved:
|
|
151
|
+
|
|
152
|
+
| Exit | Meaning |
|
|
153
|
+
| --- | --- |
|
|
154
|
+
| 0 | Reproduced: same inputs, same result digest |
|
|
155
|
+
| 3 | Cannot reconstruct: a retained input is missing, or its bytes changed |
|
|
156
|
+
| 4 | Inputs match but the result differs: a rule or the tool changed in between |
|
|
157
|
+
| 5 | The recorded result does not conserve its own total (a dollar counted twice or dropped) |
|
|
158
|
+
|
|
159
|
+
A digest proves byte identity, not correctness. It cannot tell you a meter was
|
|
160
|
+
accurate or a rule was fair, and it can only rebuild a result when the bytes
|
|
161
|
+
it names were kept. Retained payloads contain your billing data; store them
|
|
162
|
+
like the invoices they came from.
|
|
163
|
+
|
|
128
164
|
## How it works
|
|
129
165
|
|
|
130
166
|
```
|
|
@@ -151,7 +187,7 @@ Two design choices worth calling out:
|
|
|
151
187
|
|
|
152
188
|
## Case studies and paper
|
|
153
189
|
|
|
154
|
-
Six studies push real (or realistically simulated) inference workloads through unalloc's own adapters, including a run with vLLM on an NVIDIA H100. They exposed nine defects in the tool along the way, all fixed with regression tests, and they're written up as a paper: **[paper/unalloc-case-studies.pdf](https://github.com/timurista/unalloc/blob/main/paper/unalloc-case-studies.pdf)
|
|
190
|
+
Six studies push real (or realistically simulated) inference workloads through unalloc's own adapters, including a run with vLLM on an NVIDIA H100. They exposed nine defects in the tool along the way, all fixed with regression tests, and they're written up as a paper: **[Who Pays for the KV Cache? (arXiv:2609.24991)](https://arxiv.org/abs/2609.24991)**, a preprint that has not been peer reviewed. The PDF built from this repository is at [paper/unalloc-case-studies.pdf](https://github.com/timurista/unalloc/blob/main/paper/unalloc-case-studies.pdf). For the readable version, see the [blog post](https://timurista.github.io/unalloc/blog/who-pays-for-the-kv-cache.html) and the [GPU addendum](https://timurista.github.io/unalloc/blog/gpu-validation.html) on the [project site](https://timurista.github.io/unalloc/).
|
|
155
191
|
|
|
156
192
|
| Study | What runs | Headline |
|
|
157
193
|
| --- | --- | --- |
|
|
@@ -203,6 +239,7 @@ What `unalloc` does is narrower: read all four sources yourself — OpenCost, a
|
|
|
203
239
|
- [ ] Per-feature unit economics using the `quantity`/`unit` fields already carried on `CostRow`
|
|
204
240
|
- [ ] Azure OpenAI and Bedrock adapters
|
|
205
241
|
- [x] `--budget` mode: exit non-zero when unallocated share crosses a threshold, for CI
|
|
242
|
+
- [x] Result provenance and `unalloc verify`: rebuild an earlier report from retained inputs, or say why it can't be
|
|
206
243
|
- [ ] `--exclude label=value` on a source, so gateway traffic can be dropped from provider billing instead of whole sources
|
|
207
244
|
- [ ] Warn when distinct raw label keys collide on one canonical key
|
|
208
245
|
|
|
@@ -217,16 +254,31 @@ ruff check . && pytest
|
|
|
217
254
|
|
|
218
255
|
## Citing
|
|
219
256
|
|
|
220
|
-
If you use
|
|
257
|
+
If you use the findings, please cite the paper, a preprint on arXiv ([arXiv:2609.24991](https://arxiv.org/abs/2609.24991), cs.DC):
|
|
258
|
+
|
|
259
|
+
```bibtex
|
|
260
|
+
@misc{urista_kvcache_2026,
|
|
261
|
+
author = {Urista, Timothy},
|
|
262
|
+
title = {Who Pays for the {KV} Cache? Attributing Shared {AI} Inference Spend Across {Kubernetes} and {LLM} Provider Bills},
|
|
263
|
+
year = {2026},
|
|
264
|
+
eprint = {2609.24991},
|
|
265
|
+
archivePrefix = {arXiv},
|
|
266
|
+
primaryClass = {cs.DC},
|
|
267
|
+
doi = {10.48550/arXiv.2609.24991},
|
|
268
|
+
url = {https://arxiv.org/abs/2609.24991}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
If you use the software itself, cite the archived release. GitHub's "Cite this repository" button produces the same entry from [`CITATION.cff`](https://github.com/timurista/unalloc/blob/main/CITATION.cff).
|
|
221
273
|
|
|
222
|
-
- **All versions:** [doi:10.5281/zenodo.22761012](https://doi.org/10.5281/zenodo.22761012), which always resolves to the latest release — currently 0.2.3
|
|
274
|
+
- **All versions:** [doi:10.5281/zenodo.22761012](https://doi.org/10.5281/zenodo.22761012), which always resolves to the latest release — currently 0.3.0. The paper describes 0.2.3; 0.3.0 adds result provenance and `unalloc verify` and changes no attribution result
|
|
223
275
|
- **Version 0.2.1:** [doi:10.5281/zenodo.22761013](https://doi.org/10.5281/zenodo.22761013)
|
|
224
276
|
|
|
225
277
|
```bibtex
|
|
226
278
|
@software{urista_unalloc_2026,
|
|
227
279
|
author = {Urista, Timothy},
|
|
228
280
|
title = {unalloc: find the AI spend nobody owns},
|
|
229
|
-
version = {0.
|
|
281
|
+
version = {0.3.0},
|
|
230
282
|
year = {2026},
|
|
231
283
|
doi = {10.5281/zenodo.22761012},
|
|
232
284
|
url = {https://github.com/timurista/unalloc}
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "unalloc"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Find the AI spend nobody owns. Joins OpenCost allocation data with LLM provider bills and reports what's unattributed."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|