Skip to content

feat: per-request cost tracking (C7)#12

Merged
MANVENDRA-github merged 1 commit into
mainfrom
feat/cost-tracking
Jun 30, 2026
Merged

feat: per-request cost tracking (C7)#12
MANVENDRA-github merged 1 commit into
mainfrom
feat/cost-tracking

Conversation

@MANVENDRA-github

Copy link
Copy Markdown
Owner

Attributes a USD cost to every trace, so "cost" is a real number instead of
an inferred request count. Surfaces spend, cache savings, and cost-over-time
on the dashboard. (Closes the cost gap from the v0.1.x audit.)

What changed

  • New pure cost.ts (computeCostUsd): token usage × per-1K price → USD, or
    null when the model is unpriced or usage is unknown (never a guess).
  • Config: optional pricing map (USD per 1K input/output tokens, per model)
    ResolvedConfig.pricing; added to sentinel.config.example.json.
  • Trace: costUsd on every record + span attribute sentinel.cost_usd,
    set on the real path (routed model) and on a cache hit (the spend the cache
    avoided). The SQLite store gains a cost_usd column with a guarded
    migration so pre-existing DBs upgrade in place.
  • Dashboard: a "Cost" stat (with cache savings) and a cost-over-time
    sparkline; computeStats sums spend (non-cache) vs. saved (cache).

Tests

  • cost math (null / free / float-dust rounding), config parse + negative
    reject, SQLite round-trip + migration, server attributes cost end-to-end,
    dashboard spend/saved aggregation, and an e2e cost assertion. Dashboard
    screenshot regenerated to show the new views.

How to verify

  • pnpm verify → green (217 tests, coverage ≥90%)
  • pnpm build → green
  • pnpm test:e2e → green

No behavior change without a pricing block: cost stays null and nothing
else moves.

Attributes a USD cost to every trace, so "cost" is a real number instead of
an inferred request count. Surfaces spend, cache savings, and cost-over-time
on the dashboard. (Closes the cost gap from the v0.1.x audit.)

What changed
- New pure `cost.ts` (`computeCostUsd`): token usage × per-1K price → USD, or
  `null` when the model is unpriced or usage is unknown (never a guess).
- Config: optional `pricing` map (USD per 1K input/output tokens, per model)
  → `ResolvedConfig.pricing`; added to `sentinel.config.example.json`.
- Trace: `costUsd` on every record + span attribute `sentinel.cost_usd`,
  set on the real path (routed model) and on a cache hit (the spend the cache
  avoided). The SQLite store gains a `cost_usd` column with a guarded
  migration so pre-existing DBs upgrade in place.
- Dashboard: a "Cost" stat (with cache savings) and a cost-over-time
  sparkline; `computeStats` sums spend (non-cache) vs. saved (cache).

Tests
- cost math (null / free / float-dust rounding), config parse + negative
  reject, SQLite round-trip + migration, server attributes cost end-to-end,
  dashboard spend/saved aggregation, and an e2e cost assertion. Dashboard
  screenshot regenerated to show the new views.

How to verify
- `pnpm verify`   → green (217 tests, coverage ≥90%)
- `pnpm build`    → green
- `pnpm test:e2e` → green

No behavior change without a `pricing` block: cost stays `null` and nothing
else moves.
@MANVENDRA-github
MANVENDRA-github merged commit 3e3eab7 into main Jun 30, 2026
2 checks passed
@MANVENDRA-github
MANVENDRA-github deleted the feat/cost-tracking branch June 30, 2026 16:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant