GitHubBook a demoStart routing
Browse docs

Cost analytics

How every LLM dollar is attributed and rolled up — the ledger top views, the eight-dimension rollup tuple, and the decision-log row behind every number.

On this page

Cost analytics is the read side of the decision ledger. Every proxied request writes one costed JSONL row; everything on this page is an aggregation of those rows, and two surfaces answering the same question can never disagree because both read the same log.

Two ways spend is attributed

TokenTriage attributes spend along two distinct surfaces — don’t conflate them:

  1. The ledger top views roll the raw log up by a single facet from the terminal: --by model | agent | tool | trace | step-class, optionally scoped with --since. Token types (input / output / cache / reasoning) are priced per row and shown as columns, not as a facet.

    $ tokentriage top --by model --since 24h
    $ tokentriage top --trace tr_9f2c…      # per-step cost tree for one trace

    MCP-tool accounting is per turn: because agentic clients resend the whole conversation each turn, each tool invocation is counted once (the turn that first feeds its result back), so an N-turn loop reports N calls, not ~N²/2. The IN-TOK-ATTR column is a char/4 estimate — those rows carry "estimated": true, distinct from a provider-reported usage.source.

  2. The cost-analytics rollup tuple powers the dashboard’s finance views. It pre- aggregates hourly on an eight-dimension key:

    dim.tenant · dim.team · dim.key · dim.feature · decision.model_served · decision.tier · http.provider · http.endpoint

localhost:9090/ui/costDemo data
A real capture of the dashboard in demo mode: the Cost surface shows total spend, a spend spike, and per-tenant and per-model breakdowns; clicking the unattributed-spend alert drills from the total into the costed decision-log rows, each carrying its EST and UNPRICED flags. Watermarked DEMO DATA.
The Cost surface — spend attributed across the rollup dimensions, each figure carrying its pricing snapshot, with the drill from an unattributed-spend alert down to the costed rows (real dashboard, demo mode).

Business dimensions

The four business dims come from request headers when ledger.dimensions.enabled is set: x-tt-user, x-tt-tenant, x-tt-feature, and bounded x-tt-tag-<k> (default up to 8 tags). They’re stamped on the row and stripped before the upstream forward; spend with no value renders as a first-class “unattributed” slice, never silently dropped. Promotion to Prometheus labels is opt-in and cardinality-guarded via ledger.dimensions.promote_to_metrics. See Observability & tracing for the full header contract.

Rollups and grains

When analytics.rollups.enabled (over dashboard.index.enabled) is on, a month-scale question is answered from pre-aggregated cells rather than a full raw-row scan — roughly 3× faster in the house benchmark (see Benchmarks for the measured figures and methodology; the numbers are hardware-specific, not a wall-clock promise). Time buckets use a fixed grain vocabulary: 1m · 1h · 1d · 1w · 1mo · 1q.

localhost:9090/ui/cost/breakdownDemo data
The finance breakdown view: spend over calendar grains by rollup dimension, with per-row excluded-row disclosure.
Cost · breakdown — spend over calendar grains, with the excluded-row count shown beside each figure.

UNPRICED, everywhere

A row TokenTriage could not price is UNPRICED, and it propagates as a count — never as $0.00 — through every rollup, report, forecast, and export. A total reads like $41.20 over 1,904 priced rows — 116 unpriced rows excluded. This is the same rule the honesty model sets out, applied to every aggregate.

Next: turn attribution into action with Cost optimization & savings verdicts.