ai-accountant 0.0.2__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.
- ai_accountant-0.0.2/LICENSE +202 -0
- ai_accountant-0.0.2/MANIFEST.in +16 -0
- ai_accountant-0.0.2/NOTICE +4 -0
- ai_accountant-0.0.2/PKG-INFO +399 -0
- ai_accountant-0.0.2/README.md +377 -0
- ai_accountant-0.0.2/accountant.py +794 -0
- ai_accountant-0.0.2/accountant_pricing/__init__.py +15 -0
- ai_accountant-0.0.2/accountant_pricing/campaigns/__init__.py +8 -0
- ai_accountant-0.0.2/accountant_pricing/campaigns/facade.py +84 -0
- ai_accountant-0.0.2/accountant_pricing/campaigns/interfaces.py +11 -0
- ai_accountant-0.0.2/accountant_pricing/campaigns/models.py +116 -0
- ai_accountant-0.0.2/accountant_pricing/campaigns/plugins/__init__.py +1 -0
- ai_accountant-0.0.2/accountant_pricing/campaigns/plugins/structured.py +47 -0
- ai_accountant-0.0.2/accountant_pricing/campaigns/plugins/text.py +96 -0
- ai_accountant-0.0.2/accountant_pricing/models.py +126 -0
- ai_accountant-0.0.2/accountant_pricing/providers/__init__.py +25 -0
- ai_accountant-0.0.2/accountant_pricing/providers/anthropic.py +26 -0
- ai_accountant-0.0.2/accountant_pricing/providers/deepseek.py +6 -0
- ai_accountant-0.0.2/accountant_pricing/providers/openai.py +19 -0
- ai_accountant-0.0.2/accountant_pricing/providers/openrouter.py +13 -0
- ai_accountant-0.0.2/accountant_pricing/providers/zai.py +6 -0
- ai_accountant-0.0.2/accountant_pricing/resolver.py +160 -0
- ai_accountant-0.0.2/accountant_pricing/sources.py +122 -0
- ai_accountant-0.0.2/ai_accountant/__init__.py +19 -0
- ai_accountant-0.0.2/ai_accountant/sessions/__init__.py +6 -0
- ai_accountant-0.0.2/ai_accountant/sessions/models.py +56 -0
- ai_accountant-0.0.2/ai_accountant/sessions/parsers/__init__.py +1 -0
- ai_accountant-0.0.2/ai_accountant/sessions/parsers/claude.py +270 -0
- ai_accountant-0.0.2/ai_accountant/sessions/parsers/codex.py +159 -0
- ai_accountant-0.0.2/ai_accountant/sessions/parsers/machina.py +301 -0
- ai_accountant-0.0.2/ai_accountant/sessions/reader.py +154 -0
- ai_accountant-0.0.2/ai_accountant.egg-info/PKG-INFO +399 -0
- ai_accountant-0.0.2/ai_accountant.egg-info/SOURCES.txt +58 -0
- ai_accountant-0.0.2/ai_accountant.egg-info/dependency_links.txt +1 -0
- ai_accountant-0.0.2/ai_accountant.egg-info/entry_points.txt +2 -0
- ai_accountant-0.0.2/ai_accountant.egg-info/scm_file_list.json +78 -0
- ai_accountant-0.0.2/ai_accountant.egg-info/top_level.txt +6 -0
- ai_accountant-0.0.2/claude_sessions.py +5 -0
- ai_accountant-0.0.2/docs/README.md +116 -0
- ai_accountant-0.0.2/docs/campaign-classification.md +110 -0
- ai_accountant-0.0.2/docs/pricing-library.md +213 -0
- ai_accountant-0.0.2/docs/releasing.md +169 -0
- ai_accountant-0.0.2/docs/session-library.md +125 -0
- ai_accountant-0.0.2/docs/source-map.md +48 -0
- ai_accountant-0.0.2/machina_sessions.py +5 -0
- ai_accountant-0.0.2/prices.json +145 -0
- ai_accountant-0.0.2/pricing_index.py +92 -0
- ai_accountant-0.0.2/pyproject.toml +41 -0
- ai_accountant-0.0.2/scripts/check_distribution.py +128 -0
- ai_accountant-0.0.2/scripts/smoke_installed.py +97 -0
- ai_accountant-0.0.2/setup.cfg +4 -0
- ai_accountant-0.0.2/tests/test_accountant.py +379 -0
- ai_accountant-0.0.2/tests/test_campaigns.py +164 -0
- ai_accountant-0.0.2/tests/test_claude_sessions.py +219 -0
- ai_accountant-0.0.2/tests/test_machina_sessions.py +198 -0
- ai_accountant-0.0.2/tests/test_multiclient.py +298 -0
- ai_accountant-0.0.2/tests/test_pricing_library.py +376 -0
- ai_accountant-0.0.2/tests/test_release_checks.py +134 -0
- ai_accountant-0.0.2/tests/test_scm_builds.py +97 -0
- ai_accountant-0.0.2/tests/test_session_library.py +157 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
include LICENSE NOTICE README.md
|
|
2
|
+
include prices.json
|
|
3
|
+
include docs/README.md docs/source-map.md docs/pricing-library.md
|
|
4
|
+
include docs/campaign-classification.md docs/releasing.md docs/session-library.md
|
|
5
|
+
recursive-include tests test_*.py
|
|
6
|
+
recursive-include scripts *.py
|
|
7
|
+
prune .uv-cache
|
|
8
|
+
prune .venv
|
|
9
|
+
prune build
|
|
10
|
+
prune dist
|
|
11
|
+
prune docs/in-work
|
|
12
|
+
prune docs/historical
|
|
13
|
+
prune .github
|
|
14
|
+
exclude .gitattributes .gitignore uv.lock
|
|
15
|
+
exclude ai_accountant.egg-info/scm_version.json
|
|
16
|
+
global-exclude __pycache__ *.py[cod] *.sqlite* *.db *.db-* *.jsonl *.csv .env .env.*
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ai-accountant
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Provider pricing, token cost calculation, and local AI session accounting
|
|
5
|
+
Author: Joakim Henriksson
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Keywords: ai,pricing,tokens,accounting
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
License-File: NOTICE
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# AI Accountant
|
|
24
|
+
|
|
25
|
+
A Python library for reading AI session usage and calculating token costs, with
|
|
26
|
+
a bundled local accounting CLI. Licensed under
|
|
27
|
+
[Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).
|
|
28
|
+
|
|
29
|
+
The distribution name is `ai-accountant`; the primary import is `ai_accountant`.
|
|
30
|
+
Release versions are derived from Git tags. Builds from a source distribution
|
|
31
|
+
retain that version without needing Git.
|
|
32
|
+
Install this checkout for development:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
uv pip install .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from ai_accountant import SessionReader, ModelPrice, calculate_cost
|
|
40
|
+
|
|
41
|
+
reader = SessionReader()
|
|
42
|
+
# sources = reader.discover("codex", home="/path/to/.codex")
|
|
43
|
+
# session = reader.parse(sources[0], client="codex")
|
|
44
|
+
|
|
45
|
+
price = ModelPrice("example", "model", {"input": "2", "output": "10"}, "example")
|
|
46
|
+
cost = calculate_cost({"input_tokens": 1000, "output_tokens": 100}, price)
|
|
47
|
+
print(cost.amount_usd) # Decimal('0.003')
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Library
|
|
51
|
+
|
|
52
|
+
### Session reading
|
|
53
|
+
|
|
54
|
+
`SessionReader` discovers local Codex, Claude Code and Machina accounting sources.
|
|
55
|
+
The client is explicit; discovery and parsing only read files when called.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from ai_accountant import SessionReader
|
|
59
|
+
|
|
60
|
+
reader = SessionReader()
|
|
61
|
+
for path in reader.discover("codex"): # or "claude" / "machina"
|
|
62
|
+
session = reader.parse(path, client="codex")
|
|
63
|
+
print(session.session_id, session.models, session.warnings)
|
|
64
|
+
for event in session.events:
|
|
65
|
+
print(event["provider"], event["model"], event["tokens"])
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Pass `home="/path/to/store"` to `discover` to override the client's default
|
|
69
|
+
location. A parsed session contains metadata, per-model totals, normalized usage
|
|
70
|
+
observations, warnings and Codex tool statistics. It does not contain a full
|
|
71
|
+
conversation replay. SQLite indexing and reports belong to the CLI.
|
|
72
|
+
|
|
73
|
+
Normalized `input_tokens` includes fresh input, cache reads and cache writes;
|
|
74
|
+
`cached_input_tokens` and `cache_write_input_tokens` are subsets. Five-minute
|
|
75
|
+
and one-hour write counts are subsets of cache writes. Reasoning is included
|
|
76
|
+
in output. Use `usage_complete` and `reasoning_known` to interpret missing
|
|
77
|
+
evidence; an omitted category represented as zero is not proof of measured zero.
|
|
78
|
+
Source-specific parsing deduplicates usage snapshots and excludes inherited
|
|
79
|
+
Codex fork counters. Provider and model identities are preserved.
|
|
80
|
+
|
|
81
|
+
### Provider pricing
|
|
82
|
+
|
|
83
|
+
`default_resolver()` supplies provider modules for OpenAI, Anthropic, DeepSeek,
|
|
84
|
+
Z.ai and OpenRouter. Refresh explicitly; import, construction and lookup perform
|
|
85
|
+
no networking. Most built-ins currently use the LiteLLM community catalogue;
|
|
86
|
+
OpenRouter uses its own public API. Provenance is retained.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import asyncio
|
|
90
|
+
from ai_accountant import default_resolver
|
|
91
|
+
|
|
92
|
+
prices = default_resolver()
|
|
93
|
+
results = asyncio.run(prices.refresh("anthropic"))
|
|
94
|
+
for result in results:
|
|
95
|
+
print(result.status, result.error)
|
|
96
|
+
for price in prices.search("*opus*", provider="anthropic"):
|
|
97
|
+
print(price.qualified_id, price.rates)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Prices use Decimal USD per million tokens. Refresh failures retain the last good
|
|
101
|
+
snapshot. A host owns refresh scheduling, persistence and its event history.
|
|
102
|
+
`calculate_cost` accepts normalized counts and a `ModelPrice`; pass
|
|
103
|
+
`usage_complete=event["usage_complete"]` when pricing parsed observations.
|
|
104
|
+
Missing prices or consumed rates produce an unknown cost, never an assumed zero.
|
|
105
|
+
Context-tier pricing needs the per-request context size. These quotes estimate
|
|
106
|
+
API cost, not subscription quota usage or reconstructed historical invoices.
|
|
107
|
+
|
|
108
|
+
Custom provider and session-parser implementations can be registered explicitly.
|
|
109
|
+
Installed plugin discovery is opt-in. For API signatures and docstrings:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from ai_accountant import SessionReader, PricingResolver, calculate_cost
|
|
113
|
+
|
|
114
|
+
help(SessionReader)
|
|
115
|
+
help(PricingResolver)
|
|
116
|
+
help(calculate_cost)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The source distribution includes detailed references at `docs/session-library.md`,
|
|
120
|
+
`docs/pricing-library.md` and `docs/campaign-classification.md`, with maintainer
|
|
121
|
+
release instructions in `docs/releasing.md`. They are archive paths, not hosted
|
|
122
|
+
documentation links.
|
|
123
|
+
|
|
124
|
+
## CLI
|
|
125
|
+
|
|
126
|
+
A Python CLI run with uv. It reads local Codex rollouts, Claude Code transcripts,
|
|
127
|
+
and Machina turns, then reports token usage per model and estimated USD cost.
|
|
128
|
+
SQLite indexing makes combined history queryable. Codex remains the default
|
|
129
|
+
source so existing commands keep their behavior.
|
|
130
|
+
|
|
131
|
+
The installable `accountant_pricing` compatibility facade provides
|
|
132
|
+
provider plugins, live price acquisition, an in-memory resolver and pure Decimal
|
|
133
|
+
calculation for other programs. Consumers own their usage histories; SQLite
|
|
134
|
+
snapshot persistence belongs to this CLI.
|
|
135
|
+
|
|
136
|
+
```powershell
|
|
137
|
+
uv run accountant.py --list
|
|
138
|
+
uv run accountant.py SESSION_UUID
|
|
139
|
+
uv run accountant.py SESSION_UUID --parent-only
|
|
140
|
+
uv run accountant.py --latest
|
|
141
|
+
uv run accountant.py 'C:\path\to\rollout.jsonl' --prices prices.json
|
|
142
|
+
uv run accountant.py --all --prices prices.json
|
|
143
|
+
uv run accountant.py --resets --prices prices.json
|
|
144
|
+
uv run accountant.py --reset latest --prices prices.json
|
|
145
|
+
uv run accountant.py --reset 1791580844 --window primary --json
|
|
146
|
+
uv run accountant.py --client claude --list
|
|
147
|
+
uv run accountant.py --client machina --all --json
|
|
148
|
+
uv run accountant.py --client all --all --json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Use `--client codex|claude|machina|all` to choose a source. `--all` aggregates
|
|
152
|
+
the clients selected by `--client`. Codex history defaults to `$CODEX_HOME` or
|
|
153
|
+
`~/.codex`; Claude uses `CLAUDE_CONFIG_DIR` or `~/.claude`; Machina uses
|
|
154
|
+
`MACHINA_HOME` or `~/.machina`. Override those locations with `--codex-home`,
|
|
155
|
+
`--claude-home`, and `--machina-home`. Claude and Machina session IDs are
|
|
156
|
+
prefixed with `claude:` and `machina:` in combined reports and the database;
|
|
157
|
+
Codex UUIDs remain unchanged. `--latest` picks the most recently modified file
|
|
158
|
+
among selected clients, and `--list` includes each session's client.
|
|
159
|
+
|
|
160
|
+
`--reset` accepts `latest`, a Unix reset deadline, or an ISO timestamp with a
|
|
161
|
+
timezone. `--window secondary` selects the secondary quota. Window lengths come
|
|
162
|
+
from the logs; primary does not necessarily mean a five-hour window. `--latest`
|
|
163
|
+
selects the most recently modified file, including archived and subagent sessions.
|
|
164
|
+
Use a UUID or path when selecting a particular conversation. Unique UUID prefixes
|
|
165
|
+
work too. `--codex-home PATH` supports other installations or exported histories.
|
|
166
|
+
Session reports include recursively linked subagent rollouts by default, even
|
|
167
|
+
when archived. `--parent-only` excludes them. JSON includes a per-session
|
|
168
|
+
breakdown. Only explicit parent-thread links are followed; forks are separate
|
|
169
|
+
conversations. Agents with no parent link cannot be attributed to a conversation.
|
|
170
|
+
|
|
171
|
+
Codex session reports also tally tool requests by name and recorded completed tool
|
|
172
|
+
actions by type (commands, file changes, web extensions, and MCP calls). These
|
|
173
|
+
are two overlapping views: an `exec` request can run several underlying tools,
|
|
174
|
+
so do not add the two totals. Outputs and collaboration UI echoes are excluded;
|
|
175
|
+
repeated call/action IDs and copied pre-creation fork history are not counted
|
|
176
|
+
again. Counts include recorded failed actions and requests without outputs.
|
|
177
|
+
Shell commands can perform multiple file reads or searches; individual file
|
|
178
|
+
operations and hidden tool reviews cannot be reliably inferred from these counts.
|
|
179
|
+
Run `--sync` to populate tool statistics in an older index. An index without
|
|
180
|
+
these statistics reports them as unavailable rather than zero. Claude and Machina
|
|
181
|
+
report tool statistics as unavailable. Aggregate token and quota reports do not
|
|
182
|
+
include tool counts.
|
|
183
|
+
|
|
184
|
+
## SQLite
|
|
185
|
+
|
|
186
|
+
```powershell
|
|
187
|
+
uv run accountant.py --sync
|
|
188
|
+
uv run accountant.py SESSION_UUID --db accountant.sqlite3
|
|
189
|
+
uv run accountant.py --all --db accountant.sqlite3 --prices prices.json
|
|
190
|
+
uv run accountant.py --resets --db accountant.sqlite3 --prices prices.json
|
|
191
|
+
uv run accountant.py --reset latest --db accountant.sqlite3 --prices prices.json
|
|
192
|
+
uv run accountant.py --query 'SELECT * FROM model_usage ORDER BY total_tokens DESC'
|
|
193
|
+
uv run accountant.py --query 'SELECT * FROM reset_usage ORDER BY resets_at, model'
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Run `--sync` to migrate older indexes before using session or aggregate reports;
|
|
197
|
+
read-only SQL remains available on old schemas. Sync reparses available sources
|
|
198
|
+
when the parser version changes, and backfills missing Codex tool statistics.
|
|
199
|
+
Sources that are no longer available retain their indexed data; migrated legacy
|
|
200
|
+
events remain conservatively marked incomplete until reimported.
|
|
201
|
+
|
|
202
|
+
Select clients with `--client` when syncing. Sync again to import new or changed
|
|
203
|
+
logs. Unchanged files are skipped using size and modification time; changed files
|
|
204
|
+
are replaced transactionally. Archiving
|
|
205
|
+
a session updates its path without duplicating usage. Sessions whose logs are later
|
|
206
|
+
removed remain in the database. The database contains usage metadata, not prompts
|
|
207
|
+
or responses. `--db PATH` overrides the database location. Aggregate commands with
|
|
208
|
+
`--db` read the indexed snapshot; commands without it read the logs directly.
|
|
209
|
+
`--sync --rebuild` refreshes only the selected client index; use
|
|
210
|
+
`--client all --sync --rebuild` to rebuild every client.
|
|
211
|
+
|
|
212
|
+
Tables: `sessions` (including the source client), `usage_events` (usage increments,
|
|
213
|
+
provider, source-event identity, completeness flags, cache TTL counts, and raw
|
|
214
|
+
rate-limit JSON), and
|
|
215
|
+
`rate_limits` (primary/secondary observations), plus `session_links` (parent,
|
|
216
|
+
fork, and source metadata), and `tool_statistics` (Codex tool counts).
|
|
217
|
+
Views: `model_usage`, `reset_usage`, and
|
|
218
|
+
`session_roots`, which maps each session to itself and its recursively linked
|
|
219
|
+
descendants. The recursive view guards against cycles. `--query` opens SQLite
|
|
220
|
+
read-only and prints JSON. For example, aggregate a root session and all its
|
|
221
|
+
descendants by model:
|
|
222
|
+
|
|
223
|
+
```sql
|
|
224
|
+
SELECT u.model, SUM(u.input_tokens + u.output_tokens) AS total_tokens
|
|
225
|
+
FROM session_roots r
|
|
226
|
+
JOIN usage_events u ON u.session_id = r.session_id
|
|
227
|
+
WHERE r.root_session_id = 'SESSION_UUID'
|
|
228
|
+
GROUP BY u.model
|
|
229
|
+
ORDER BY total_tokens DESC;
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Other queries:
|
|
233
|
+
|
|
234
|
+
```sql
|
|
235
|
+
SELECT u.model, SUM(u.input_tokens + u.output_tokens) AS total_tokens
|
|
236
|
+
FROM usage_events u
|
|
237
|
+
WHERE julianday(u.timestamp) >= julianday('2026-10-01T00:00:00Z')
|
|
238
|
+
AND julianday(u.timestamp) < julianday('2026-10-08T00:00:00Z')
|
|
239
|
+
GROUP BY u.model;
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Prices
|
|
243
|
+
|
|
244
|
+
The script automatically loads `prices.json` beside `accountant.py`. This base
|
|
245
|
+
contains the user's supplied OpenAI rates and officially sourced DeepSeek,
|
|
246
|
+
Anthropic and Z.ai estimates,
|
|
247
|
+
in **USD per million tokens**. OpenAI rates were supplied on 2026-10-04 and were
|
|
248
|
+
not independently fetched during onboarding. Use `--prices FILE` to override the base. For example:
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"gpt-6.1-sol": {"input": 2.0, "cached_input": 0.1, "output": 10.0},
|
|
253
|
+
"gpt-6-luna": {"input": 0.1, "cached_input": 0.01, "output": 0.5}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The flat sheet is not fetched automatically. Provider price acquisition is opt-in:
|
|
258
|
+
|
|
259
|
+
```powershell
|
|
260
|
+
uv run accountant.py --refresh-prices --db accountant.sqlite3
|
|
261
|
+
uv run accountant.py --refresh-prices --price-provider openai --force-prices
|
|
262
|
+
uv run accountant.py --price-history --price-provider openai
|
|
263
|
+
uv run accountant.py --all --db accountant.sqlite3 --price-provider openai --json
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The source archive's `docs/pricing-library.md` details source coverage,
|
|
267
|
+
plugin extension, TTL, failure retention and snapshot history. Snapshot reports
|
|
268
|
+
use standard base-context API reference rates and include provenance/assumptions.
|
|
269
|
+
These are current estimates rather than reconstructed invoices.
|
|
270
|
+
|
|
271
|
+
GPT-5.5 Pro's "no discount" is represented
|
|
272
|
+
by a cached-input rate equal to its input rate. The user supplied `gpt-5.6-luna`
|
|
273
|
+
rates matching `gpt-6-luna`, and `gpt-5.6-terra` input rates matching `gpt-6.1-sol`
|
|
274
|
+
with output at $12 per million tokens. The user supplied free pricing for
|
|
275
|
+
`codex-auto-review`; all its token rates are zero. No other aliases are inferred.
|
|
276
|
+
DeepSeek V4 Flash and Pro have explicit bare and `deepseek/`-prefixed entries.
|
|
277
|
+
Their current [official prices](https://api-docs.deepseek.com/quick_start/pricing/)
|
|
278
|
+
were retrieved via the [DeepSeek homepage](https://www.deepseek.com/) on
|
|
279
|
+
2026-10-12. The flat configuration uses **peak** rates: input/cache/output
|
|
280
|
+
$0.30/$0.006/$1.20 for Flash and $1.32/$0.044/$3.96 for Pro, per million tokens.
|
|
281
|
+
Off-peak rates are half and retained as metadata; the loader does not apply them.
|
|
282
|
+
Peak hours are weekdays 01:00–04:00 and 06:00–10:00 UTC excluding Chinese public
|
|
283
|
+
holidays. The official page explicitly bills the retired V4 Flash alias at
|
|
284
|
+
current V4.1 Flash rates; Pro currently identifies V4-Pro-0813. These are current
|
|
285
|
+
direct-provider estimates applied to history, not reconstructed invoices. Codex
|
|
286
|
+
DeepSeek records report provider `cog`; its actual charges are not established.
|
|
287
|
+
|
|
288
|
+
The observed `claude-opus-4-8` model has [official Anthropic rates](https://platform.claude.com/docs/en/models/opus-4-8/overview)
|
|
289
|
+
retrieved on 2026-10-12: input/cache-read/output $5/$0.50/$25 per million,
|
|
290
|
+
with separate five-minute and one-hour cache-write rates $6.25/$10.
|
|
291
|
+
Unclassified cache writes have no assumed rate. The user reports using alternative
|
|
292
|
+
endpoints rather than Anthropic; for these logs the Opus rate is a hypothetical
|
|
293
|
+
comparison. The CLI still prices exact model labels and does not validate the
|
|
294
|
+
serving endpoint or actual underlying model.
|
|
295
|
+
|
|
296
|
+
The observed `glm-5.2` model has [official Z.ai rates](https://docs.z.ai/guides/overview/pricing)
|
|
297
|
+
retrieved on 2026-10-12: input/cache-read/output $1.40/$0.26/$4.40 per million.
|
|
298
|
+
Cache storage is temporarily free on that page; it is not mapped to a token
|
|
299
|
+
cache-write rate. These are standard API estimates, not Coding Plan subscription
|
|
300
|
+
charges. Incomplete GLM usage remains unpriced even with known model rates.
|
|
301
|
+
|
|
302
|
+
Optional `cache_write` supplies a generic cache-write rate. `cache_write_5m` and
|
|
303
|
+
`cache_write_1h` can price the two TTL buckets separately; each overrides the
|
|
304
|
+
generic rate for its bucket. Unclassified writes still need `cache_write`.
|
|
305
|
+
Unknown models or missing applicable rates yield an unknown total, with a priced
|
|
306
|
+
subtotal. Exact names avoid guessing prices for aliases. Estimates apply the supplied
|
|
307
|
+
flat rates to all history; they do not infer service-tier multipliers, historical
|
|
308
|
+
price changes, long-context rates, subscription charges, or tool fees.
|
|
309
|
+
|
|
310
|
+
Cost = `(uncached input × input rate + cached input × cached rate + cache-write
|
|
311
|
+
input × cache-write rate + output × output rate) / 1,000,000`.
|
|
312
|
+
Cached and cache-write tokens are treated as subsets of input. Cache-write TTL
|
|
313
|
+
counts are subsets of total cache writes. Reports show `cached_percent` as cached
|
|
314
|
+
input divided by inclusive input, and `reasoning_output_tokens: null` when a
|
|
315
|
+
client does not report reasoning counts. Incomplete usage is retained and warned
|
|
316
|
+
about; its cost is not treated as zero. A model with any incomplete usage is left
|
|
317
|
+
out of the priced subtotal as a whole. Provider names are stored only when a log
|
|
318
|
+
reports them. Rates apply only to explicit entries in the price sheet, including the observed
|
|
319
|
+
Anthropic and Z.ai model IDs. Claude Code client identity does not imply an
|
|
320
|
+
Anthropic model or endpoint. Older
|
|
321
|
+
Machina turn totals with ambiguous input/cache semantics stay visible as observed
|
|
322
|
+
values, carry an incomplete flag, and do not produce a cached percentage or cost.
|
|
323
|
+
Current canonical Machina request rounds preserve direct input, cache reads,
|
|
324
|
+
cache writes, and output separately.
|
|
325
|
+
Machina `session.yaml` metadata is read with PyYAML's safe loader when PyYAML is
|
|
326
|
+
installed; without it, the reader uses transcript-directory identity and still
|
|
327
|
+
accounts for current canonical request rounds. To enable YAML metadata for one
|
|
328
|
+
run, use `uv run --with pyyaml accountant.py --client machina --latest`.
|
|
329
|
+
|
|
330
|
+
CLI aggregates and prices group by exact model name across selected clients and
|
|
331
|
+
providers. SQL `model_usage` separates client/provider/model; `reset_usage` also
|
|
332
|
+
separates the quota identity. Supply rates only when the model-wide pricing
|
|
333
|
+
assumption is appropriate. Codex quota resets are recorded; other clients
|
|
334
|
+
currently have no quota observations and appear as unassigned usage.
|
|
335
|
+
|
|
336
|
+
## Interpretation
|
|
337
|
+
|
|
338
|
+
- Codex metadata `source: "vscode"` does not establish VS Code use. Local
|
|
339
|
+
rollouts with that marker also record `originator: "codex-tui"` or
|
|
340
|
+
`"JetBrains.IntelliJ IDEA"`. Report source and originator as recorded metadata;
|
|
341
|
+
neither proves deliberate editor use or agent activity.
|
|
342
|
+
|
|
343
|
+
- Cumulative counter differences avoid counting duplicate notifications. Model
|
|
344
|
+
attribution follows the latest `turn_context`. Decreasing counters start a new
|
|
345
|
+
segment and generate a warning. Missing models stay `unknown`.
|
|
346
|
+
- Reset reports assign each increment to the reset deadline attached to its event.
|
|
347
|
+
Deadlines within 60 seconds are grouped for the same limit ID and window length
|
|
348
|
+
to absorb observed clock jitter. SQL `reset_usage` preserves exact deadlines.
|
|
349
|
+
- Percentages are account quota observations, not per-model token percentages.
|
|
350
|
+
Reports show first, last, and maximum observed percentages; percentages are never
|
|
351
|
+
added. Times are recorded event timestamps and reset deadlines in UTC, not elapsed
|
|
352
|
+
work time. Usage without a recorded reset is reported separately.
|
|
353
|
+
- Results cover available logs only. Missing history, usage imported by a fork,
|
|
354
|
+
inherited cumulative baselines, and consumption from other devices may affect
|
|
355
|
+
completeness. A large cumulative increment is attributed to its current event;
|
|
356
|
+
absent intermediate records cannot reconstruct its earlier timing or model.
|
|
357
|
+
For forks with creation metadata, copied events before creation establish the
|
|
358
|
+
counter baseline but do not contribute usage to the fork. A counter emitted at
|
|
359
|
+
creation before local turn/request activity also establishes the inherited
|
|
360
|
+
baseline. Real usage and tools at creation after activity starts remain counted.
|
|
361
|
+
Parser version changes trigger reimport on the next sync, including this fix.
|
|
362
|
+
The first session
|
|
363
|
+
metadata record identifies the rollout; copied parent metadata does not replace it.
|
|
364
|
+
- Active rollouts can have a partially written final line. Invalid lines are skipped
|
|
365
|
+
with warnings and can be recovered on the next sync when the file changes.
|
|
366
|
+
|
|
367
|
+
## Verification
|
|
368
|
+
|
|
369
|
+
```powershell
|
|
370
|
+
uv run python -m unittest discover -s tests -v
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
If your environment restricts the default uv cache directory, add
|
|
374
|
+
`--cache-dir .uv-cache` immediately after `uv`.
|
|
375
|
+
|
|
376
|
+
The CLI needs no third-party dependencies for token accounting and can also run
|
|
377
|
+
directly with Python 3.11 or later:
|
|
378
|
+
|
|
379
|
+
```sh
|
|
380
|
+
python3 accountant.py --help
|
|
381
|
+
python3 -m unittest discover -s tests -v
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
GitHub Actions runs the tests on Python 3.11 and 3.13. Commit `uv.lock` with
|
|
385
|
+
changes to project dependencies. Local environments, SQLite indexes, session
|
|
386
|
+
logs, and generated historical usage reports are excluded from version control;
|
|
387
|
+
`prices.json` is the shared flat-sheet fallback; provider acquisition is owned by
|
|
388
|
+
the pricing library.
|
|
389
|
+
|
|
390
|
+
## Development and local artifacts
|
|
391
|
+
|
|
392
|
+
The source distribution includes `docs/README.md` (architecture) and
|
|
393
|
+
`docs/source-map.md` for ownership, adapter contracts and index lifecycle.
|
|
394
|
+
|
|
395
|
+
The onboarding copied the seed index, its backup, historical-models.json,
|
|
396
|
+
claude-cache-report.json, claude-cache-by-session.csv and diagnose-network.ps1
|
|
397
|
+
into this workspace as ignored local files. They are snapshots and diagnostics,
|
|
398
|
+
not shared project inputs. The seed originals were preserved. Environments and
|
|
399
|
+
caches are regenerated locally rather than copied.
|