Skip to content

feat: track and display LLM token usage across CLI, reports, and extension - #224

Merged
jithin23-kv merged 4 commits into
masterfrom
feat/token-usage-tracking
Jul 29, 2026
Merged

feat: track and display LLM token usage across CLI, reports, and extension#224
jithin23-kv merged 4 commits into
masterfrom
feat/token-usage-tracking

Conversation

@arunSunnyKVS

@arunSunnyKVS arunSunnyKVS commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Problem

opfor run makes multiple LLM calls per run (attacker generation, adaptive follow-ups, judge) via the Vercel AI SDK, which returns token usage on every generateText() result. Currently all usage data is discarded — users have no visibility into how many tokens a run consumed, making cost management and budget alerting impossible.

Solution

Introduce a TokenTracker class with a parent-child hierarchy: one root tracker per run, one child per evaluator. Every LLM call site (withRetry, generateText, generateObject, chatCompletionJsonContent) now records its usage. Aggregated totals are surfaced in the CLI summary, HTML/JSON reports, and browser extension popup.

Changes

core/

  • execute/tokenTracker.ts — New TokenTracker / ChildTracker classes and TokenUsage interface
  • execute/types.ts — Added optional tokenUsage field to EvaluatorResult and UnifiedRunReport.summary
  • execute/runAll.ts — Thread TokenTracker through RunAllOptions, attach run-level totals to report summary
  • execute/evaluatorLoop.ts — Create per-evaluator child trackers, attach evalTracker.totals to every evaluator result (including partial/error/stop branches)
  • execute/runAllBrowser.ts — Same per-evaluator tracking for browser path, centralized partial-result decoration
  • execute/agentAttackDriver.ts — Pass tokenTracker to judge calls
  • execute/mcpAttackDriver.ts — Pass tokenTracker to MCP judge calls
  • lib/llmRetry.tswithRetry auto-records usage from successful results when tokenTracker is provided
  • llm/openaiCompatible.tschatCompletionJsonContent parses and records usage from raw fetch responses
  • generate/generateAttacks.ts — Thread tokenTracker through attack generation
  • generate/generateNextTurn.ts — Thread tokenTracker through adaptive follow-up generation
  • evaluators/judge.ts — Thread tokenTracker through judge calls
  • run/judge.ts — Thread tokenTracker through MCP judge helpers
  • report/buildReport.ts — Map tokenUsage into EvaluatorViewModel for HTML rendering
  • report/render.ts — Display per-evaluator token counts in HTML report detail accordions
  • report/types.ts — Added tokenUsage to EvaluatorViewModel and report summary type

runners/cli/

  • commands/run.ts — Display token usage summary line after results

runners/extension/

  • orchestrator.js — Pass tokenUsage from runAllBrowser report to popup result objects (success + error paths)
  • popup.js — Aggregate and display token usage on Done screen (total + input/output breakdown), per-evaluator tokens in result rows, per-evaluator tokens in downloadable HTML report
  • popup.html — Token stat card in Done screen, CSS adjustments for 2-column stat grid

Tests

  • core/tests/tokenTracker.test.ts — Unit tests for TokenTracker parent-child hierarchy

Docs

  • docs/cli.md — Token usage tracking section
  • docs/browser-extension.md — Token usage note
  • AGENTS.mdtokenTracker.ts in key files table, token tracking mechanism docs

Issue

Closes #223

How to test

CLI

  1. npm run build
  2. opfor run --config tests/e2e/agents/customer-support/opfor.config.json
  3. Verify token usage summary line appears after results:
    Token usage: 51,323 input / 6,057 output (57,380 total)
    
  4. Open the generated HTML report — verify per-evaluator token counts in the detail accordions and run-level totals in the executive summary
  5. Check the JSON report — verify summary.tokenUsage and per-evaluator tokenUsage fields

Browser extension

  1. npm run build (rebuilds core.bundle.js)
  2. Load the extension in Chrome (chrome://extensions → Load unpacked → runners/extension/)
  3. Run a suite against any chat UI
  4. Verify the Done screen shows total tokens with input/output breakdown
  5. Verify per-evaluator token counts appear in result rows
  6. Download the HTML report — verify per-evaluator tokens in the detail section

Unit tests

npm test -- --run core/tests/tokenTracker.test.ts

Screenshots

N/A

Summary by CodeRabbit

  • New Features
    • Added LLM token usage tracking (input/output/total) across generation, retries, judging, and per-evaluator execution.
    • Token usage is now included in run summaries, evaluator results, downloadable HTML/JSON reports, and browser/extension “Done” screens.
  • Documentation
    • Updated CLI and browser-extension docs to describe token metrics and where they appear.
  • Tests
    • Added unit tests validating usage parsing, accumulation, and parent/child aggregation behavior.

…nsion

Add a TokenTracker class that meters every LLM call (attacker generation,
adaptive follow-ups, judge) and aggregates per-evaluator and per-run totals.

- Core: thread TokenTracker through runAll → evaluatorLoop → attack drivers;
  withRetry, generateText, and chatCompletionJsonContent auto-record usage
- CLI: print token summary line after run results
- Reports: add Token Usage stat card to HTML executive summary, per-evaluator
  counts in detail headers, and tokenUsage fields in JSON output
- Extension: propagate tokenUsage from orchestrator to popup; show Tokens stat
  on Done screen with in/out breakdown; per-evaluator counts in result rows
  and downloaded HTML report; fix pruneRawForHistory to preserve tokenUsage;
  fix "# Details" → proper section 5 header in HTML template
- Docs: update cli.md, browser-extension.md, AGENTS.md

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ff049a2f-9a4d-474a-ade1-243a0cd91c07

📥 Commits

Reviewing files that changed from the base of the PR and between 6b11423 and 30914d3.

📒 Files selected for processing (2)
  • core/src/execute/tokenTracker.ts
  • core/tests/tokenTracker.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • core/tests/tokenTracker.test.ts
  • core/src/execute/tokenTracker.ts

Walkthrough

The PR adds nested token tracking across LLM generation and judging, attaches usage to evaluator and run results, and displays token counts in CLI, JSON, HTML, and browser-extension outputs.

Changes

Token usage tracking

Layer / File(s) Summary
Token usage accumulator and contracts
core/src/execute/tokenTracker.ts, core/src/execute/types.ts, core/src/report/types.ts, core/tests/tokenTracker.test.ts
Adds validated nested token aggregation, token usage result fields, and coverage for accumulation and child behavior.
LLM usage capture
core/src/lib/llmRetry.ts, core/src/llm/openaiCompatible.ts, core/src/generate/*, core/src/evaluators/judge.ts, core/src/run/judge.ts
Records provider usage from retry-wrapped generation, adaptive turns, MCP turns, and judge calls.
Execution pipeline propagation
core/src/execute/evaluatorLoop.ts, core/src/execute/*AttackDriver.ts, core/src/execute/runAll*.ts
Creates run and evaluator trackers, passes them through attack execution, and attaches evaluator and run totals to reports.
Report and UI surfaces
core/src/report/*, runners/cli/src/commands/run.ts, runners/extension/*, docs/*, AGENTS.md
Displays token totals in reports and interfaces, preserves extension history data, and documents tracking behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning CLI, reports, and extension tracking are covered, but the required run_finish NDJSON tokenUsage field is not shown in the changes. Add tokenUsage to the run_finish NDJSON event summary and verify the emitter serializes run-level token totals consistently.
Docstring Coverage ⚠️ Warning Docstring coverage is 76.19% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: tracking and displaying LLM token usage across the CLI, reports, and extension.
Description check ✅ Passed The description includes all required sections and covers the problem, solution, changes, issue, testing, and screenshots.
Out of Scope Changes check ✅ Passed The changes stay focused on token tracking, reporting, and display; no unrelated functional features stand out.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/token-usage-tracking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@arunSunnyKVS
arunSunnyKVS marked this pull request as ready for review July 28, 2026 11:54

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
core/tests/tokenTracker.test.ts (1)

10-22: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the totalTokens branch.

These tests only verify totals derived from input/output, so they would pass while explicit provider totals are silently ignored. Add a total-only case and a case where totalTokens differs from the component sum.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/tests/tokenTracker.test.ts` around lines 10 - 22, Add tests in the
TokenTracker record suite for an input containing only explicit totalTokens, and
for input where totalTokens differs from inputTokens plus outputTokens; assert
that totals preserve the provider-supplied total in both cases while retaining
the expected component values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/src/execute/evaluatorLoop.ts`:
- Around line 243-245: Centralize evaluator-result decoration so partial results
preserve per-evaluator token usage: in core/src/execute/evaluatorLoop.ts lines
243-245, ensure the early-stop return applies evalTracker.totals to
toEvaluatorResult output; in core/src/execute/runAllBrowser.ts lines 232-242,
apply the same decoration in both attack error/stop branches. Reuse the existing
evaluator result and evalTracker symbols without changing normal completion
behavior.

In `@core/src/execute/tokenTracker.ts`:
- Around line 26-38: Preserve explicit total token counts end to end: update
TokenTracker.record in core/src/execute/tokenTracker.ts to accumulate supplied
totalTokens and use input/output sums only when totalTokens is absent; update
the OpenAI-compatible adapter in core/src/llm/openaiCompatible.ts to pass
data.usage.total_tokens; extend core/tests/tokenTracker.test.ts with total-only
and mismatched-total regression cases.

In `@core/src/lib/llmRetry.ts`:
- Around line 149-152: Validate the final LLM usage object with the existing Zod
validation approach before calling TokenTracker.record(), replacing unchecked
casts/property access. Apply this at core/src/lib/llmRetry.ts:149-152,
core/src/llm/openaiCompatible.ts:149-157, and both
core/src/generate/generateNextTurn.ts:154-155 and :301-302 call sites; only
record usage when it matches the expected inputTokens/outputTokens shape.

In `@docs/cli.md`:
- Around line 220-224: Update the fenced code block in the CLI results example
by specifying an appropriate language, such as text, immediately after its
opening fence to satisfy markdownlint MD040; leave the example content
unchanged.

---

Nitpick comments:
In `@core/tests/tokenTracker.test.ts`:
- Around line 10-22: Add tests in the TokenTracker record suite for an input
containing only explicit totalTokens, and for input where totalTokens differs
from inputTokens plus outputTokens; assert that totals preserve the
provider-supplied total in both cases while retaining the expected component
values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a0f354c-09be-4e06-b271-c5d6a75e74b6

📥 Commits

Reviewing files that changed from the base of the PR and between 094687c and 3601c33.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (24)
  • AGENTS.md
  • core/src/evaluators/judge.ts
  • core/src/execute/agentAttackDriver.ts
  • core/src/execute/evaluatorLoop.ts
  • core/src/execute/mcpAttackDriver.ts
  • core/src/execute/runAll.ts
  • core/src/execute/runAllBrowser.ts
  • core/src/execute/tokenTracker.ts
  • core/src/execute/types.ts
  • core/src/generate/generateAttacks.ts
  • core/src/generate/generateNextTurn.ts
  • core/src/lib/llmRetry.ts
  • core/src/llm/openaiCompatible.ts
  • core/src/report/buildReport.ts
  • core/src/report/render.ts
  • core/src/report/types.ts
  • core/src/run/judge.ts
  • core/tests/tokenTracker.test.ts
  • docs/browser-extension.md
  • docs/cli.md
  • runners/cli/src/commands/run.ts
  • runners/extension/orchestrator.js
  • runners/extension/popup.html
  • runners/extension/popup.js

Comment thread core/src/execute/evaluatorLoop.ts
Comment thread core/src/execute/tokenTracker.ts Outdated
Comment thread core/src/lib/llmRetry.ts Outdated
Comment thread docs/cli.md
arunSunnyKVS and others added 2 commits July 29, 2026 10:09
…cstrings

Centralize evaluator-result decoration so stop/error branches also
receive evalTracker.totals, preventing partial reports from losing
the affected evaluator's usage.

Add docstrings across all modified files to meet the 80% coverage
threshold required by CodeRabbit.

Co-authored-by: Cursor <cursoragent@cursor.com>
TokenTracker now accumulates the provider-supplied totalTokens
(which may include reasoning tokens) instead of recomputing it
from inputTokens + outputTokens.

All recording boundaries (withRetry, chatCompletionJsonContent,
generateNextTurn) now validate the usage shape with a Zod schema
before persisting, rejecting negative, non-integer, or unexpected
fields.

The OpenAI-compatible adapter now forwards total_tokens into
TokenTracker.record().

New test cases cover total-only usage, mismatched totals, and
parseUsage rejection of malformed input.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/src/execute/tokenTracker.ts`:
- Around line 30-41: Update LlmUsageSchema in core/src/execute/tokenTracker.ts
to strip unknown provider metadata while validating and normalizing inputTokens,
outputTokens, and totalTokens; remove the strict rejection behavior. Update the
regression test in core/tests/tokenTracker.test.ts at lines 119-123 to expect
the normalized tracked usage when extra metadata fields are present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bba93145-28f3-4675-b71c-c749198786e6

📥 Commits

Reviewing files that changed from the base of the PR and between 841824b and 6b11423.

📒 Files selected for processing (5)
  • core/src/execute/tokenTracker.ts
  • core/src/generate/generateNextTurn.ts
  • core/src/lib/llmRetry.ts
  • core/src/llm/openaiCompatible.ts
  • core/tests/tokenTracker.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • core/src/lib/llmRetry.ts
  • core/src/llm/openaiCompatible.ts
  • core/src/generate/generateNextTurn.ts

Comment thread core/src/execute/tokenTracker.ts
Replace .strict() with .passthrough() in LlmUsageSchema so
provider-specific fields (cachedTokens, reasoningTokens, etc.)
are silently stripped instead of failing the entire parse.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jithin23-kv
jithin23-kv merged commit 5f4b355 into master Jul 29, 2026
9 of 10 checks passed
@jithin23-kv
jithin23-kv deleted the feat/token-usage-tracking branch July 29, 2026 11:04
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.

feat: show token utilization to users in CLI and extension

2 participants