Model usage analytics for pi
Track token & cost usage by model, project, and session — daily / weekly / monthly / 6-monthly / yearly — in an interactive TUI dashboard. Optional budgets fire a warning when a threshold is crossed.
pi-tracker is a pi extension that turns every assistant turn into a record and gives you a live, drill-down analytics view:
- Capture — every finalized assistant message (
message_end) is logged with its token breakdown and cost. - Pivot — group by model, project, or session, across 6 time windows.
- Drill down — pick a model to see which projects use it, then which sessions, then back up. A breadcrumb keeps your bearings.
- Budgets — set token/cost limits per scope & period; get warned at a threshold and again when exceeded.
- Back-fill — import your full pi session history on day one so you start with real data, not an empty log.
pi-tracker — Model usage
window: All Time • 37 model(s) • filter: —
Model usage tokens cost turns
──────────────────────────────────────────────────────────────────────────────────
▶ anthropic/claude-opus-4-8 ████████████████████ 1,844,364,365 $2388.42 6155
openai-codex/gpt-5.5 ██░░░░░░░░░░░░░░░░░░ 1,212,486,248 $2096.32 10475
zai-anthropic/glm-5.2 ████████████████████ 14,798,725,266 n/a 4621
──────────────────────────────────────────────────────────────────────────────────
Total 17,855,575,879 $4484.74 21251
[1-6] window [d] dimension [enter] drill in [u] back [↑↓] move [q] close
n/a = that provider didn't return pricing (e.g. a model without published
costs), not $0.
pi-tracker is a pi package. Install it with pi directly:
pi install npm:pi-tracker
Or, from source (for development):
git clone git@github.com:alpertarhan/pi-tracker.git
cd pi-tracker && npm install
ln -s "$(pwd)" ~/.pi/agent/extensions/pi-trackerRestart pi (or /reload) and run /analytics. Then back-fill your history:
/analytics import
That scans ~/.pi/agent/sessions/ and recovers all past usage (idempotent —
safe to re-run).
Opens the interactive dashboard. Keys:
| Key | Action |
|---|---|
1–6 |
switch window (today / week / month / 6mo / year / all) |
d |
cycle grouping dimension (model / session / project) |
↑ ↓ |
move cursor (auto-scrolls) |
enter |
drill into the cursor row — pivot to the next dimension |
u / backspace |
pop the last filter (go back up a level) |
q / esc |
close |
Back-fill analytics from pi session history. Idempotent.
View configured budgets. Add new ones two ways:
- Interactive (TUI):
/budget add— SelectList flow. - Args:
/budget add <scope> <period> <metric> <max> [warn]
scope: global | session | project (project uses the current cwd)
period: day | week | month | year
metric: cost | tot | in | out
max: positive number (USD if metric=cost, else tokens)
warn: fraction 0..1, default 0.8
- Data source — the
message_endevent is the only place token usage appears (event.message.usage).after_provider_responsehas only HTTP status. - Storage — append-only JSONL. O(1) write, zero dependencies, crash-safe. An in-memory accumulator (rebuilt once per session, updated incrementally) keeps per-message budget checks O(budgets) instead of O(log).
- Cost awareness — each record tracks whether the provider returned
pricing (
costKnown). A model row showsn/awhen none of its turns had pricing, and$as soon as any does.
~/.pi/agent/analytics/
├── usage.jsonl append-only raw log (one record per assistant message)
├── budgets.json budget limit config (optional)
└── warned.jsonl append-only dedup keys for fired warnings (resets per period)
Clean, layered, dependency-injected — dependencies point inward only:
src/
domain/ pure rules — ZERO dependencies (no fs, no pi)
types.ts data contracts (UsageRecord, BudgetLimit, …)
time.ts periodKey / windowStartMs / isoWeekKey
aggregate.ts aggregate / sumRows / dimensionKey
budget.ts budgetKey / statusForLimit / recordMatchesLimit
enums.ts DIMENSIONS / WINDOWS
format.ts fmtNum / fmtCost / fmtBar / padRight
session-parser.ts parse pi session history → UsageRecords
storage/ infrastructure — implements the ports
ports.ts UsageStore + ConfigStore interfaces (total, never throw)
jsonl-store.ts concrete JSONL/file adapter (real fs)
memory-store.ts in-memory fake (tests)
app/
accumulator.ts in-memory read-side cache (takes UsageStore)
ui/
dashboard.ts pure view — data injected, NO I/O during render, drill-down
budget-add.ts interactive /budget add flow (SelectList)
pi-tracker.ts composition root — wires pi events to the layers (DI seam)
index.ts re-exports factory for pi's dir-based discovery
There's a check:layers guard that enforces this in CI.
Why this shape: the domain layer has no deps, so it's unit-tested directly; storage is exercised through ports with an in-memory fake (no disk); the view never touches storage during render; every storage method is total (never throws) so a disk fault can't crash pi.
npm install
npm test # unit tests (pure domain + store fake)
npm run typecheck # typecheck (strict + unused checks)
npm run check:layers # architectural layer guard
npm run smoke # runtime smoke (DI + capture + render)
npm run ci # all of the above (what CI runs)See CONTRIBUTING.md for the full guide.
Contributions are very welcome! 🎉
- 🐛 Found a bug? Open an issue
- ✨ Have an idea? Suggest a feature
- 💬 Want to chat? Start a discussion
- 📝 Want to code? Read
CONTRIBUTING.md— small, focused PRs fly through review.
Please note this project has a Code of Conduct. By participating you agree to abide by its terms.
MIT © Alper Tarhan