fix(pricing): book daily cost to the day tokens were spent
token_usage holds a session's cumulative totals with no time, so /api/pricing/cost booked a multi-day session's whole cost to its start date and the status bar's Today read ~$0. TranscriptCache now also splits usage by each message's UTC hour (tokensByHour); the hook writer stores it in the new token_usage_hourly table (MAX per hour) and the server back-fills existing sessions at start. The daily split books each total over its hourly rows' local days and the rest to the start date, so the days still add up to the total.
This commit is contained in:
+12
@@ -1864,6 +1864,18 @@ curl -X DELETE http://localhost:4820/api/pricing/gpt-5.1-codex
|
||||
| 403 | Cannot delete default rule |
|
||||
| 500 | Database error |
|
||||
|
||||
#### Total Cost
|
||||
|
||||
```http
|
||||
GET /api/pricing/cost?tz_offset=<minutes>&sources=<csv>
|
||||
```
|
||||
|
||||
The total cost with a per-model `breakdown`, plus `daily_costs: [{date, cost}]` by local date (`tz_offset` is the browser's `getTimezoneOffset()`; the status bar's **Today** is the entry for today). A day is the day the tokens were SPENT: each session's total is split over the local days of its `token_usage_hourly` rows (one per UTC hour, from each Claude transcript message's timestamp), and whatever the total holds beyond those rows — a session recorded before that table and whose transcript is gone, sub-agent files, Codex and Antigravity sessions — is booked to the session's start date. The days always add up to the total.
|
||||
|
||||
```bash
|
||||
curl "http://localhost:4820/api/pricing/cost?tz_offset=-420"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Notifications
|
||||
|
||||
@@ -500,6 +500,12 @@ Managed through the `/api/remote-sources/*` routes; sync/status changes are broa
|
||||
|
||||
---
|
||||
|
||||
### token_usage_hourly
|
||||
|
||||
The same buckets as `token_usage` (session, model, speed, inference_geo, service_tier and the eight count columns, no baselines), split by `hour` — the UTC hour `YYYY-MM-DDTHH` a Claude transcript message was written in. The hook writer fills it from `TranscriptCache`'s `tokensByHour` on every transcript-bearing event (`writeHourly` in `server/lib/hourly-usage.js`, `stmts.upsertHourlyUsage`, MAX per column so a rewritten transcript never takes an hour back), and the server back-fills sessions that have tokens but no hourly rows a few seconds after each start (`backfillHourly`, one transcript per tick). Only the daily split of `GET /api/pricing/cost` reads it; `token_usage` stays the total, and what it holds beyond these rows is booked to the session's start date. Cascades with its session.
|
||||
|
||||
---
|
||||
|
||||
### codex_rollouts
|
||||
|
||||
How far `server/lib/codex-usage.js` has read each Codex rollout journal (`$CODEX_HOME/sessions/YYYY/MM/DD/rollout-<ts>-<thread>.jsonl`). A file whose `size` and `mtime_ms` are unchanged is not read again; a grown one is read from `offset` to its last newline, at most 16 MB per pass (`state.more` marks a file with more to read); a shrunken one from 0. There is one row per `thread_id`: the same journal met under a new path (CODEX_HOME moved, or reached through a symlink) replaces its old row instead of being counted twice. Because it lives in the DB, a Codex session the user deleted stays gone until Codex writes to that journal again, while a fresh database re-imports every journal. `POST /api/settings/clear-data` empties this table (and `antigravity_conversations`) with the sessions, so a cleared session is imported again whole instead of never, or half on its next write.
|
||||
|
||||
Reference in New Issue
Block a user