AI Accountant
A Python library for reading AI session usage and calculating token costs, with a bundled local accounting CLI. Licensed under Apache 2.0.
The distribution name is ai-accountant; the primary import is ai_accountant.
Release versions are derived from Git tags. Builds from a source distribution
retain that version without needing Git.
Install this checkout for development:
uv pip install .
from ai_accountant import SessionReader, ModelPrice, calculate_cost
reader = SessionReader()
# sources = reader.discover("codex", home="/path/to/.codex")
# session = reader.parse(sources[0], client="codex")
price = ModelPrice("example", "model", {"input": "2", "output": "10"}, "example")
cost = calculate_cost({"input_tokens": 1000, "output_tokens": 100}, price)
print(cost.amount_usd) # Decimal('0.003')
Library
Session reading
SessionReader discovers local Codex, Claude Code and Machina accounting sources.
The client is explicit; discovery and parsing only read files when called.
from ai_accountant import SessionReader
reader = SessionReader()
for path in reader.discover("codex"): # or "claude" / "machina"
session = reader.parse(path, client="codex")
print(session.session_id, session.models, session.warnings)
for event in session.events:
print(event["provider"], event["model"], event["tokens"])
Pass home="/path/to/store" to discover to override the client's default
location. A parsed session contains metadata, per-model totals, normalized usage
observations, warnings and Codex tool statistics. It does not contain a full
conversation replay. SQLite indexing and reports belong to the CLI.
Normalized input_tokens includes fresh input, cache reads and cache writes;
cached_input_tokens and cache_write_input_tokens are subsets. Five-minute
and one-hour write counts are subsets of cache writes. Reasoning is included
in output. Use usage_complete and reasoning_known to interpret missing
evidence; an omitted category represented as zero is not proof of measured zero.
Source-specific parsing deduplicates usage snapshots and excludes inherited
Codex fork counters. Provider and model identities are preserved.
Provider pricing
default_resolver() supplies provider modules for OpenAI, Anthropic, DeepSeek,
Z.ai and OpenRouter. Refresh explicitly; import, construction and lookup perform
no networking. Most built-ins currently use the LiteLLM community catalogue;
OpenRouter uses its own public API. Provenance is retained.
import asyncio
from ai_accountant import default_resolver
prices = default_resolver()
results = asyncio.run(prices.refresh("anthropic"))
for result in results:
print(result.status, result.error)
for price in prices.search("*opus*", provider="anthropic"):
print(price.qualified_id, price.rates)
Prices use Decimal USD per million tokens. Refresh failures retain the last good
snapshot. A host owns refresh scheduling, persistence and its event history.
calculate_cost accepts normalized counts and a ModelPrice; pass
usage_complete=event["usage_complete"] when pricing parsed observations.
Missing prices or consumed rates produce an unknown cost, never an assumed zero.
Context-tier pricing needs the per-request context size. These quotes estimate
API cost, not subscription quota usage or reconstructed historical invoices.
Custom provider and session-parser implementations can be registered explicitly. Installed plugin discovery is opt-in. For API signatures and docstrings:
from ai_accountant import SessionReader, PricingResolver, calculate_cost
help(SessionReader)
help(PricingResolver)
help(calculate_cost)
The source distribution includes detailed references at docs/session-library.md,
docs/pricing-library.md and docs/campaign-classification.md, with maintainer
release instructions in docs/releasing.md. They are archive paths, not hosted
documentation links.
CLI
A Python CLI run with uv. It reads local Codex rollouts, Claude Code transcripts, and Machina turns, then reports token usage per model and estimated USD cost. SQLite indexing makes combined history queryable. Codex remains the default source so existing commands keep their behavior.
The installable accountant_pricing compatibility facade provides
provider plugins, live price acquisition, an in-memory resolver and pure Decimal
calculation for other programs. Consumers own their usage histories; SQLite
snapshot persistence belongs to this CLI.
uv run accountant.py --list
uv run accountant.py SESSION_UUID
uv run accountant.py SESSION_UUID --parent-only
uv run accountant.py --latest
uv run accountant.py 'C:\path\to\rollout.jsonl' --prices prices.json
uv run accountant.py --all --prices prices.json
uv run accountant.py --resets --prices prices.json
uv run accountant.py --reset latest --prices prices.json
uv run accountant.py --reset 1791580844 --window primary --json
uv run accountant.py --client claude --list
uv run accountant.py --client machina --all --json
uv run accountant.py --client all --all --json
Use --client codex|claude|machina|all to choose a source. --all aggregates
the clients selected by --client. Codex history defaults to $CODEX_HOME or
~/.codex; Claude uses CLAUDE_CONFIG_DIR or ~/.claude; Machina uses
MACHINA_HOME or ~/.machina. Override those locations with --codex-home,
--claude-home, and --machina-home. Claude and Machina session IDs are
prefixed with claude: and machina: in combined reports and the database;
Codex UUIDs remain unchanged. --latest picks the most recently modified file
among selected clients, and --list includes each session's client.
--reset accepts latest, a Unix reset deadline, or an ISO timestamp with a
timezone. --window secondary selects the secondary quota. Window lengths come
from the logs; primary does not necessarily mean a five-hour window. --latest
selects the most recently modified file, including archived and subagent sessions.
Use a UUID or path when selecting a particular conversation. Unique UUID prefixes
work too. --codex-home PATH supports other installations or exported histories.
Session reports include recursively linked subagent rollouts by default, even
when archived. --parent-only excludes them. JSON includes a per-session
breakdown. Only explicit parent-thread links are followed; forks are separate
conversations. Agents with no parent link cannot be attributed to a conversation.
Codex session reports also tally tool requests by name and recorded completed tool
actions by type (commands, file changes, web extensions, and MCP calls). These
are two overlapping views: an exec request can run several underlying tools,
so do not add the two totals. Outputs and collaboration UI echoes are excluded;
repeated call/action IDs and copied pre-creation fork history are not counted
again. Counts include recorded failed actions and requests without outputs.
Shell commands can perform multiple file reads or searches; individual file
operations and hidden tool reviews cannot be reliably inferred from these counts.
Run --sync to populate tool statistics in an older index. An index without
these statistics reports them as unavailable rather than zero. Claude and Machina
report tool statistics as unavailable. Aggregate token and quota reports do not
include tool counts.
SQLite
uv run accountant.py --sync
uv run accountant.py SESSION_UUID --db accountant.sqlite3
uv run accountant.py --all --db accountant.sqlite3 --prices prices.json
uv run accountant.py --resets --db accountant.sqlite3 --prices prices.json
uv run accountant.py --reset latest --db accountant.sqlite3 --prices prices.json
uv run accountant.py --query 'SELECT * FROM model_usage ORDER BY total_tokens DESC'
uv run accountant.py --query 'SELECT * FROM reset_usage ORDER BY resets_at, model'
Run --sync to migrate older indexes before using session or aggregate reports;
read-only SQL remains available on old schemas. Sync reparses available sources
when the parser version changes, and backfills missing Codex tool statistics.
Sources that are no longer available retain their indexed data; migrated legacy
events remain conservatively marked incomplete until reimported.
Select clients with --client when syncing. Sync again to import new or changed
logs. Unchanged files are skipped using size and modification time; changed files
are replaced transactionally. Archiving
a session updates its path without duplicating usage. Sessions whose logs are later
removed remain in the database. The database contains usage metadata, not prompts
or responses. --db PATH overrides the database location. Aggregate commands with
--db read the indexed snapshot; commands without it read the logs directly.
--sync --rebuild refreshes only the selected client index; use
--client all --sync --rebuild to rebuild every client.
Tables: sessions (including the source client), usage_events (usage increments,
provider, source-event identity, completeness flags, cache TTL counts, and raw
rate-limit JSON), and
rate_limits (primary/secondary observations), plus session_links (parent,
fork, and source metadata), and tool_statistics (Codex tool counts).
Views: model_usage, reset_usage, and
session_roots, which maps each session to itself and its recursively linked
descendants. The recursive view guards against cycles. --query opens SQLite
read-only and prints JSON. For example, aggregate a root session and all its
descendants by model:
SELECT u.model, SUM(u.input_tokens + u.output_tokens) AS total_tokens
FROM session_roots r
JOIN usage_events u ON u.session_id = r.session_id
WHERE r.root_session_id = 'SESSION_UUID'
GROUP BY u.model
ORDER BY total_tokens DESC;
Other queries:
SELECT u.model, SUM(u.input_tokens + u.output_tokens) AS total_tokens
FROM usage_events u
WHERE julianday(u.timestamp) >= julianday('2026-10-01T00:00:00Z')
AND julianday(u.timestamp) < julianday('2026-10-08T00:00:00Z')
GROUP BY u.model;
Prices
The script automatically loads prices.json beside accountant.py. This base
contains the user's supplied OpenAI rates and officially sourced DeepSeek,
Anthropic and Z.ai estimates,
in USD per million tokens. OpenAI rates were supplied on 2026-10-04 and were
not independently fetched during onboarding. Use --prices FILE to override the base. For example:
{
"gpt-6.1-sol": {"input": 2.0, "cached_input": 0.1, "output": 10.0},
"gpt-6-luna": {"input": 0.1, "cached_input": 0.01, "output": 0.5}
}
The flat sheet is not fetched automatically. Provider price acquisition is opt-in:
uv run accountant.py --refresh-prices --db accountant.sqlite3
uv run accountant.py --refresh-prices --price-provider openai --force-prices
uv run accountant.py --price-history --price-provider openai
uv run accountant.py --all --db accountant.sqlite3 --price-provider openai --json
The source archive's docs/pricing-library.md details source coverage,
plugin extension, TTL, failure retention and snapshot history. Snapshot reports
use standard base-context API reference rates and include provenance/assumptions.
These are current estimates rather than reconstructed invoices.
GPT-5.5 Pro's "no discount" is represented
by a cached-input rate equal to its input rate. The user supplied gpt-5.6-luna
rates matching gpt-6-luna, and gpt-5.6-terra input rates matching gpt-6.1-sol
with output at $12 per million tokens. The user supplied free pricing for
codex-auto-review; all its token rates are zero. No other aliases are inferred.
DeepSeek V4 Flash and Pro have explicit bare and deepseek/-prefixed entries.
Their current official prices
were retrieved via the DeepSeek homepage on
2026-10-12. The flat configuration uses peak rates: input/cache/output
$0.30/$0.006/$1.20 for Flash and $1.32/$0.044/$3.96 for Pro, per million tokens.
Off-peak rates are half and retained as metadata; the loader does not apply them.
Peak hours are weekdays 01:00–04:00 and 06:00–10:00 UTC excluding Chinese public
holidays. The official page explicitly bills the retired V4 Flash alias at
current V4.1 Flash rates; Pro currently identifies V4-Pro-0813. These are current
direct-provider estimates applied to history, not reconstructed invoices. Codex
DeepSeek records report provider cog; its actual charges are not established.
The observed claude-opus-4-8 model has official Anthropic rates
retrieved on 2026-10-12: input/cache-read/output $5/$0.50/$25 per million,
with separate five-minute and one-hour cache-write rates $6.25/$10.
Unclassified cache writes have no assumed rate. The user reports using alternative
endpoints rather than Anthropic; for these logs the Opus rate is a hypothetical
comparison. The CLI still prices exact model labels and does not validate the
serving endpoint or actual underlying model.
The observed glm-5.2 model has official Z.ai rates
retrieved on 2026-10-12: input/cache-read/output $1.40/$0.26/$4.40 per million.
Cache storage is temporarily free on that page; it is not mapped to a token
cache-write rate. These are standard API estimates, not Coding Plan subscription
charges. Incomplete GLM usage remains unpriced even with known model rates.
Optional cache_write supplies a generic cache-write rate. cache_write_5m and
cache_write_1h can price the two TTL buckets separately; each overrides the
generic rate for its bucket. Unclassified writes still need cache_write.
Unknown models or missing applicable rates yield an unknown total, with a priced
subtotal. Exact names avoid guessing prices for aliases. Estimates apply the supplied
flat rates to all history; they do not infer service-tier multipliers, historical
price changes, long-context rates, subscription charges, or tool fees.
Cost = (uncached input × input rate + cached input × cached rate + cache-write input × cache-write rate + output × output rate) / 1,000,000.
Cached and cache-write tokens are treated as subsets of input. Cache-write TTL
counts are subsets of total cache writes. Reports show cached_percent as cached
input divided by inclusive input, and reasoning_output_tokens: null when a
client does not report reasoning counts. Incomplete usage is retained and warned
about; its cost is not treated as zero. A model with any incomplete usage is left
out of the priced subtotal as a whole. Provider names are stored only when a log
reports them. Rates apply only to explicit entries in the price sheet, including the observed
Anthropic and Z.ai model IDs. Claude Code client identity does not imply an
Anthropic model or endpoint. Older
Machina turn totals with ambiguous input/cache semantics stay visible as observed
values, carry an incomplete flag, and do not produce a cached percentage or cost.
Current canonical Machina request rounds preserve direct input, cache reads,
cache writes, and output separately.
Machina session.yaml metadata is read with PyYAML's safe loader when PyYAML is
installed; without it, the reader uses transcript-directory identity and still
accounts for current canonical request rounds. To enable YAML metadata for one
run, use uv run --with pyyaml accountant.py --client machina --latest.
CLI aggregates and prices group by exact model name across selected clients and
providers. SQL model_usage separates client/provider/model; reset_usage also
separates the quota identity. Supply rates only when the model-wide pricing
assumption is appropriate. Codex quota resets are recorded; other clients
currently have no quota observations and appear as unassigned usage.
Interpretation
-
Codex metadata
source: "vscode"does not establish VS Code use. Local rollouts with that marker also recordoriginator: "codex-tui"or"JetBrains.IntelliJ IDEA". Report source and originator as recorded metadata; neither proves deliberate editor use or agent activity. -
Cumulative counter differences avoid counting duplicate notifications. Model attribution follows the latest
turn_context. Decreasing counters start a new segment and generate a warning. Missing models stayunknown. -
Reset reports assign each increment to the reset deadline attached to its event. Deadlines within 60 seconds are grouped for the same limit ID and window length to absorb observed clock jitter. SQL
reset_usagepreserves exact deadlines. -
Percentages are account quota observations, not per-model token percentages. Reports show first, last, and maximum observed percentages; percentages are never added. Times are recorded event timestamps and reset deadlines in UTC, not elapsed work time. Usage without a recorded reset is reported separately.
-
Results cover available logs only. Missing history, usage imported by a fork, inherited cumulative baselines, and consumption from other devices may affect completeness. A large cumulative increment is attributed to its current event; absent intermediate records cannot reconstruct its earlier timing or model. For forks with creation metadata, copied events before creation establish the counter baseline but do not contribute usage to the fork. A counter emitted at creation before local turn/request activity also establishes the inherited baseline. Real usage and tools at creation after activity starts remain counted. Parser version changes trigger reimport on the next sync, including this fix. The first session metadata record identifies the rollout; copied parent metadata does not replace it.
-
Active rollouts can have a partially written final line. Invalid lines are skipped with warnings and can be recovered on the next sync when the file changes.
Verification
uv run python -m unittest discover -s tests -v
If your environment restricts the default uv cache directory, add
--cache-dir .uv-cache immediately after uv.
The CLI needs no third-party dependencies for token accounting and can also run directly with Python 3.11 or later:
python3 accountant.py --help
python3 -m unittest discover -s tests -v
GitHub Actions runs the tests on Python 3.11 and 3.13. Commit uv.lock with
changes to project dependencies. Local environments, SQLite indexes, session
logs, and generated historical usage reports are excluded from version control;
prices.json is the shared flat-sheet fallback; provider acquisition is owned by
the pricing library.
Development and local artifacts
The source distribution includes docs/README.md (architecture) and
docs/source-map.md for ownership, adapter contracts and index lifecycle.
The onboarding copied the seed index, its backup, historical-models.json, claude-cache-report.json, claude-cache-by-session.csv and diagnose-network.ps1 into this workspace as ignored local files. They are snapshots and diagnostics, not shared project inputs. The seed originals were preserved. Environments and caches are regenerated locally rather than copied.
Metadata
Release files for ai-accountant 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ai_accountant-0.0.2.tar.gz | 99.3 kB | With provenance | Details for ai_accountant-0.0.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ai_accountant-0.0.2-py3-none-any.whl | Python 3 | none | any | Details for ai_accountant-0.0.2-py3-none-any.whl |
Total release size: 156.5 kB
Release files / ai_accountant-0.0.2.tar.gz
| Download URL | ai_accountant-0.0.2.tar.gz |
|---|---|
| Size | 99.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e01b8eb05cd01359bc838177fe752d84b562227e3b8818d72ba6ef4523c8369a
|
|
BLAKE2b-256 checksum How to use checksums |
c56dea4819a4942fb06a4c967fc1727c12c59cd8fc2c3a4b1c742774ddb72f53
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logRelease files / ai_accountant-0.0.2-py3-none-any.whl
| Download URL | ai_accountant-0.0.2-py3-none-any.whl |
|---|---|
| Size | 57.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c22f7c34ab6214f1e2ce1af39fa9368b4609e26db767306986cfdba43c855211
|
|
BLAKE2b-256 checksum How to use checksums |
c4bd6df8da23dd00f317f780997e92987b8ae97937d234590bbbaba5ec3dbbf6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logSecurity for ai-accountant 0.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for ai_accountant-0.0.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.