Skip to content

fix: report real token usage and estimate model costs#20

Merged
ephraimduncan merged 5 commits intomainfrom
fix/token-usage-tracking
Mar 22, 2026
Merged

fix: report real token usage and estimate model costs#20
ephraimduncan merged 5 commits intomainfrom
fix/token-usage-tracking

Conversation

@ephraimduncan
Copy link
Copy Markdown
Owner

Summary

Fixes token usage always showing 0 and cost always showing $0.00 in OpenCode.

Changes

Real token usage from gRPC stream (src/proxy.ts)

  • Track outputTokens from Cursor's tokenDelta messages and totalTokens from checkpoint tokenDetails
  • Emit a final SSE chunk with usage (prompt/completion/total) per OpenAI spec for streaming responses
  • Return real usage in non-streaming responses via a new CollectedResponse type
  • Add stream_options to the request interface

Market-rate cost estimation (src/index.ts)

  • Add a cost table with $/M token rates for all current Cursor models (Anthropic, Google, OpenAI, xAI, Moonshot, Cursor)
  • Pattern-matching fallback for dynamically discovered models whose exact ID isn't in the table
  • Sensible default (sonnet-tier) for completely unknown models
  • Replace hardcoded cost: { input: 0, output: 0, ... } with estimateModelCost(model.id)

The proxy hardcoded usage: { prompt_tokens: 0, completion_tokens: 0,
total_tokens: 0 } in both streaming and non-streaming responses, causing
OpenCode's sidebar to always show 0 tokens.

Cursor provides token data via two channels that were being ignored:
- TokenDeltaUpdate (incremental output token counts in InteractionUpdate)
- ConversationTokenDetails (total context usage in checkpoint updates)

Changes:
- Handle tokenDelta case in handleInteractionUpdate, accumulating output
  token counts in StreamState
- Extract tokenDetails.usedTokens from ConversationStateStructure on
  checkpoint updates
- Emit a final usage-only SSE chunk (empty choices, per OpenAI spec)
  before [DONE] in streaming responses
- Return real usage from collectFullResponse for non-streaming responses
- Add stream_options to ChatCompletionRequest interface
Cursor is subscription-based so per-token cost was hardcoded to 0,
making the sidebar always show $0.00 spent. Map model IDs to their
upstream provider's per-token pricing (Claude, GPT, Gemini, Grok)
so users see approximate market value of their usage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant