agent-cost-tracker 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.
- agent_cost_tracker-0.2.1/LICENSE +21 -0
- agent_cost_tracker-0.2.1/PKG-INFO +227 -0
- agent_cost_tracker-0.2.1/README.md +205 -0
- agent_cost_tracker-0.2.1/pyproject.toml +37 -0
- agent_cost_tracker-0.2.1/setup.cfg +4 -0
- agent_cost_tracker-0.2.1/src/agent_cost/__init__.py +7 -0
- agent_cost_tracker-0.2.1/src/agent_cost/analyze.py +55 -0
- agent_cost_tracker-0.2.1/src/agent_cost/cli.py +141 -0
- agent_cost_tracker-0.2.1/src/agent_cost/compare.py +174 -0
- agent_cost_tracker-0.2.1/src/agent_cost/models.py +70 -0
- agent_cost_tracker-0.2.1/src/agent_cost/parsers/__init__.py +16 -0
- agent_cost_tracker-0.2.1/src/agent_cost/parsers/claude.py +148 -0
- agent_cost_tracker-0.2.1/src/agent_cost/parsers/codex.py +82 -0
- agent_cost_tracker-0.2.1/src/agent_cost/parsers/detect.py +190 -0
- agent_cost_tracker-0.2.1/src/agent_cost/parsers/hermes.py +36 -0
- agent_cost_tracker-0.2.1/src/agent_cost/parsers/opencode.py +318 -0
- agent_cost_tracker-0.2.1/src/agent_cost/pricing.py +191 -0
- agent_cost_tracker-0.2.1/src/agent_cost/report.py +187 -0
- agent_cost_tracker-0.2.1/src/agent_cost_tracker.egg-info/PKG-INFO +227 -0
- agent_cost_tracker-0.2.1/src/agent_cost_tracker.egg-info/SOURCES.txt +30 -0
- agent_cost_tracker-0.2.1/src/agent_cost_tracker.egg-info/dependency_links.txt +1 -0
- agent_cost_tracker-0.2.1/src/agent_cost_tracker.egg-info/entry_points.txt +2 -0
- agent_cost_tracker-0.2.1/src/agent_cost_tracker.egg-info/top_level.txt +1 -0
- agent_cost_tracker-0.2.1/tests/test_billing_modes.py +119 -0
- agent_cost_tracker-0.2.1/tests/test_claude_parser.py +17 -0
- agent_cost_tracker-0.2.1/tests/test_cli.py +38 -0
- agent_cost_tracker-0.2.1/tests/test_codex_parser.py +14 -0
- agent_cost_tracker-0.2.1/tests/test_compare.py +30 -0
- agent_cost_tracker-0.2.1/tests/test_detect.py +56 -0
- agent_cost_tracker-0.2.1/tests/test_hermes_parser.py +15 -0
- agent_cost_tracker-0.2.1/tests/test_opencode_parser.py +236 -0
- agent_cost_tracker-0.2.1/tests/test_pricing.py +65 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yingxiangge
|
|
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.
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-cost-tracker
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Token, cache and context observability for AI coding agents.
|
|
5
|
+
Author: yingxiangge
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yingxiangge/agent-cost
|
|
8
|
+
Project-URL: Repository, https://github.com/yingxiangge/agent-cost
|
|
9
|
+
Project-URL: Issues, https://github.com/yingxiangge/agent-cost/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/yingxiangge/agent-cost/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: llm,tokens,cost,observability,codex,hermes,agent,claude-code,opencode
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
17
|
+
Classifier: Topic :: System :: Monitoring
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# agent-cost
|
|
24
|
+
|
|
25
|
+
[](https://github.com/yingxiangge/agent-cost/actions/workflows/ci.yml)
|
|
26
|
+
[](https://www.python.org/)
|
|
27
|
+
[](LICENSE)
|
|
28
|
+
|
|
29
|
+
**Token, cache and context observability for AI coding agents.**
|
|
30
|
+
|
|
31
|
+
AI coding agents are expensive not because they have no cache — they usually
|
|
32
|
+
have too much of it. `agent-cost` is a read-only CLI that shows you, per
|
|
33
|
+
session or across multiple agents, how many tokens were really burned, where
|
|
34
|
+
the context went, and which agent delivers the best prompt-caching efficiency.
|
|
35
|
+
|
|
36
|
+
It was built from real-world usage across Codex, Hermes, and Claude Code. Across
|
|
37
|
+
72 real Claude Code sessions (63.7M characters of transcript), the context
|
|
38
|
+
attribution comes out as:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
tool_output 94.8%
|
|
42
|
+
assistant 4.3%
|
|
43
|
+
user 0.5%
|
|
44
|
+
tool_calls 0.1%
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Those same sessions total 9.1B billed prompt tokens at a 98.7% cache hit rate —
|
|
48
|
+
a high hit rate does not protect you from unbounded context growth, it only
|
|
49
|
+
changes the unit price.
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
git clone https://github.com/yingxiangge/agent-cost.git
|
|
55
|
+
cd agent-cost
|
|
56
|
+
pip install -e .
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Requires Python 3.10+.
|
|
60
|
+
|
|
61
|
+
Not on PyPI yet. The distribution will be published as **`agent-cost-tracker`**
|
|
62
|
+
— `agent-cost` is taken on PyPI — while the command it installs stays
|
|
63
|
+
`agent-cost`:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install agent-cost-tracker
|
|
67
|
+
pipx run --spec agent-cost-tracker agent-cost compare ~/.claude/projects/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Compare usage, cache efficiency, and cost across multiple agents or sessions
|
|
74
|
+
agent-cost compare ~/.claude/projects/ ~/.codex/sessions/ ./hermes/
|
|
75
|
+
|
|
76
|
+
# Output structured JSON for analysis pipelines
|
|
77
|
+
agent-cost compare --json ~/.claude/projects/ ~/.codex/sessions/
|
|
78
|
+
|
|
79
|
+
# Inspect one session (Claude Code, Hermes, OpenCode, Codex, or a folder)
|
|
80
|
+
agent-cost inspect ~/.codex/sessions/2026/08/11/rollout-*.jsonl
|
|
81
|
+
|
|
82
|
+
# Context growth and actionable recommendations
|
|
83
|
+
agent-cost analyze ~/.codex/sessions/2026/08/11/
|
|
84
|
+
|
|
85
|
+
# Aggregate totals across sessions
|
|
86
|
+
agent-cost stats ~/.codex/sessions/2026/08/
|
|
87
|
+
|
|
88
|
+
# On a Claude Pro/Max subscription: report API-equivalent value, not spend
|
|
89
|
+
agent-cost --subscription compare ~/.claude/projects/
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Real example (`agent-cost compare`)
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
Agent Comparison Report
|
|
96
|
+
══════════════════════════════════════════════════════════════════════════════
|
|
97
|
+
AGENT SESSIONS TOTAL TOKENS CACHED % TOOLS EST. USD AVG $/SESS
|
|
98
|
+
-------------------------------------------------------------------------------
|
|
99
|
+
hermes 2 2,071,557 89.9% 0 0.82* 0.41
|
|
100
|
+
claude-code 1 12,590 76.2% 1 0.02 0.02
|
|
101
|
+
opencode 1 8,130 83.1% 2 0.00 0.00
|
|
102
|
+
codex 1 111 0.0% 1 0.00 0.00
|
|
103
|
+
-------------------------------------------------------------------------------
|
|
104
|
+
TOTAL 5 2,092,388 0.84*
|
|
105
|
+
|
|
106
|
+
n/a / * = model has no rate card, excluded from dollar totals (1 of 5 sessions).
|
|
107
|
+
|
|
108
|
+
Key Insights:
|
|
109
|
+
• Highest cache efficiency: hermes (89.9% hit rate).
|
|
110
|
+
• Most tool intensive: opencode (2 total tool calls).
|
|
111
|
+
• Prompt caching saved approx. $0.03 across analyzed sessions.
|
|
112
|
+
• 1 of 5 sessions have no rate card and are excluded from every dollar
|
|
113
|
+
figure above. Supply rates with --pricing to include them.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Real example (`agent-cost analyze`)
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
Analysis: 019feec8-4ee1-7560-9a9f-89952d1af2d3
|
|
120
|
+
──────────────────────────────
|
|
121
|
+
Context growth: 24,072 -> 70,609 tokens (x2.93)
|
|
122
|
+
Largest context sources:
|
|
123
|
+
tool_output 83.6%
|
|
124
|
+
base_instructions 5.3%
|
|
125
|
+
assistant 4.5%
|
|
126
|
+
developer 3.3%
|
|
127
|
+
Compactions: 1
|
|
128
|
+
Prompt curve: 65K -> 66K -> 66K -> 70K -> 74K -> 80K
|
|
129
|
+
|
|
130
|
+
Recommendations:
|
|
131
|
+
- Prompt size is growing steeply; start a fresh session instead of continuing.
|
|
132
|
+
- Session was already compacted 1x; further work belongs in a new session.
|
|
133
|
+
- Tool output dominates context; consider truncating or filtering large command output.
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Supported data sources
|
|
137
|
+
|
|
138
|
+
- **Claude Code** (`~/.claude/projects/**/*.jsonl` or `.json`): real turn-by-turn
|
|
139
|
+
token usage with `input`, `output`, `cache_read_input_tokens`, and
|
|
140
|
+
`cache_creation_input_tokens` breakdown, plus tool calls.
|
|
141
|
+
- **Hermes** (`sessions.json`): real `input/output/cache_read/cache_write`
|
|
142
|
+
totals plus estimated cost when the agent has finalized them.
|
|
143
|
+
- **OpenCode** (`~/.local/share/opencode/opencode.db` SQLite database, or `.json` / `.jsonl` exports):
|
|
144
|
+
real turn-by-turn token usage, tool calls, and cache stats (`input`, `output`,
|
|
145
|
+
`cache.read`, `cache.write`).
|
|
146
|
+
- **Codex rollouts** (`~/.codex/sessions/**/*.jsonl`): turns, tool calls,
|
|
147
|
+
compaction events, prompt-size curve and context-source attribution.
|
|
148
|
+
Rollouts usually have no token counters, so prompt sizes are estimated from
|
|
149
|
+
content length (approx. 4 chars/token) and reported as estimates.
|
|
150
|
+
|
|
151
|
+
Parsing is intentionally read-only: files are only opened and counted, never
|
|
152
|
+
executed or modified. Database connections use read-only SQLite URIs.
|
|
153
|
+
|
|
154
|
+
## Pricing
|
|
155
|
+
|
|
156
|
+
Costs are estimated from a built-in price table (see `pricing.py`) covering the
|
|
157
|
+
Claude family (Fable 5, Opus 5/4.8/4.7/4.6/4.5/4.1, Sonnet 5/4.6/4.5, Haiku 4.5,
|
|
158
|
+
plus the Claude 3.x generation), GPT-4o, GPT-5, o1, o3-mini, DeepSeek V3/R1,
|
|
159
|
+
Qwen 2.5 Coder, and Gemini. Anthropic rates are from the
|
|
160
|
+
[official pricing page](https://platform.claude.com/docs/en/about-claude/pricing)
|
|
161
|
+
as of 2026-08-16.
|
|
162
|
+
|
|
163
|
+
**A model that is not in the table is reported as `n/a`, never guessed.** It is
|
|
164
|
+
excluded from every dollar total, and the report says how many sessions that
|
|
165
|
+
covers. A wrong cost number is worse than no cost number, so there is no
|
|
166
|
+
default rate card to fall back on.
|
|
167
|
+
|
|
168
|
+
**Billing modes are priced per turn, not per session.** Fast mode and US-pinned
|
|
169
|
+
inference cost more and can be toggled mid-session, so usage is split into
|
|
170
|
+
`(speed, inference_geo)` buckets and each bucket is priced on its own card:
|
|
171
|
+
|
|
172
|
+
| Mode | Effect | Source |
|
|
173
|
+
|:---|:---|:---|
|
|
174
|
+
| `speed: "fast"` | Opus 5 / Opus 4.8 billed at **$10 / $50** instead of $5 / $25. Other models fall back to standard rates, matching the API. | `usage.speed` |
|
|
175
|
+
| `inference_geo: "us"` | **1.1x** on every token category. `global` / `not_available` are standard priced. | `usage.inference_geo` |
|
|
176
|
+
|
|
177
|
+
The two stack. `agent-cost inspect` lists the modes when a session used more
|
|
178
|
+
than one.
|
|
179
|
+
|
|
180
|
+
**On a subscription, pass `--subscription`.** Claude Pro/Max sessions do not
|
|
181
|
+
generate per-token charges, so the transcript's dollar value is what those
|
|
182
|
+
tokens *would* have cost on metered API billing — useful for comparing agents
|
|
183
|
+
and deciding when to restart a session, useless as a bill. The flag relabels
|
|
184
|
+
every figure as API-equivalent value. It has to be explicit: the transcript
|
|
185
|
+
carries no field distinguishing subscription from API usage.
|
|
186
|
+
|
|
187
|
+
One remaining approximation: `cache_write` uses the 5-minute rate (1.25x input),
|
|
188
|
+
because the usage payload does not record which cache TTL was used — sessions
|
|
189
|
+
on the 1-hour cache (2x input) are undercounted.
|
|
190
|
+
|
|
191
|
+
Supply your own rates to override any of this:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
agent-cost --pricing "$(cat my-pricing.json)" compare ~/.claude/projects/
|
|
195
|
+
export AGENT_COST_PRICING="$(cat my-pricing.json)"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Your entries are merged over the built-in table, so overriding one model leaves
|
|
199
|
+
the rest intact. Copy [`pricing.example.json`](pricing.example.json) as a
|
|
200
|
+
starting template. Note that `--pricing` is a global flag and must come *before*
|
|
201
|
+
the subcommand.
|
|
202
|
+
|
|
203
|
+
Model names resolve by exact match first, then by longest matching prefix, so a
|
|
204
|
+
`claude-opus-4-5` entry also covers `claude-opus-4-5-20260101`, and `gpt-5-mini`
|
|
205
|
+
is never priced as `gpt-5`.
|
|
206
|
+
|
|
207
|
+
## Roadmap
|
|
208
|
+
|
|
209
|
+
- [x] `agent-cost compare`: side-by-side agent comparison with `--by-agent` and `--json`
|
|
210
|
+
- [x] Claude Code / Codex / Hermes session parsing
|
|
211
|
+
- [x] OpenCode: SQLite database (`opencode.db`) and JSON export parsing
|
|
212
|
+
- [ ] Support Cursor (`composer.json` / workspace state) log formats
|
|
213
|
+
- [ ] Support Cline / Roo Code conversation history formats
|
|
214
|
+
- [ ] `agent-cost watch`: budget thresholds with warnings before a session blows up
|
|
215
|
+
- [ ] Tool-call-level cost attribution when providers expose per-request usage
|
|
216
|
+
|
|
217
|
+
## Contributing
|
|
218
|
+
|
|
219
|
+
Bug reports are especially welcome — particularly a session that parses
|
|
220
|
+
incorrectly. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, tests, and how to
|
|
221
|
+
add support for a new session format. Please never attach a raw session file to
|
|
222
|
+
an issue; strip it first.
|
|
223
|
+
|
|
224
|
+
## Security
|
|
225
|
+
|
|
226
|
+
See [SECURITY.md](SECURITY.md). Session files are untrusted input; this tool
|
|
227
|
+
never executes them, never renders them, and never sends data anywhere.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# agent-cost
|
|
2
|
+
|
|
3
|
+
[](https://github.com/yingxiangge/agent-cost/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.python.org/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
**Token, cache and context observability for AI coding agents.**
|
|
8
|
+
|
|
9
|
+
AI coding agents are expensive not because they have no cache — they usually
|
|
10
|
+
have too much of it. `agent-cost` is a read-only CLI that shows you, per
|
|
11
|
+
session or across multiple agents, how many tokens were really burned, where
|
|
12
|
+
the context went, and which agent delivers the best prompt-caching efficiency.
|
|
13
|
+
|
|
14
|
+
It was built from real-world usage across Codex, Hermes, and Claude Code. Across
|
|
15
|
+
72 real Claude Code sessions (63.7M characters of transcript), the context
|
|
16
|
+
attribution comes out as:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
tool_output 94.8%
|
|
20
|
+
assistant 4.3%
|
|
21
|
+
user 0.5%
|
|
22
|
+
tool_calls 0.1%
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Those same sessions total 9.1B billed prompt tokens at a 98.7% cache hit rate —
|
|
26
|
+
a high hit rate does not protect you from unbounded context growth, it only
|
|
27
|
+
changes the unit price.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/yingxiangge/agent-cost.git
|
|
33
|
+
cd agent-cost
|
|
34
|
+
pip install -e .
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Requires Python 3.10+.
|
|
38
|
+
|
|
39
|
+
Not on PyPI yet. The distribution will be published as **`agent-cost-tracker`**
|
|
40
|
+
— `agent-cost` is taken on PyPI — while the command it installs stays
|
|
41
|
+
`agent-cost`:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install agent-cost-tracker
|
|
45
|
+
pipx run --spec agent-cost-tracker agent-cost compare ~/.claude/projects/
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Compare usage, cache efficiency, and cost across multiple agents or sessions
|
|
52
|
+
agent-cost compare ~/.claude/projects/ ~/.codex/sessions/ ./hermes/
|
|
53
|
+
|
|
54
|
+
# Output structured JSON for analysis pipelines
|
|
55
|
+
agent-cost compare --json ~/.claude/projects/ ~/.codex/sessions/
|
|
56
|
+
|
|
57
|
+
# Inspect one session (Claude Code, Hermes, OpenCode, Codex, or a folder)
|
|
58
|
+
agent-cost inspect ~/.codex/sessions/2026/08/11/rollout-*.jsonl
|
|
59
|
+
|
|
60
|
+
# Context growth and actionable recommendations
|
|
61
|
+
agent-cost analyze ~/.codex/sessions/2026/08/11/
|
|
62
|
+
|
|
63
|
+
# Aggregate totals across sessions
|
|
64
|
+
agent-cost stats ~/.codex/sessions/2026/08/
|
|
65
|
+
|
|
66
|
+
# On a Claude Pro/Max subscription: report API-equivalent value, not spend
|
|
67
|
+
agent-cost --subscription compare ~/.claude/projects/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Real example (`agent-cost compare`)
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
Agent Comparison Report
|
|
74
|
+
══════════════════════════════════════════════════════════════════════════════
|
|
75
|
+
AGENT SESSIONS TOTAL TOKENS CACHED % TOOLS EST. USD AVG $/SESS
|
|
76
|
+
-------------------------------------------------------------------------------
|
|
77
|
+
hermes 2 2,071,557 89.9% 0 0.82* 0.41
|
|
78
|
+
claude-code 1 12,590 76.2% 1 0.02 0.02
|
|
79
|
+
opencode 1 8,130 83.1% 2 0.00 0.00
|
|
80
|
+
codex 1 111 0.0% 1 0.00 0.00
|
|
81
|
+
-------------------------------------------------------------------------------
|
|
82
|
+
TOTAL 5 2,092,388 0.84*
|
|
83
|
+
|
|
84
|
+
n/a / * = model has no rate card, excluded from dollar totals (1 of 5 sessions).
|
|
85
|
+
|
|
86
|
+
Key Insights:
|
|
87
|
+
• Highest cache efficiency: hermes (89.9% hit rate).
|
|
88
|
+
• Most tool intensive: opencode (2 total tool calls).
|
|
89
|
+
• Prompt caching saved approx. $0.03 across analyzed sessions.
|
|
90
|
+
• 1 of 5 sessions have no rate card and are excluded from every dollar
|
|
91
|
+
figure above. Supply rates with --pricing to include them.
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Real example (`agent-cost analyze`)
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
Analysis: 019feec8-4ee1-7560-9a9f-89952d1af2d3
|
|
98
|
+
──────────────────────────────
|
|
99
|
+
Context growth: 24,072 -> 70,609 tokens (x2.93)
|
|
100
|
+
Largest context sources:
|
|
101
|
+
tool_output 83.6%
|
|
102
|
+
base_instructions 5.3%
|
|
103
|
+
assistant 4.5%
|
|
104
|
+
developer 3.3%
|
|
105
|
+
Compactions: 1
|
|
106
|
+
Prompt curve: 65K -> 66K -> 66K -> 70K -> 74K -> 80K
|
|
107
|
+
|
|
108
|
+
Recommendations:
|
|
109
|
+
- Prompt size is growing steeply; start a fresh session instead of continuing.
|
|
110
|
+
- Session was already compacted 1x; further work belongs in a new session.
|
|
111
|
+
- Tool output dominates context; consider truncating or filtering large command output.
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Supported data sources
|
|
115
|
+
|
|
116
|
+
- **Claude Code** (`~/.claude/projects/**/*.jsonl` or `.json`): real turn-by-turn
|
|
117
|
+
token usage with `input`, `output`, `cache_read_input_tokens`, and
|
|
118
|
+
`cache_creation_input_tokens` breakdown, plus tool calls.
|
|
119
|
+
- **Hermes** (`sessions.json`): real `input/output/cache_read/cache_write`
|
|
120
|
+
totals plus estimated cost when the agent has finalized them.
|
|
121
|
+
- **OpenCode** (`~/.local/share/opencode/opencode.db` SQLite database, or `.json` / `.jsonl` exports):
|
|
122
|
+
real turn-by-turn token usage, tool calls, and cache stats (`input`, `output`,
|
|
123
|
+
`cache.read`, `cache.write`).
|
|
124
|
+
- **Codex rollouts** (`~/.codex/sessions/**/*.jsonl`): turns, tool calls,
|
|
125
|
+
compaction events, prompt-size curve and context-source attribution.
|
|
126
|
+
Rollouts usually have no token counters, so prompt sizes are estimated from
|
|
127
|
+
content length (approx. 4 chars/token) and reported as estimates.
|
|
128
|
+
|
|
129
|
+
Parsing is intentionally read-only: files are only opened and counted, never
|
|
130
|
+
executed or modified. Database connections use read-only SQLite URIs.
|
|
131
|
+
|
|
132
|
+
## Pricing
|
|
133
|
+
|
|
134
|
+
Costs are estimated from a built-in price table (see `pricing.py`) covering the
|
|
135
|
+
Claude family (Fable 5, Opus 5/4.8/4.7/4.6/4.5/4.1, Sonnet 5/4.6/4.5, Haiku 4.5,
|
|
136
|
+
plus the Claude 3.x generation), GPT-4o, GPT-5, o1, o3-mini, DeepSeek V3/R1,
|
|
137
|
+
Qwen 2.5 Coder, and Gemini. Anthropic rates are from the
|
|
138
|
+
[official pricing page](https://platform.claude.com/docs/en/about-claude/pricing)
|
|
139
|
+
as of 2026-08-16.
|
|
140
|
+
|
|
141
|
+
**A model that is not in the table is reported as `n/a`, never guessed.** It is
|
|
142
|
+
excluded from every dollar total, and the report says how many sessions that
|
|
143
|
+
covers. A wrong cost number is worse than no cost number, so there is no
|
|
144
|
+
default rate card to fall back on.
|
|
145
|
+
|
|
146
|
+
**Billing modes are priced per turn, not per session.** Fast mode and US-pinned
|
|
147
|
+
inference cost more and can be toggled mid-session, so usage is split into
|
|
148
|
+
`(speed, inference_geo)` buckets and each bucket is priced on its own card:
|
|
149
|
+
|
|
150
|
+
| Mode | Effect | Source |
|
|
151
|
+
|:---|:---|:---|
|
|
152
|
+
| `speed: "fast"` | Opus 5 / Opus 4.8 billed at **$10 / $50** instead of $5 / $25. Other models fall back to standard rates, matching the API. | `usage.speed` |
|
|
153
|
+
| `inference_geo: "us"` | **1.1x** on every token category. `global` / `not_available` are standard priced. | `usage.inference_geo` |
|
|
154
|
+
|
|
155
|
+
The two stack. `agent-cost inspect` lists the modes when a session used more
|
|
156
|
+
than one.
|
|
157
|
+
|
|
158
|
+
**On a subscription, pass `--subscription`.** Claude Pro/Max sessions do not
|
|
159
|
+
generate per-token charges, so the transcript's dollar value is what those
|
|
160
|
+
tokens *would* have cost on metered API billing — useful for comparing agents
|
|
161
|
+
and deciding when to restart a session, useless as a bill. The flag relabels
|
|
162
|
+
every figure as API-equivalent value. It has to be explicit: the transcript
|
|
163
|
+
carries no field distinguishing subscription from API usage.
|
|
164
|
+
|
|
165
|
+
One remaining approximation: `cache_write` uses the 5-minute rate (1.25x input),
|
|
166
|
+
because the usage payload does not record which cache TTL was used — sessions
|
|
167
|
+
on the 1-hour cache (2x input) are undercounted.
|
|
168
|
+
|
|
169
|
+
Supply your own rates to override any of this:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
agent-cost --pricing "$(cat my-pricing.json)" compare ~/.claude/projects/
|
|
173
|
+
export AGENT_COST_PRICING="$(cat my-pricing.json)"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Your entries are merged over the built-in table, so overriding one model leaves
|
|
177
|
+
the rest intact. Copy [`pricing.example.json`](pricing.example.json) as a
|
|
178
|
+
starting template. Note that `--pricing` is a global flag and must come *before*
|
|
179
|
+
the subcommand.
|
|
180
|
+
|
|
181
|
+
Model names resolve by exact match first, then by longest matching prefix, so a
|
|
182
|
+
`claude-opus-4-5` entry also covers `claude-opus-4-5-20260101`, and `gpt-5-mini`
|
|
183
|
+
is never priced as `gpt-5`.
|
|
184
|
+
|
|
185
|
+
## Roadmap
|
|
186
|
+
|
|
187
|
+
- [x] `agent-cost compare`: side-by-side agent comparison with `--by-agent` and `--json`
|
|
188
|
+
- [x] Claude Code / Codex / Hermes session parsing
|
|
189
|
+
- [x] OpenCode: SQLite database (`opencode.db`) and JSON export parsing
|
|
190
|
+
- [ ] Support Cursor (`composer.json` / workspace state) log formats
|
|
191
|
+
- [ ] Support Cline / Roo Code conversation history formats
|
|
192
|
+
- [ ] `agent-cost watch`: budget thresholds with warnings before a session blows up
|
|
193
|
+
- [ ] Tool-call-level cost attribution when providers expose per-request usage
|
|
194
|
+
|
|
195
|
+
## Contributing
|
|
196
|
+
|
|
197
|
+
Bug reports are especially welcome — particularly a session that parses
|
|
198
|
+
incorrectly. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, tests, and how to
|
|
199
|
+
add support for a new session format. Please never attach a raw session file to
|
|
200
|
+
an issue; strip it first.
|
|
201
|
+
|
|
202
|
+
## Security
|
|
203
|
+
|
|
204
|
+
See [SECURITY.md](SECURITY.md). Session files are untrusted input; this tool
|
|
205
|
+
never executes them, never renders them, and never sends data anywhere.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agent-cost-tracker"
|
|
7
|
+
version = "0.2.1"
|
|
8
|
+
description = "Token, cache and context observability for AI coding agents."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "yingxiangge" }]
|
|
13
|
+
keywords = ["llm", "tokens", "cost", "observability", "codex", "hermes", "agent", "claude-code", "opencode"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
20
|
+
"Topic :: System :: Monitoring",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/yingxiangge/agent-cost"
|
|
25
|
+
Repository = "https://github.com/yingxiangge/agent-cost"
|
|
26
|
+
Issues = "https://github.com/yingxiangge/agent-cost/issues"
|
|
27
|
+
Changelog = "https://github.com/yingxiangge/agent-cost/blob/main/CHANGELOG.md"
|
|
28
|
+
|
|
29
|
+
[project.scripts]
|
|
30
|
+
agent-cost = "agent_cost.cli:main"
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
where = ["src"]
|
|
34
|
+
|
|
35
|
+
[tool.pytest.ini_options]
|
|
36
|
+
testpaths = ["tests"]
|
|
37
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from agent_cost.models import SessionStats
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def analyze(stats: SessionStats) -> dict:
|
|
7
|
+
"""Derive actionable signals from session statistics."""
|
|
8
|
+
signals: dict = {"recommendations": [], "context_growth": None, "largest_sources": []}
|
|
9
|
+
|
|
10
|
+
samples = [s["estimated_prompt_tokens"] for s in stats.context_samples]
|
|
11
|
+
if len(samples) >= 4:
|
|
12
|
+
half = len(samples) // 2
|
|
13
|
+
first = sum(samples[:half]) / half
|
|
14
|
+
second = sum(samples[half:]) / (len(samples) - half)
|
|
15
|
+
ratio = second / first if first else 0.0
|
|
16
|
+
signals["context_growth"] = {
|
|
17
|
+
"first_half_avg": round(first),
|
|
18
|
+
"second_half_avg": round(second),
|
|
19
|
+
"growth_ratio": round(ratio, 2),
|
|
20
|
+
}
|
|
21
|
+
if ratio >= 2.5:
|
|
22
|
+
signals["recommendations"].append(
|
|
23
|
+
"Prompt size is growing steeply; start a fresh session instead of continuing."
|
|
24
|
+
)
|
|
25
|
+
elif ratio >= 1.6:
|
|
26
|
+
signals["recommendations"].append(
|
|
27
|
+
"Context is growing steadily; budget a /compact or a new session soon."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if stats.compaction_events:
|
|
31
|
+
signals["recommendations"].append(
|
|
32
|
+
f"Session was already compacted {stats.compaction_events}x; further work belongs in a new session."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
if stats.turns >= 25 and not stats.context_samples:
|
|
36
|
+
signals["recommendations"].append("High turn count; review whether a new session would be cheaper.")
|
|
37
|
+
|
|
38
|
+
has_cache_data = bool(stats.cache_read_tokens or stats.cache_write_tokens)
|
|
39
|
+
if has_cache_data and stats.cache_hit_rate < 0.4 and stats.prompt_tokens:
|
|
40
|
+
signals["recommendations"].append(
|
|
41
|
+
"Low cache hit rate; same-prefix reuse is low, which usually inflates input cost."
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
total_chars = sum(stats.source_chars.values())
|
|
45
|
+
if total_chars:
|
|
46
|
+
ranked = sorted(stats.source_chars.items(), key=lambda kv: kv[1], reverse=True)[:4]
|
|
47
|
+
signals["largest_sources"] = [
|
|
48
|
+
{"source": k, "percent": round(v * 100 / total_chars, 1)} for k, v in ranked
|
|
49
|
+
]
|
|
50
|
+
if any(k == "tool_output" for k, _ in ranked[:2]):
|
|
51
|
+
signals["recommendations"].append(
|
|
52
|
+
"Tool output dominates context; consider truncating or filtering large command output."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return signals
|