Skip to content

feat(dashboard): surface cumulative token totals in StatsBar#365

Open
ninadgns wants to merge 1 commit into
SignalPilot-Labs:mainfrom
ninadgns:feat/statsbar-token-totals
Open

feat(dashboard): surface cumulative token totals in StatsBar#365
ninadgns wants to merge 1 commit into
SignalPilot-Labs:mainfrom
ninadgns:feat/statsbar-token-totals

Conversation

@ninadgns

Copy link
Copy Markdown

Problem

The StatsBar shows Context, which reads like overall token usage but isn't. context_tokens is a snapshot of the last message's window (input + cache_creation + cache_read of that one message, stream.py:412) — it moves up and down as the conversation compacts, so it can't be a total. Sitting next to a settled Cost figure, it invites exactly the wrong reading.

Meanwhile the four cumulative counters are already streamed to the browser. The usage audit event carries the full breakdown:

"context_tokens":              ...,
"total_input_tokens":          ...,
"total_output_tokens":         ...,
"cache_creation_input_tokens": ...,
"cache_read_input_tokens":     ...,
"total_cost_usd":              ...,

computeLiveStats received that whole object and read two fields, discarding the rest. There was no way to see overall usage in the UI at all — you had to query the audit endpoint or Postgres directly.

Change

Add a Tokens stat showing the sum of all four counters, with a hover breakdown (Input 1.5k · Output 2.0M · Cache write 0 · Cache read 300) naming each component. Context stays as-is — it's a genuinely useful and different signal.

Render-only. No backend, DB, or event-shape changes; the data was already client-side.

Notes

  • Summing all four matters: cache reads dominate agentic runs, so input+output alone under-reports by an order of magnitude. Covered by a regression test.
  • Follows the existing live-then-settled fallback used by formatContextStat.
  • Renders rather than 0 when nothing was ever reported, per the repo's fail-fast rule that distinct failure modes must render distinctly — a pipeline that never emitted usage must not look like a free run.
  • total_output_tokens includes thinking tokens; the breakdown label stays generic rather than implying otherwise.

Testing

  • pnpm exec vitest run — 95 files, 719 tests pass (6 new)
  • pnpm exec tsc --noEmit — clean

Python checks (pyright, ruff, pytest) were not run: this change touches no Python.

🤖 Generated with Claude Code

The agent already streams the full usage breakdown to the browser — the
`usage` audit event carries total_input_tokens, total_output_tokens,
cache_creation_input_tokens and cache_read_input_tokens alongside
context_tokens. computeLiveStats read only context_tokens and
total_cost_usd and dropped the four cumulative counters on the floor, so
the only token number in the UI was `Context`.

`Context` is a snapshot of the last message's window and moves both up
and down, which makes it easy to misread as overall usage. Add a
`Tokens` stat next to it showing the sum of all four counters, with a
hover breakdown naming each component.

Render-only: no backend, DB, or event-shape changes. Follows the
existing live-then-settled fallback and renders `—` rather than `0` when
no usage was ever reported, so a broken pipeline stays visible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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