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

@jsourcebotjsourcebot 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

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck 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.

@coderabbitaicoderabbitaiBot 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

@jsourcebotjsourcebot changed the title Jminnetian/tool token cost trackingfeat: support richer token cost tracking for askJun 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 threadpackages/web/src/ee/features/chat/tokenEstimation.ts
brendan-kellam
brendan-kellam previously approved these changes Jun 19, 2026
@jsourcebot
jsourcebot merged commit b9c7f0d into mainJun 20, 2026
11 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jun 20, 2026
@coderabbitaicoderabbitaiBot mentioned this pull request Jul 6, 2026
Sign up for freeto 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

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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

@jsourcebotjsourcebot 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

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck 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.

@coderabbitaicoderabbitaiBot 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

@jsourcebotjsourcebot changed the title Jminnetian/tool token cost trackingfeat: support richer token cost tracking for askJun 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 threadpackages/web/src/ee/features/chat/tokenEstimation.ts
brendan-kellam
brendan-kellam previously approved these changes Jun 19, 2026
@jsourcebot
jsourcebot merged commit b9c7f0d into mainJun 20, 2026
11 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jun 20, 2026
@coderabbitaicoderabbitaiBot mentioned this pull request Jul 6, 2026
Sign up for freeto 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

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@jsourcebotjsourcebot 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

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck 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.

@coderabbitaicoderabbitaiBot 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

@jsourcebotjsourcebot changed the title Jminnetian/tool token cost trackingfeat: support richer token cost tracking for askJun 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 threadpackages/web/src/ee/features/chat/tokenEstimation.ts
brendan-kellam
brendan-kellam previously approved these changes Jun 19, 2026
@jsourcebot
jsourcebot merged commit b9c7f0d into mainJun 20, 2026
11 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jun 20, 2026
@coderabbitaicoderabbitaiBot mentioned this pull request Jul 6, 2026
Sign up for freeto 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

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@jsourcebotjsourcebot 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

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck 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.

@coderabbitaicoderabbitaiBot 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

@jsourcebotjsourcebot changed the title Jminnetian/tool token cost trackingfeat: support richer token cost tracking for askJun 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 threadpackages/web/src/ee/features/chat/tokenEstimation.ts
brendan-kellam
brendan-kellam previously approved these changes Jun 19, 2026
@jsourcebot
jsourcebot merged commit b9c7f0d into mainJun 20, 2026
11 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jun 20, 2026
@coderabbitaicoderabbitaiBot mentioned this pull request Jul 6, 2026
Sign up for freeto 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

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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

@jsourcebotjsourcebot 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

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck 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.

@coderabbitaicoderabbitaiBot 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

@jsourcebotjsourcebot changed the title Jminnetian/tool token cost trackingfeat: support richer token cost tracking for askJun 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 threadpackages/web/src/ee/features/chat/tokenEstimation.ts
brendan-kellam
brendan-kellam previously approved these changes Jun 19, 2026
@jsourcebot
jsourcebot merged commit b9c7f0d into mainJun 20, 2026
11 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jun 20, 2026
@coderabbitaicoderabbitaiBot mentioned this pull request Jul 6, 2026
Sign up for freeto 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

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@jsourcebotjsourcebot 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

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck 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.

@coderabbitaicoderabbitaiBot 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

@jsourcebotjsourcebot changed the title Jminnetian/tool token cost trackingfeat: support richer token cost tracking for askJun 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 threadpackages/web/src/ee/features/chat/tokenEstimation.ts
brendan-kellam
brendan-kellam previously approved these changes Jun 19, 2026
@jsourcebot
jsourcebot merged commit b9c7f0d into mainJun 20, 2026
11 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jun 20, 2026
@coderabbitaicoderabbitaiBot mentioned this pull request Jul 6, 2026
Sign up for freeto 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

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@jsourcebotjsourcebot 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

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck 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.

@coderabbitaicoderabbitaiBot 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

@jsourcebotjsourcebot changed the title Jminnetian/tool token cost trackingfeat: support richer token cost tracking for askJun 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 threadpackages/web/src/ee/features/chat/tokenEstimation.ts
brendan-kellam
brendan-kellam previously approved these changes Jun 19, 2026
@jsourcebot
jsourcebot merged commit b9c7f0d into mainJun 20, 2026
11 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jun 20, 2026
@coderabbitaicoderabbitaiBot mentioned this pull request Jul 6, 2026
Sign up for freeto 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

@jsourcebot@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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

@jsourcebotjsourcebot 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

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck 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.

@coderabbitaicoderabbitaiBot 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

@jsourcebotjsourcebot changed the title Jminnetian/tool token cost trackingfeat: support richer token cost tracking for askJun 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 threadpackages/web/src/ee/features/chat/tokenEstimation.ts
brendan-kellam
brendan-kellam previously approved these changes Jun 19, 2026
@jsourcebot
jsourcebot merged commit b9c7f0d into mainJun 20, 2026
11 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Jun 20, 2026
@coderabbitaicoderabbitaiBot mentioned this pull request Jul 6, 2026
Sign up for freeto 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

@jsourcebot@brendan-kellam