coding-agent-cost 0.3.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {coding_agent_cost-0.3.0/coding_agent_cost.egg-info → coding_agent_cost-0.5.0}/PKG-INFO +160 -3
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/README.md +159 -2
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/__init__.py +1 -1
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/aggregate.py +15 -1
- coding_agent_cost-0.5.0/agent_cost/billing_plan.py +654 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/cli.py +86 -2
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/facts.py +12 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/rates.json +46 -3
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/rates.py +73 -5
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/readers/claude.py +47 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/renderers.py +60 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0/coding_agent_cost.egg-info}/PKG-INFO +160 -3
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/coding_agent_cost.egg-info/SOURCES.txt +3 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/pyproject.toml +1 -1
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_aggregate.py +157 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_astra_pricing.py +2 -2
- coding_agent_cost-0.5.0/tests/test_billing_plan.py +1360 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_cli.py +411 -1
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_e0a_review3_hardening.py +3 -3
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_facts.py +41 -0
- coding_agent_cost-0.5.0/tests/test_haiku_5_5_rates.py +87 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_rates.py +200 -2
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_reader_claude.py +191 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_reader_codex.py +20 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_sonnet_5_5_rates.py +1 -1
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_version_consistency.py +8 -7
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/LICENSE +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/config.py +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/readers/__init__.py +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/agent_cost/readers/codex.py +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/coding_agent_cost.egg-info/dependency_links.txt +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/coding_agent_cost.egg-info/entry_points.txt +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/coding_agent_cost.egg-info/top_level.txt +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/setup.cfg +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_e0a_fable_5_1_and_sonnet_5_correction.py +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_measure_v1_contract.py +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_opus_5_5_rates.py +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_reader_dedup.py +0 -0
- {coding_agent_cost-0.3.0 → coding_agent_cost-0.5.0}/tests/test_reader_output_lower_bound.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coding-agent-cost
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Estimate AI coding agent (Claude Code / Codex CLI) token usage and cost from local logs
|
|
5
5
|
Author: shiki-yusuke
|
|
6
6
|
License: MIT
|
|
@@ -200,6 +200,114 @@ If another program wants to parse agent-cost's output for one or more
|
|
|
200
200
|
specific session ids, see `agent-cost measure` below rather than
|
|
201
201
|
scraping `report`.
|
|
202
202
|
|
|
203
|
+
## Internal billing plan (optional, confidential)
|
|
204
|
+
|
|
205
|
+
Some organizations bill Claude seats internally with their own rules -- a
|
|
206
|
+
fixed subscription per billing window, a usage allowance measured in
|
|
207
|
+
list-price dollars, and a discounted rate above it. `report --billing-plan
|
|
208
|
+
PATH` applies such a plan to the Claude usage agent-cost already prices at
|
|
209
|
+
the public catalog. Nothing changes without the flag, and there is no
|
|
210
|
+
default path or environment variable that turns it on.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
agent-cost report --billing-plan ~/private/billing-plan.json \
|
|
214
|
+
--since 2030-01-01T00:00:00+00:00 --until 2030-02-01T00:00:00+00:00 --format json
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
A plan (`plan_schema_version` `"1"`; every value below is made up):
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"plan_schema_version": "1",
|
|
222
|
+
"plan_id": "bp-example",
|
|
223
|
+
"nonce": "0123456789abcdef0123456789abcdef",
|
|
224
|
+
"applies_to": {"agent": "claude", "scope": "seat"},
|
|
225
|
+
"basis": "agent-cost-list-price",
|
|
226
|
+
"periods": [
|
|
227
|
+
{"period_id": "p1", "effective_from": "2030-01-01T00:00:00+00:00",
|
|
228
|
+
"window_subscription_usd": "3", "allowance_usd": null, "overage": null},
|
|
229
|
+
{"period_id": "p2", "effective_from": "2030-01-11T00:00:00+00:00",
|
|
230
|
+
"window_subscription_usd": "10", "allowance_usd": "50",
|
|
231
|
+
"overage": {"type": "charge_multiplier", "value": "0.5"}},
|
|
232
|
+
{"period_id": "end", "effective_from": "2030-02-01T00:00:00+00:00", "terminates": true}
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
- `periods` is a list of finite billing windows: window *i* is
|
|
238
|
+
`[effective_from_i, effective_from_{i+1})`, and the last entry must be
|
|
239
|
+
`{period_id, effective_from, terminates: true}` so that no window is
|
|
240
|
+
open-ended. Write one window per billing month (or per contract change).
|
|
241
|
+
`effective_from` needs a UTC offset and no sub-second part.
|
|
242
|
+
- `window_subscription_usd` is the fixed amount billed for that window, as
|
|
243
|
+
you write it -- agent-cost never prorates it.
|
|
244
|
+
- `allowance_usd` is the window's usage allowance in list-price dollars.
|
|
245
|
+
`null` means the window has no overage concept at all (it is not a zero
|
|
246
|
+
allowance), and then `overage` must be `null` too.
|
|
247
|
+
- `overage` prices `max(0, list_cost - allowance)`:
|
|
248
|
+
`{"type": "charge_multiplier", "value": "0.5"}` charges overage × value
|
|
249
|
+
(`0 <= value <= 1`), and `{"type": "block_discount", "block_usd": "5",
|
|
250
|
+
"discount_usd": "1"}` charges overage minus `floor(overage / block_usd) ×
|
|
251
|
+
discount_usd` (`0 < discount_usd <= block_usd`).
|
|
252
|
+
- Amounts are decimal strings only (no JSON numbers), at most 28
|
|
253
|
+
significant digits. `plan_id` is `[A-Za-z0-9_-]{1,32}`, `period_id`
|
|
254
|
+
`[A-Za-z0-9_-]{1,16}`, `nonce` 32 lowercase hex characters (e.g.
|
|
255
|
+
`python3 -c 'import secrets; print(secrets.token_hex(16))'`). Unknown or
|
|
256
|
+
duplicate keys are errors. v1 only accepts `scope: "seat"`: an allowance
|
|
257
|
+
shared by a department or org cannot be computed from one person's logs.
|
|
258
|
+
|
|
259
|
+
The JSON output gains an `internal_billing` block (existing keys are
|
|
260
|
+
unchanged), and the table gains a section headed `Internal billing
|
|
261
|
+
(CONFIDENTIAL -- do not share)`. For each window that intersects
|
|
262
|
+
`[since, until)` it shows `list_cost_usd` (Claude facts in the window,
|
|
263
|
+
priced at the packaged catalog), `allowance_usd`, `overage_usd`,
|
|
264
|
+
`overage_cost_usd`, `window_subscription_usd`, `internal_cost_usd` --
|
|
265
|
+
all as 4-decimal strings -- and four separate status fields:
|
|
266
|
+
|
|
267
|
+
- `query_coverage`: `full` when `--since`/`--until` cover the whole
|
|
268
|
+
window, otherwise `partial`. It says nothing about whether the logs
|
|
269
|
+
themselves are complete (deleted transcripts, other machines, API or web
|
|
270
|
+
usage are never seen; see `data_quality` at the top level as well).
|
|
271
|
+
- `window_state`: `closed` once the window has ended (`until <=
|
|
272
|
+
generated_at`), otherwise `open`.
|
|
273
|
+
- `list_cost_pricing`: `priced`, `lower_bound` (some facts were unpriced
|
|
274
|
+
or only priced as a lower bound, or an output fact's tokens are only a
|
|
275
|
+
lower bound -- `source_quality: "output_lower_bound"`) or
|
|
276
|
+
`no_usage_observed`.
|
|
277
|
+
- `internal_cost_certainty`: `estimate` only when the window is fully
|
|
278
|
+
queried, closed and `priced`. Otherwise `lower_bound` for windows with no
|
|
279
|
+
overage or `charge_multiplier` (more usage can only raise the amount), and
|
|
280
|
+
`indeterminate` for `block_discount` (more usage can cross a block
|
|
281
|
+
boundary and lower it). This describes what can be derived from the
|
|
282
|
+
observed list cost -- it is not a statement that the number matches an
|
|
283
|
+
actual invoice.
|
|
284
|
+
|
|
285
|
+
`window_subscription_usd` in each row is the plan's input echoed back, not
|
|
286
|
+
an amount allocated to your query. `internal_cost_usd` is the cost of the
|
|
287
|
+
whole billing window computed from the usage visible in this report -- it
|
|
288
|
+
is not the cost of the `[since, until)` range, so do not sum windows to get
|
|
289
|
+
one. `plan_coverage: partial` with `uncovered` ranges flags the parts of
|
|
290
|
+
`[since, until)` that no window covers (before the first window or after
|
|
291
|
+
`terminates`); usage there is not billed.
|
|
292
|
+
|
|
293
|
+
`measure` has no billing plan option on purpose: a per-session internal
|
|
294
|
+
cost would mean allocating a window-level charge across sessions.
|
|
295
|
+
`--billing-plan` cannot be combined with `--rates` (the basis must be the
|
|
296
|
+
packaged public catalog), `--format csv`, an `--agent` list without
|
|
297
|
+
`claude`, or a missing `--since`/`--until`; any of those, an unreadable or
|
|
298
|
+
invalid plan, or a calculation that would need rounding exits 2 with
|
|
299
|
+
nothing on stdout. `AGENT_COST_NOW` (an ISO 8601 instant with a UTC offset)
|
|
300
|
+
pins `generated_at`, and with it `window_state`, for reproducible runs.
|
|
301
|
+
|
|
302
|
+
**Keep the plan and its output private.** The plan must be a regular file
|
|
303
|
+
owned by you with mode `0600` (or stricter), reached without any symlink in
|
|
304
|
+
its path (on macOS `/tmp` and `/var` are symlinks -- keep it under your
|
|
305
|
+
home directory), and not inside a git repository or worktree. Error messages
|
|
306
|
+
never print plan values or the path. Output produced with the plan is
|
|
307
|
+
confidential: agent-cost has no redaction, so mind your terminal scrollback,
|
|
308
|
+
shell history, and the permissions of any file you redirect it to, and keep
|
|
309
|
+
it out of shared logs, CI, artifacts and ledgers.
|
|
310
|
+
|
|
203
311
|
## What this measures, and what it doesn't
|
|
204
312
|
|
|
205
313
|
agent-cost only reads data that is already on disk. It never talks to the
|
|
@@ -244,6 +352,16 @@ network, never calls `gh`, and never resolves branches or PRs.
|
|
|
244
352
|
present in the log, it's used; otherwise the cache-write tokens are priced
|
|
245
353
|
at the 5-minute rate as an explicit **lower bound** and flagged
|
|
246
354
|
`lower_bound` rather than guessed at the (more expensive) 1-hour rate.
|
|
355
|
+
Each Claude fact also carries its request's prompt length
|
|
356
|
+
(`prompt_tokens` = `input_tokens` + `cache_read_input_tokens` +
|
|
357
|
+
`cache_creation_input_tokens`), used only to pick the tier of a model
|
|
358
|
+
whose price depends on prompt length (`claude-haiku-5-5`).
|
|
359
|
+
- **Prompt-length tiers need a known prompt length.** A fact whose prompt
|
|
360
|
+
length is unknown -- every Codex fact (a delta of cumulative totals, not
|
|
361
|
+
one request), a Claude fact from a conflicting dedup group, or a Claude
|
|
362
|
+
row whose cache-write TTL breakdown exceeds its `cache_creation_input_tokens`
|
|
363
|
+
-- is priced at the model's base (cheapest) tier and flagged
|
|
364
|
+
`lower_bound`. Models without tiers are unaffected.
|
|
247
365
|
- **Codex CLI**: rollout files record a *cumulative* token count after each
|
|
248
366
|
turn; agent-cost turns that into per-turn deltas. Under the Codex token-rate
|
|
249
367
|
tariff used here, cache writes are not added as a separate charge. The current
|
|
@@ -303,6 +421,37 @@ carries a `catalog_version`, a list of `sources` (the pricing page a rate
|
|
|
303
421
|
came from), and is validated on load (no duplicate model keys or aliases,
|
|
304
422
|
no negative rates, no overlapping periods for the same model).
|
|
305
423
|
|
|
424
|
+
A rate period can price by prompt length (catalog `schema_version` `"2"`).
|
|
425
|
+
The period's own five values are the base tier, for prompts up to the first
|
|
426
|
+
threshold; each entry in `prompt_tiers` applies to prompts *strictly over*
|
|
427
|
+
its `prompt_tokens_over`, and a request uses the largest threshold it
|
|
428
|
+
exceeds. Prompt length is the request's input + cache-read + cache-write
|
|
429
|
+
tokens, and the chosen tier prices every token kind of that request,
|
|
430
|
+
output included:
|
|
431
|
+
|
|
432
|
+
```json
|
|
433
|
+
{
|
|
434
|
+
"rate_id": "claude-haiku-5-5-launch-2026-10-07",
|
|
435
|
+
"effective_from": "2026-10-07T00:00:00+00:00",
|
|
436
|
+
"effective_until": null,
|
|
437
|
+
"input_nocache": "0.10", "cache_read": "0.01", "cache_write_5m": "0.125", "cache_write_1h": "0.20", "output": "0.50",
|
|
438
|
+
"prompt_tiers": [
|
|
439
|
+
{ "prompt_tokens_over": 100000,
|
|
440
|
+
"input_nocache": "0.50", "cache_read": "0.05", "cache_write_5m": "0.625", "cache_write_1h": "1.0", "output": "2.50" }
|
|
441
|
+
]
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
In a period with `prompt_tiers`, all five values of the base and of every
|
|
446
|
+
tier must be present and non-null, thresholds must be positive integers in
|
|
447
|
+
ascending order without duplicates, and every value must be greater than or
|
|
448
|
+
equal to the same value in the previous tier (the base first). That
|
|
449
|
+
monotonicity is what makes the base tier a true lower bound for a fact whose
|
|
450
|
+
prompt length is unknown. A `schema_version` `"1"` catalog may not contain
|
|
451
|
+
`prompt_tiers`; agent-cost 0.3.0 and earlier reject a `"2"` catalog as
|
|
452
|
+
unsupported rather than mispricing it, so a custom `--rates` file that uses
|
|
453
|
+
tiers must declare `"2"`.
|
|
454
|
+
|
|
306
455
|
To use your own catalog instead of the one bundled with the package, pass
|
|
307
456
|
`--rates path/to/rates.json` to `report` or `export` -- this fully replaces
|
|
308
457
|
the bundled catalog, it does not merge with it. Inspect any catalog with:
|
|
@@ -402,10 +551,11 @@ agent-cost makes zero network calls. `agent-cost export`'s JSONL never
|
|
|
402
551
|
includes absolute file paths, rollout paths, prompt/message content, or git
|
|
403
552
|
branch names -- only the fields needed to reproduce a cost estimate:
|
|
404
553
|
`occurred_at_utc`, `agent`, `session_id`, `model_raw`, `model_key`,
|
|
405
|
-
`token_kind`, `tokens`, `mode`,
|
|
554
|
+
`token_kind`, `tokens`, `mode`, `source_quality` (a fixed-vocabulary
|
|
406
555
|
caveat about how that one fact was derived: `"ok"`, Codex's
|
|
407
556
|
`"first_event_delta"`, or Claude's `"identity_missing"` /
|
|
408
|
-
`"output_lower_bound"` -- never null)
|
|
557
|
+
`"output_lower_bound"` -- never null), and `prompt_tokens` (the request's
|
|
558
|
+
prompt length as an integer, or null when unknown).
|
|
409
559
|
|
|
410
560
|
## License
|
|
411
561
|
|
|
@@ -429,10 +579,17 @@ MIT. See [LICENSE](LICENSE).
|
|
|
429
579
|
未知のモデル・単価表にない token 種別は `unpriced` として扱い、憶測の価格を出しません。
|
|
430
580
|
- 単価表 (`agent_cost/rates.json`) は履歴型カタログで、値上げは新しい期間として追加します。
|
|
431
581
|
`--rates PATH` で別カタログに完全差し替えできます。
|
|
582
|
+
- prompt 長で単価が変わるモデル(`claude-haiku-5-5`、100,000 tokens 超で高い単価)は期間の
|
|
583
|
+
`prompt_tiers`(schema_version `"2"`)で表し、リクエストごとの prompt 長(input + cache read +
|
|
584
|
+
cache write)で tier を選びます。prompt 長が不明な fact(Codex 由来、conflicting group、usage
|
|
585
|
+
不整合)は基底 tier(最安)で **下限推計**(`lower_bound`)になります。
|
|
432
586
|
- `agent-cost measure --session-id ID [--session-id ID ...] --format json` は他プログラムから
|
|
433
587
|
subprocess で叩くための機械可読な契約です(`protocol_version: "measure/v1"`)。指定した
|
|
434
588
|
session_id が1件も見つからなくても終了コードは0(空集計として表現)、`--session-id` 未指定など
|
|
435
589
|
の入力エラーのみ終了コード2です。
|
|
590
|
+
- `report --billing-plan PATH`(任意・機密)は、ローカルの課金プラン(窓ごとの定額・利用枠・超過規則)を
|
|
591
|
+
公開単価で集計した Claude の list_cost に当てて `internal_billing` を出します。フラグ無しの出力は不変です。
|
|
592
|
+
- plan ファイルは 0600・symlink 不可・git repo 外に置き、出力は機密成果物として扱います(`measure` には按分になるため無し)。
|
|
436
593
|
- 破損したログ行、読めなくなったファイル、Codex の累積カウンタが逆行するケースなどは、すべて
|
|
437
594
|
`data_quality` に件数として記録し、黙って丸めたり捨てたりしません。
|
|
438
595
|
- Codex の `output` は `output_tokens` のみです。実 rollout データを突合した結果
|
|
@@ -174,6 +174,114 @@ If another program wants to parse agent-cost's output for one or more
|
|
|
174
174
|
specific session ids, see `agent-cost measure` below rather than
|
|
175
175
|
scraping `report`.
|
|
176
176
|
|
|
177
|
+
## Internal billing plan (optional, confidential)
|
|
178
|
+
|
|
179
|
+
Some organizations bill Claude seats internally with their own rules -- a
|
|
180
|
+
fixed subscription per billing window, a usage allowance measured in
|
|
181
|
+
list-price dollars, and a discounted rate above it. `report --billing-plan
|
|
182
|
+
PATH` applies such a plan to the Claude usage agent-cost already prices at
|
|
183
|
+
the public catalog. Nothing changes without the flag, and there is no
|
|
184
|
+
default path or environment variable that turns it on.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
agent-cost report --billing-plan ~/private/billing-plan.json \
|
|
188
|
+
--since 2030-01-01T00:00:00+00:00 --until 2030-02-01T00:00:00+00:00 --format json
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
A plan (`plan_schema_version` `"1"`; every value below is made up):
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"plan_schema_version": "1",
|
|
196
|
+
"plan_id": "bp-example",
|
|
197
|
+
"nonce": "0123456789abcdef0123456789abcdef",
|
|
198
|
+
"applies_to": {"agent": "claude", "scope": "seat"},
|
|
199
|
+
"basis": "agent-cost-list-price",
|
|
200
|
+
"periods": [
|
|
201
|
+
{"period_id": "p1", "effective_from": "2030-01-01T00:00:00+00:00",
|
|
202
|
+
"window_subscription_usd": "3", "allowance_usd": null, "overage": null},
|
|
203
|
+
{"period_id": "p2", "effective_from": "2030-01-11T00:00:00+00:00",
|
|
204
|
+
"window_subscription_usd": "10", "allowance_usd": "50",
|
|
205
|
+
"overage": {"type": "charge_multiplier", "value": "0.5"}},
|
|
206
|
+
{"period_id": "end", "effective_from": "2030-02-01T00:00:00+00:00", "terminates": true}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
- `periods` is a list of finite billing windows: window *i* is
|
|
212
|
+
`[effective_from_i, effective_from_{i+1})`, and the last entry must be
|
|
213
|
+
`{period_id, effective_from, terminates: true}` so that no window is
|
|
214
|
+
open-ended. Write one window per billing month (or per contract change).
|
|
215
|
+
`effective_from` needs a UTC offset and no sub-second part.
|
|
216
|
+
- `window_subscription_usd` is the fixed amount billed for that window, as
|
|
217
|
+
you write it -- agent-cost never prorates it.
|
|
218
|
+
- `allowance_usd` is the window's usage allowance in list-price dollars.
|
|
219
|
+
`null` means the window has no overage concept at all (it is not a zero
|
|
220
|
+
allowance), and then `overage` must be `null` too.
|
|
221
|
+
- `overage` prices `max(0, list_cost - allowance)`:
|
|
222
|
+
`{"type": "charge_multiplier", "value": "0.5"}` charges overage × value
|
|
223
|
+
(`0 <= value <= 1`), and `{"type": "block_discount", "block_usd": "5",
|
|
224
|
+
"discount_usd": "1"}` charges overage minus `floor(overage / block_usd) ×
|
|
225
|
+
discount_usd` (`0 < discount_usd <= block_usd`).
|
|
226
|
+
- Amounts are decimal strings only (no JSON numbers), at most 28
|
|
227
|
+
significant digits. `plan_id` is `[A-Za-z0-9_-]{1,32}`, `period_id`
|
|
228
|
+
`[A-Za-z0-9_-]{1,16}`, `nonce` 32 lowercase hex characters (e.g.
|
|
229
|
+
`python3 -c 'import secrets; print(secrets.token_hex(16))'`). Unknown or
|
|
230
|
+
duplicate keys are errors. v1 only accepts `scope: "seat"`: an allowance
|
|
231
|
+
shared by a department or org cannot be computed from one person's logs.
|
|
232
|
+
|
|
233
|
+
The JSON output gains an `internal_billing` block (existing keys are
|
|
234
|
+
unchanged), and the table gains a section headed `Internal billing
|
|
235
|
+
(CONFIDENTIAL -- do not share)`. For each window that intersects
|
|
236
|
+
`[since, until)` it shows `list_cost_usd` (Claude facts in the window,
|
|
237
|
+
priced at the packaged catalog), `allowance_usd`, `overage_usd`,
|
|
238
|
+
`overage_cost_usd`, `window_subscription_usd`, `internal_cost_usd` --
|
|
239
|
+
all as 4-decimal strings -- and four separate status fields:
|
|
240
|
+
|
|
241
|
+
- `query_coverage`: `full` when `--since`/`--until` cover the whole
|
|
242
|
+
window, otherwise `partial`. It says nothing about whether the logs
|
|
243
|
+
themselves are complete (deleted transcripts, other machines, API or web
|
|
244
|
+
usage are never seen; see `data_quality` at the top level as well).
|
|
245
|
+
- `window_state`: `closed` once the window has ended (`until <=
|
|
246
|
+
generated_at`), otherwise `open`.
|
|
247
|
+
- `list_cost_pricing`: `priced`, `lower_bound` (some facts were unpriced
|
|
248
|
+
or only priced as a lower bound, or an output fact's tokens are only a
|
|
249
|
+
lower bound -- `source_quality: "output_lower_bound"`) or
|
|
250
|
+
`no_usage_observed`.
|
|
251
|
+
- `internal_cost_certainty`: `estimate` only when the window is fully
|
|
252
|
+
queried, closed and `priced`. Otherwise `lower_bound` for windows with no
|
|
253
|
+
overage or `charge_multiplier` (more usage can only raise the amount), and
|
|
254
|
+
`indeterminate` for `block_discount` (more usage can cross a block
|
|
255
|
+
boundary and lower it). This describes what can be derived from the
|
|
256
|
+
observed list cost -- it is not a statement that the number matches an
|
|
257
|
+
actual invoice.
|
|
258
|
+
|
|
259
|
+
`window_subscription_usd` in each row is the plan's input echoed back, not
|
|
260
|
+
an amount allocated to your query. `internal_cost_usd` is the cost of the
|
|
261
|
+
whole billing window computed from the usage visible in this report -- it
|
|
262
|
+
is not the cost of the `[since, until)` range, so do not sum windows to get
|
|
263
|
+
one. `plan_coverage: partial` with `uncovered` ranges flags the parts of
|
|
264
|
+
`[since, until)` that no window covers (before the first window or after
|
|
265
|
+
`terminates`); usage there is not billed.
|
|
266
|
+
|
|
267
|
+
`measure` has no billing plan option on purpose: a per-session internal
|
|
268
|
+
cost would mean allocating a window-level charge across sessions.
|
|
269
|
+
`--billing-plan` cannot be combined with `--rates` (the basis must be the
|
|
270
|
+
packaged public catalog), `--format csv`, an `--agent` list without
|
|
271
|
+
`claude`, or a missing `--since`/`--until`; any of those, an unreadable or
|
|
272
|
+
invalid plan, or a calculation that would need rounding exits 2 with
|
|
273
|
+
nothing on stdout. `AGENT_COST_NOW` (an ISO 8601 instant with a UTC offset)
|
|
274
|
+
pins `generated_at`, and with it `window_state`, for reproducible runs.
|
|
275
|
+
|
|
276
|
+
**Keep the plan and its output private.** The plan must be a regular file
|
|
277
|
+
owned by you with mode `0600` (or stricter), reached without any symlink in
|
|
278
|
+
its path (on macOS `/tmp` and `/var` are symlinks -- keep it under your
|
|
279
|
+
home directory), and not inside a git repository or worktree. Error messages
|
|
280
|
+
never print plan values or the path. Output produced with the plan is
|
|
281
|
+
confidential: agent-cost has no redaction, so mind your terminal scrollback,
|
|
282
|
+
shell history, and the permissions of any file you redirect it to, and keep
|
|
283
|
+
it out of shared logs, CI, artifacts and ledgers.
|
|
284
|
+
|
|
177
285
|
## What this measures, and what it doesn't
|
|
178
286
|
|
|
179
287
|
agent-cost only reads data that is already on disk. It never talks to the
|
|
@@ -218,6 +326,16 @@ network, never calls `gh`, and never resolves branches or PRs.
|
|
|
218
326
|
present in the log, it's used; otherwise the cache-write tokens are priced
|
|
219
327
|
at the 5-minute rate as an explicit **lower bound** and flagged
|
|
220
328
|
`lower_bound` rather than guessed at the (more expensive) 1-hour rate.
|
|
329
|
+
Each Claude fact also carries its request's prompt length
|
|
330
|
+
(`prompt_tokens` = `input_tokens` + `cache_read_input_tokens` +
|
|
331
|
+
`cache_creation_input_tokens`), used only to pick the tier of a model
|
|
332
|
+
whose price depends on prompt length (`claude-haiku-5-5`).
|
|
333
|
+
- **Prompt-length tiers need a known prompt length.** A fact whose prompt
|
|
334
|
+
length is unknown -- every Codex fact (a delta of cumulative totals, not
|
|
335
|
+
one request), a Claude fact from a conflicting dedup group, or a Claude
|
|
336
|
+
row whose cache-write TTL breakdown exceeds its `cache_creation_input_tokens`
|
|
337
|
+
-- is priced at the model's base (cheapest) tier and flagged
|
|
338
|
+
`lower_bound`. Models without tiers are unaffected.
|
|
221
339
|
- **Codex CLI**: rollout files record a *cumulative* token count after each
|
|
222
340
|
turn; agent-cost turns that into per-turn deltas. Under the Codex token-rate
|
|
223
341
|
tariff used here, cache writes are not added as a separate charge. The current
|
|
@@ -277,6 +395,37 @@ carries a `catalog_version`, a list of `sources` (the pricing page a rate
|
|
|
277
395
|
came from), and is validated on load (no duplicate model keys or aliases,
|
|
278
396
|
no negative rates, no overlapping periods for the same model).
|
|
279
397
|
|
|
398
|
+
A rate period can price by prompt length (catalog `schema_version` `"2"`).
|
|
399
|
+
The period's own five values are the base tier, for prompts up to the first
|
|
400
|
+
threshold; each entry in `prompt_tiers` applies to prompts *strictly over*
|
|
401
|
+
its `prompt_tokens_over`, and a request uses the largest threshold it
|
|
402
|
+
exceeds. Prompt length is the request's input + cache-read + cache-write
|
|
403
|
+
tokens, and the chosen tier prices every token kind of that request,
|
|
404
|
+
output included:
|
|
405
|
+
|
|
406
|
+
```json
|
|
407
|
+
{
|
|
408
|
+
"rate_id": "claude-haiku-5-5-launch-2026-10-07",
|
|
409
|
+
"effective_from": "2026-10-07T00:00:00+00:00",
|
|
410
|
+
"effective_until": null,
|
|
411
|
+
"input_nocache": "0.10", "cache_read": "0.01", "cache_write_5m": "0.125", "cache_write_1h": "0.20", "output": "0.50",
|
|
412
|
+
"prompt_tiers": [
|
|
413
|
+
{ "prompt_tokens_over": 100000,
|
|
414
|
+
"input_nocache": "0.50", "cache_read": "0.05", "cache_write_5m": "0.625", "cache_write_1h": "1.0", "output": "2.50" }
|
|
415
|
+
]
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
In a period with `prompt_tiers`, all five values of the base and of every
|
|
420
|
+
tier must be present and non-null, thresholds must be positive integers in
|
|
421
|
+
ascending order without duplicates, and every value must be greater than or
|
|
422
|
+
equal to the same value in the previous tier (the base first). That
|
|
423
|
+
monotonicity is what makes the base tier a true lower bound for a fact whose
|
|
424
|
+
prompt length is unknown. A `schema_version` `"1"` catalog may not contain
|
|
425
|
+
`prompt_tiers`; agent-cost 0.3.0 and earlier reject a `"2"` catalog as
|
|
426
|
+
unsupported rather than mispricing it, so a custom `--rates` file that uses
|
|
427
|
+
tiers must declare `"2"`.
|
|
428
|
+
|
|
280
429
|
To use your own catalog instead of the one bundled with the package, pass
|
|
281
430
|
`--rates path/to/rates.json` to `report` or `export` -- this fully replaces
|
|
282
431
|
the bundled catalog, it does not merge with it. Inspect any catalog with:
|
|
@@ -376,10 +525,11 @@ agent-cost makes zero network calls. `agent-cost export`'s JSONL never
|
|
|
376
525
|
includes absolute file paths, rollout paths, prompt/message content, or git
|
|
377
526
|
branch names -- only the fields needed to reproduce a cost estimate:
|
|
378
527
|
`occurred_at_utc`, `agent`, `session_id`, `model_raw`, `model_key`,
|
|
379
|
-
`token_kind`, `tokens`, `mode`,
|
|
528
|
+
`token_kind`, `tokens`, `mode`, `source_quality` (a fixed-vocabulary
|
|
380
529
|
caveat about how that one fact was derived: `"ok"`, Codex's
|
|
381
530
|
`"first_event_delta"`, or Claude's `"identity_missing"` /
|
|
382
|
-
`"output_lower_bound"` -- never null)
|
|
531
|
+
`"output_lower_bound"` -- never null), and `prompt_tokens` (the request's
|
|
532
|
+
prompt length as an integer, or null when unknown).
|
|
383
533
|
|
|
384
534
|
## License
|
|
385
535
|
|
|
@@ -403,10 +553,17 @@ MIT. See [LICENSE](LICENSE).
|
|
|
403
553
|
未知のモデル・単価表にない token 種別は `unpriced` として扱い、憶測の価格を出しません。
|
|
404
554
|
- 単価表 (`agent_cost/rates.json`) は履歴型カタログで、値上げは新しい期間として追加します。
|
|
405
555
|
`--rates PATH` で別カタログに完全差し替えできます。
|
|
556
|
+
- prompt 長で単価が変わるモデル(`claude-haiku-5-5`、100,000 tokens 超で高い単価)は期間の
|
|
557
|
+
`prompt_tiers`(schema_version `"2"`)で表し、リクエストごとの prompt 長(input + cache read +
|
|
558
|
+
cache write)で tier を選びます。prompt 長が不明な fact(Codex 由来、conflicting group、usage
|
|
559
|
+
不整合)は基底 tier(最安)で **下限推計**(`lower_bound`)になります。
|
|
406
560
|
- `agent-cost measure --session-id ID [--session-id ID ...] --format json` は他プログラムから
|
|
407
561
|
subprocess で叩くための機械可読な契約です(`protocol_version: "measure/v1"`)。指定した
|
|
408
562
|
session_id が1件も見つからなくても終了コードは0(空集計として表現)、`--session-id` 未指定など
|
|
409
563
|
の入力エラーのみ終了コード2です。
|
|
564
|
+
- `report --billing-plan PATH`(任意・機密)は、ローカルの課金プラン(窓ごとの定額・利用枠・超過規則)を
|
|
565
|
+
公開単価で集計した Claude の list_cost に当てて `internal_billing` を出します。フラグ無しの出力は不変です。
|
|
566
|
+
- plan ファイルは 0600・symlink 不可・git repo 外に置き、出力は機密成果物として扱います(`measure` には按分になるため無し)。
|
|
410
567
|
- 破損したログ行、読めなくなったファイル、Codex の累積カウンタが逆行するケースなどは、すべて
|
|
411
568
|
`data_quality` に件数として記録し、黙って丸めたり捨てたりしません。
|
|
412
569
|
- Codex の `output` は `output_tokens` のみです。実 rollout データを突合した結果
|
|
@@ -28,6 +28,11 @@ def price_fact(catalog: RateCatalog, fact: Fact) -> Tuple[Optional[Decimal], str
|
|
|
28
28
|
never at the more expensive 1-hour rate -- and flagged accordingly.
|
|
29
29
|
An unrecognized model or an unpriced token kind for an otherwise-known
|
|
30
30
|
model both return ``(None, "unpriced", None)`` rather than a guess.
|
|
31
|
+
|
|
32
|
+
For a period with ``prompt_tiers``, the fact is priced at the tier with
|
|
33
|
+
the largest ``prompt_tokens_over`` its ``prompt_tokens`` exceeds (the
|
|
34
|
+
base values when none). A fact whose ``prompt_tokens`` is unknown is
|
|
35
|
+
priced at the base values -- the cheapest tier -- as a lower bound.
|
|
31
36
|
"""
|
|
32
37
|
resolved_key, period = catalog.rate_for(fact.model_key, fact.occurred_at_utc)
|
|
33
38
|
if resolved_key is None or period is None:
|
|
@@ -47,7 +52,16 @@ def price_fact(catalog: RateCatalog, fact: Fact) -> Tuple[Optional[Decimal], str
|
|
|
47
52
|
rate_field = "cache_write_5m"
|
|
48
53
|
lower_bound = True
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
values = period.values
|
|
56
|
+
if period.prompt_tiers:
|
|
57
|
+
if fact.prompt_tokens is None:
|
|
58
|
+
lower_bound = True
|
|
59
|
+
else:
|
|
60
|
+
for tier in period.prompt_tiers:
|
|
61
|
+
if fact.prompt_tokens > tier.prompt_tokens_over:
|
|
62
|
+
values = tier.values
|
|
63
|
+
|
|
64
|
+
rate = values.get(rate_field)
|
|
51
65
|
if rate is None:
|
|
52
66
|
return None, "unpriced", None
|
|
53
67
|
|