Skip to content

feat: support richer token cost tracking for ask#1353

Merged
jsourcebot merged 7 commits into
mainfrom
jminnetian/tool-token-cost-tracking
Jun 20, 2026
Merged

feat: support richer token cost tracking for ask#1353
jsourcebot merged 7 commits into
mainfrom
jminnetian/tool-token-cost-tracking

Conversation

@jsourcebot

@jsourcebot jsourcebot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor
Screenshot 2026-06-18 at 5 43 17 PM

Adds tracking of token costs per step, also adds estimates of tool call token usage. This information is embedded in the chat history. Tool call token usages are estimates because a single step can run multiple tool calls and there is no mechanism to discern which part of the input token cost came from which tool call.

Summary by CodeRabbit

  • New Features

    • Per-step and per-tool token cost tracking in chat history
    • Token usage badges displaying estimated output tokens for tool calls
    • Step-indexed token usage information in chat details panel
  • Refactor

    • Reorganized thinking steps rendering structure to support token attribution
  • Tests

    • Added token estimation utility test suite

Estimate the input-token footprint of each tool call's output (the cost
the result imposes when fed back to the model on subsequent steps) using
a local length-based estimator, persist it per tool call in the chat
message metadata, and surface it inline in each tool call row next to
the Details toggle. Estimates are ~-prefixed to keep them distinct from
the authoritative billed token totals.
Record the provider-reported input/output token usage of each agent step
in the chat message metadata and display it per step group in the
thinking steps view (joined to UI step groups via the step index now
tagged on each tool token usage entry).

Also fix the tool output estimator to measure the model-visible payload:
tools with a toModelOutput mapping (all builtins) send only their output
text to the model, so estimating the raw ToolResult object was counting
UI-only metadata the model never sees. The bytes-per-token ratio is now
a uniform ~2 chars/token, calibrated against provider-reported per-step
usage of code-heavy tool results.
Collect usage from researchStream.steps and response.messages after the
stream completes (covers approval-gated and failed tool calls, off the
hot path), nest tool estimates under their step in a single
stepTokenUsage array, and join UI steps to entries by stepIndex.
@github-actions

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

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

Run ID: 9ea6fcc9-0476-4120-9752-f9aa11a7ae44

📥 Commits

Reviewing files that changed from the base of the PR and between 94936b3 and 814e3ac.

📒 Files selected for processing (3)
  • packages/web/src/ee/features/chat/agent.ts
  • packages/web/src/ee/features/chat/tokenEstimation.test.ts
  • packages/web/src/ee/features/chat/tokenEstimation.ts
💤 Files with no reviewable changes (2)
  • packages/web/src/ee/features/chat/tokenEstimation.test.ts
  • packages/web/src/ee/features/chat/tokenEstimation.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/web/src/ee/features/chat/agent.ts

Walkthrough

Adds per-step token usage tracking for the Ask Sourcebot chat agent. A new offline estimation utility computes token footprints from tool outputs. The message metadata schema gains a stepTokenUsage field, populated post-stream in the agent. The UI is updated to render per-step and per-tool token badges in the thinking steps panel, with a new ToolTokenBadge component.

Changes

Per-step tool token usage estimation and display

Layer / File(s) Summary
Token estimation utilities and schema types
packages/web/src/ee/features/chat/tokenEstimation.ts, packages/web/src/ee/features/chat/tokenEstimation.test.ts, packages/web/src/features/chat/types.ts
New tokenEstimation module exports estimateTokenCount, estimateToolOutputTokens, and estimateModelToolOutputTokens. sbChatMessageMetadataSchema gains optional stepTokenUsage with per-step counts and tool entries; StepTokenUsageEntry and ToolTokenUsageEntry derived types are exported. Tests cover text, JSON, content, and mapped tool-result shapes.
Post-stream step token usage computation in agent
packages/web/src/ee/features/chat/agent.ts, packages/web/src/ee/features/chat/agent.test.ts
After the research stream completes, the agent scans response.messages for tool-result parts, builds a toolCallId→token-estimate map, constructs ordered stepTokenUsage aligned to steps, prepends unclaimed estimates to the first step for approval-gated tools, and concatenates prior stepTokenUsage when emitting message-metadata. Test mock updated to resolve response as {messages: []}.
Step index tracking in chatThreadListItem
packages/web/src/ee/features/chat/components/chatThread/chatThreadListItem.tsx
useMemo now returns both uiVisibleThinkingSteps (as ThinkingStep[] with stepIndex) and answerStepIndex; a running counter increments on step-start parts. answerStepIndex is forwarded to DetailsCard.
DetailsCard and ThinkingSteps step-index and token-usage rendering
packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx, packages/web/src/ee/features/chat/components/chatThread/detailsCard.test.tsx
New exported ThinkingStep interface pairs stepIndex with parts. DetailsCardProps gains optional answerStepIndex. DetailsCardComponent builds toolTokenUsageMap from metadata.stepTokenUsage and passes it to ThinkingSteps, which iterates ThinkingStep[], looks up usage by step index, and conditionally inlines or right-aligns StepTokenUsage. StepPartRenderer now accepts toolTokenUsageMap and routes built-in tools through TOOL_GUARD_CONFIG instead of per-tool switch cases, threading estimatedOutputTokens into renderers.
ToolTokenBadge and tool component integration
packages/web/src/ee/features/chat/components/chatThread/tools/toolTokenBadge.tsx, packages/web/src/ee/features/chat/components/chatThread/tools/toolOutputGuard.tsx, packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx, packages/web/src/ee/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx
New ToolTokenBadge client component renders a shortened token count with a tooltip. ToolOutputGuard, McpToolComponent, and ToolSearchToolComponent each add an optional estimatedOutputTokens prop and conditionally render ToolTokenBadge with a vertical Separator when provided.
Changelog
CHANGELOG.md
Adds entry for per-step token cost tracking and estimated tool call token usage in Ask Sourcebot chat history.

Sequence Diagram(s)

sequenceDiagram
  participant Agent as agent.ts
  participant ResearchStream as researchStream
  participant Metadata as message-metadata
  participant DetailsCard as DetailsCard
  participant ThinkingSteps as ThinkingSteps
  participant ToolTokenBadge as ToolTokenBadge

  Agent->>ResearchStream: await stream completion
  ResearchStream-->>Agent: response.messages, steps
  Agent->>Agent: scan tool-result parts → toolUsageByToolCallId
  Agent->>Agent: align to steps → stepTokenUsage[]
  Agent->>Metadata: emit stepTokenUsage (concat with priorMetadata)
  Metadata-->>DetailsCard: metadata.stepTokenUsage
  DetailsCard->>DetailsCard: build toolTokenUsageMap
  DetailsCard->>ThinkingSteps: ThinkingStep[], toolTokenUsageMap, answerStepIndex
  ThinkingSteps->>ThinkingSteps: lookup usage[stepIndex] per step
  ThinkingSteps->>ToolTokenBadge: estimatedOutputTokens per tool call
  ToolTokenBadge-->>ThinkingSteps: badge UI with tooltip
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • sourcebot-dev/sourcebot#843: Modifies the same detailsCard.tsx thinking-steps/tool-part rendering path to add tool-listCommits, which this PR further refactors with TOOL_GUARD_CONFIG and step-indexed token usage.

Suggested reviewers

  • brendan-kellam
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: support richer token cost tracking for ask' accurately describes the main feature addition: enhanced token cost tracking per step and per tool for the ask feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jminnetian/tool-token-cost-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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/web/src/ee/features/chat/agent.ts (1)

257-263: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent computed token metadata from being overwritten by caller metadata

On Line 262, spreading ...metadata last lets external metadata replace derived fields (stepTokenUsage, totals, modelName, traceId), which can break the step-index join contract used by the UI.

Suggested fix
             writer.write({
                 type: 'message-metadata',
                 messageMetadata: {
+                    ...metadata,
                     totalTokens: (priorMetadata?.totalTokens ?? 0) + (totalUsage.totalTokens ?? 0),
                     totalInputTokens: (priorMetadata?.totalInputTokens ?? 0) + (totalUsage.inputTokens ?? 0),
                     totalOutputTokens: (priorMetadata?.totalOutputTokens ?? 0) + (totalUsage.outputTokens ?? 0),
                     totalCacheReadTokens: (priorMetadata?.totalCacheReadTokens ?? 0) + (totalUsage.inputTokenDetails?.cacheReadTokens ?? 0),
                     totalCacheWriteTokens: (priorMetadata?.totalCacheWriteTokens ?? 0) + (totalUsage.inputTokenDetails?.cacheWriteTokens ?? 0),
                     totalResponseTimeMs: (priorMetadata?.totalResponseTimeMs ?? 0) + (new Date().getTime() - startTime.getTime()),
                     // Concatenated (not summed) across approval-continuation
                     // phases so earlier phases' steps are preserved in order.
                     stepTokenUsage: [...(priorMetadata?.stepTokenUsage ?? []), ...stepTokenUsage],
                     modelName,
                     traceId,
-                    ...metadata,
                 }
             });
🤖 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 `@packages/web/src/ee/features/chat/agent.ts` around lines 257 - 263, The
spread operator `...metadata` is placed last in the object literal, which allows
caller-provided metadata to overwrite the computed derived fields
(stepTokenUsage, modelName, traceId, and totals). Reorder the object properties
by moving `...metadata` to the beginning of the object literal before the
computed fields, so that the carefully derived values (stepTokenUsage,
modelName, traceId, and any totals fields) are spread after the external
metadata and cannot be accidentally overwritten by caller data.
🤖 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.

Outside diff comments:
In `@packages/web/src/ee/features/chat/agent.ts`:
- Around line 257-263: The spread operator `...metadata` is placed last in the
object literal, which allows caller-provided metadata to overwrite the computed
derived fields (stepTokenUsage, modelName, traceId, and totals). Reorder the
object properties by moving `...metadata` to the beginning of the object literal
before the computed fields, so that the carefully derived values
(stepTokenUsage, modelName, traceId, and any totals fields) are spread after the
external metadata and cannot be accidentally overwritten by caller data.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 181d5be9-92d5-4715-9a94-791f21b19000

📥 Commits

Reviewing files that changed from the base of the PR and between 26435a4 and da8cd83.

📒 Files selected for processing (12)
  • packages/web/src/ee/features/chat/agent.test.ts
  • packages/web/src/ee/features/chat/agent.ts
  • packages/web/src/ee/features/chat/components/chatThread/chatThreadListItem.tsx
  • packages/web/src/ee/features/chat/components/chatThread/detailsCard.test.tsx
  • packages/web/src/ee/features/chat/components/chatThread/detailsCard.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/mcpToolComponent.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolOutputGuard.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolSearchToolComponent.tsx
  • packages/web/src/ee/features/chat/components/chatThread/tools/toolTokenBadge.tsx
  • packages/web/src/features/chat/tokenEstimation.test.ts
  • packages/web/src/features/chat/tokenEstimation.ts
  • packages/web/src/features/chat/types.ts

@jsourcebot jsourcebot changed the title Jminnetian/tool token cost tracking feat: support richer token cost tracking for ask Jun 19, 2026
Spread caller-supplied metadata before the derived token fields so
stepTokenUsage and the totals can't be clobbered, which would desync
the UI's index-based step join.
Comment thread packages/web/src/ee/features/chat/tokenEstimation.ts
brendan-kellam
brendan-kellam previously approved these changes Jun 19, 2026
@jsourcebot jsourcebot merged commit b9c7f0d into main Jun 20, 2026
11 checks passed
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.

2 participants