Skip to content

fix(opencode): Support incremental step usage, reasoning tokens, and model pricing - #91

Merged
4ndreello merged 1 commit into
mainfrom
fix/opencode-usage-pricing-incremental
Sep 20, 2026
Merged

4ndreello merged 1 commit into
mainfrom
fix/opencode-usage-pricing-incremental

Conversation

@4ndreello

Copy link
Copy Markdown
Owner

Summary

This PR addresses the issue where OpenCode sessions reported Tokens: 0 and unpriced $0.00? costs in CodeDeck metrics, and fixes associated pricing and parser regressions across the runtime.

Root Causes

  1. Parser dropped step_finish: src/drivers/opencode/parser.ts explicitly discarded step_finish events where OpenCode reports step tokens and costs.
  2. Harness dummy cost: OpenCode reports cost: 0 for models missing from its local models.json (such as alibaba-token-plan), which suppressed CodeDeck's fallback pricing.
  3. Overwrite vs Accumulation: OpenCode emits tokens per-step (incremental deltas), whereas the daemon previously assumed cumulative session totals and overwrote tokens on each update.
  4. Model ID prefix mismatch: Model identifiers with provider prefixes (e.g., alibaba-token-plan/qwen3.8-max or opencode/claude-sonnet-4-6) failed static lookup in MODEL_PRICES.
  5. Claude cached token drop: src/drivers/claude/parser.ts used ?? between read and creation tokens, dropping cache creation tokens when read tokens were present.

Key Changes

  • Core Events: Added optional incremental?: boolean to UsageUpdatedEvent.
  • OpenCode Parser:
    • Captures step_finish events.
    • Folds tokens.reasoning into outputTokens (standard provider billing for o-series/Qwen/DeepSeek).
    • Maps tokens.cache.read to cachedTokens.
    • Converts cost: 0 to undefined to allow static table fallback while preserving positive reported costs.
    • Emits incremental: true.
  • Daemon (updateSessionFromEvent):
    • For incremental: true, atomically adds inputTokens, outputTokens, cachedTokens, and cost to existing session usage in SQLite.
    • Non-incremental events continue to replace usage (preserving compatibility with Codex, Antigravity, and Claude).
    • Updates Session.model if reported in the usage event.
    • Deduplication via sourceKey in session-runtime.ts prevents double-counting during daemon restarts and process reattach.
  • Pricing & Models:
    • Added resolveModelPrice() to normalize model IDs by stripping provider prefixes, display labels, and providing case-insensitive resolution.
    • Added Alibaba Qwen models (qwen3.8-max, qwen3.8-flash, qwen-plus, qwen-turbo) to MODEL_PRICES.
    • Added explicit cached rates for Claude models (0.1x of input rate).
  • Claude Parser: Sums cache_read_input_tokens + cache_creation_input_tokens.
  • Backfill Script: Added scripts/backfill-opencode-usage.ts to retroactively recover tokens and costs from historical .ndjson session logs.

Testing & Verification

  • 83 test files passed, 1,130 tests total (0 failures, 0 warnings).
  • Added test coverage for:
    • step_finish parsing, reasoning folding, incremental flag, and root/part fallbacks in tests/opencode-parser.test.ts.
    • Model normalization, provider prefixes, display labels, Qwen pricing, and negative tokens in tests/pricing.test.ts.
    • Incremental usage accumulation across steps and pricing in tests/usage-daemon.test.ts.
    • Cache token summation in tests/events.test.ts.
  • TypeScript build (npm run build) completed with 0 errors.

…model pricing

- Add incremental flag to UsageUpdatedEvent for step-level usage deltas
- Intercept step_finish in OpenCode parser, folding reasoning tokens into output
- Discard dummy cost: 0 to allow CodeDeck pricing table fallback
- Atomically accumulate incremental usage in daemon and persist to SQLite
- Normalize model IDs in pricing.ts (strip provider prefixes/display labels)
- Add pricing entries for Alibaba Qwen models and Claude cached rates
- Fix Claude parser cache creation token drop
- Add backfill script for recovering historical session metrics
- Add comprehensive test coverage across parser, pricing, events, and daemon
@sonarqubecloud

Copy link
Copy Markdown

@4ndreello
4ndreello merged commit 10a5b91 into main Sep 20, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant