Conversation
…ning) The previous extractor only read `input_tokens`/`output_tokens`/`total_tokens` aliases and picked the first defined value across sources. This missed the pi-ai Usage shape (`input`, `output`, `cacheRead`, `cacheWrite`) entirely for per-field reads and also failed to sum usage across multiple assistant messages in a single turn. - Extend `AgentTurnUsage` with `cachedInputTokens`, `cacheCreationTokens`, and `reasoningTokens` so diagnostics carry every counter the provider reports as its own field. - Teach `extractGenAiUsageSummary` to recognize pi-ai aliases and to sum counters across sources so multi-message turns report aggregate usage. - Render the Slack footer "Tokens" value as the sum of all reported component counters (input + output + cachedInput + cacheCreation + reasoning) instead of relying on the provider's inconsistent `totalTokens` field. Fall back to `totalTokens` only when no component counters were reported. Co-Authored-By: Devin <devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: David Cramer <david@sentry.io>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The only source reaching extractGenAiUsageSummary is pi-ai's normalized AssistantMessage.usage (input, output, cacheRead, cacheWrite, totalTokens). The OpenAI/Anthropic/Gemini-style aliases (input_tokens, prompt_tokens, cached_input_tokens, etc.) never matched anything in practice — they coincidentally agreed on totalTokens only. - Remove collectUsageRoots/readTokenCount/alias table in favor of a single PI_USAGE_FIELDS map from pi-ai field name to AgentTurnUsage field name. - Drop reasoningTokens from AgentTurnUsage; pi-ai folds reasoning into output already and never exposes it as a separate top-level field. - Update footer and tests accordingly. Co-Authored-By: Devin <devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: David Cramer <david@sentry.io>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 512c423. Configure here.
| cachedInputTokens?: number; | ||
| /** Input tokens written into the provider's prompt cache. */ | ||
| cacheCreationTokens?: number; | ||
| /** Provider-reported total. May not equal the sum of individual counters across providers. */ |
There was a problem hiding this comment.
Missing reasoningTokens field promised by PR
Medium Severity
The PR title says "count all token types (input, output, cached, reasoning)" and the summary explicitly states AgentTurnUsage is extended with reasoningTokens. The footer description says the total is input + output + cachedInput + cacheCreation + reasoning. However, reasoningTokens is entirely missing — it's not in the AgentTurnUsage interface, not in PI_USAGE_FIELDS, and not in resolveTotalTokens's components array. Grep confirms zero matches across the package. When a provider surfaces reasoning tokens as a distinct field (not folded into output), they'll be silently dropped and the Slack footer total will under-count.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 512c423. Configure here.


Summary
The turn-diagnostics usage extractor was under-counting tokens for two reasons:
input_tokens/output_tokens/total_tokensstyle names, so the pi-aiAssistantMessage.usageshape (input,output,cacheRead,cacheWrite,totalTokens) was only matching ontotalTokens. Cache-read, cache-write, and reasoning tokens were dropped on the floor..find((v) => v !== undefined)and took the first message's usage instead of summing across the turn.The Slack footer also computed total tokens as
inputTokens + outputTokensonly, which missed cached/cache-creation/reasoning tokens even when individual counters were available.Changes
packages/junior/src/chat/usage.ts— extendAgentTurnUsagewithcachedInputTokens,cacheCreationTokens, andreasoningTokens. Diagnostics now carry every counter the provider reports as its own field so renderers can choose how to present them.packages/junior/src/chat/logging.ts—extractGenAiUsageSummarynow:input,output,cacheRead,cacheWrite) alongside the previous OpenAI/Anthropic/Gemini aliases;packages/junior/src/chat/slack/footer.ts— render theTokensfooter item as the sum of every reported component counter (input + output + cachedInput + cacheCreation + reasoning). Falls back tototalTokensonly when no component counters were reported, since providers disagree on whethertotalTokensincludes cached tokens.packages/junior/src/chat/respond.ts— detect "has usage" by checking any field instead of hard-coding the old three.tests/unit/logging/extract-gen-ai-usage-summary.test.tsand additional cases intests/unit/slack/footer.test.ts.Review & Testing Checklist for Human
Tokensfooter value now reflects cached + cache-creation tokens (e.g. a turn against an Anthropic model that hits prompt caching).AssistantReply.diagnostics.usage(logs, metrics, evals) handle the new optional fields correctly.totalTokensacross sources is acceptable; if any call site currently expectstotalTokensto be a single-message value rather than a turn aggregate, that assumption changes with this PR.Notes
totalTokensis still preserved as an individual field. We prefer the sum of component counters when any are present because pi-ai's provider adapters disagree on whether theirtotalTokensalready includescacheRead(openai-completions adds it, openai-responses passes the provider value through). Summing components avoids both under- and over-counting.reasoning_tokens/reasoningTokenskey. pi-ai currently folds reasoning tokens intooutputfor the OpenAI completions path, soreasoningTokenswill often remain undefined — no double counting.Link to Devin session: https://app.devin.ai/sessions/dcea113d0cba43448157973f8f4b7105
Requested by: @dcramer