Skip to content

fix: decode OpenRouter usage cost fields in ChatResponse.Usage - #50

Merged
ProJedi1234 merged 3 commits into
mainfrom
cursor/decode-usage-cost-fields-e112
Jun 14, 2026
Merged

ProJedi1234 merged 3 commits into
mainfrom
cursor/decode-usage-cost-fields-e112

Conversation

@ProJedi1234

@ProJedi1234 ProJedi1234 commented Jun 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes #49 by extending ChatResponse.Usage to decode inline cost, cost_details, and prompt_tokens_details fields that OpenRouter already returns on chat completion responses. Previously these fields were silently dropped during JSON decoding (same class of bug as openrouter-python-sdk#65).

This is the upstream prerequisite for accurate per-call cost capture in maia-server observability.

Changes

  • ChatResponse.Usage — added optional cost, cost_details, and prompt_tokens_details with nested CostDetails and PromptTokensDetails types
  • StreamingDelta.Usage — mirrors the same fields using shared nested types from ChatResponse.Usage (passive decoding only; no streaming behavior changes)
  • UsageCostTests — new unit tests with a representative OpenRouter response fixture proving cost fields decode correctly, plus backward-compat test for responses without cost fields

Acceptance criteria

  • ChatResponse.Usage decodes cost (nullable)
  • ChatResponse.Usage decodes cost_details when present
  • ChatResponse.Usage decodes prompt_tokens_details (cached/audio tokens) when present
  • Existing chat response decoding tests still pass (180 tests)
  • New unit test proves cost is decoded from a representative OpenRouter chat response fixture
  • New release cut and tagged (maintainer action post-merge)

Out of scope (per issue)

  • maia-server integration
  • Streaming response usage tests (passive decoding added on StreamingDelta.Usage)
  • is_byok, server_tool_use, and extended completion_tokens_details fields (audio/image tokens)

Test plan

  • swift test --parallel — all 180 tests pass
  • swiftlint on changed files — 0 violations
Open in Web Open in Cursor 

Summary by CodeRabbit

Release Notes

  • New Features
    • Enhanced API responses with detailed cost and token usage metrics
    • Added token breakdown including audio, image, and cached token tracking
    • Included cost breakdowns for upstream inference and completions

Extend ChatResponse.Usage to decode cost, cost_details, and
prompt_tokens_details that OpenRouter returns on chat completions.
These fields were previously silently dropped during JSON decoding.

- Add optional cost (Double?) for credits charged per request
- Add CostDetails nested type for upstream inference cost breakdown
- Add PromptTokensDetails for cached/audio/video prompt token counts
- Mirror new fields on StreamingDelta.Usage via shared nested types
- Add UsageCostTests with representative OpenRouter response fixture

Fixes #49

Co-authored-by: Aditya Dhar <[email protected]>
@coderabbitai

coderabbitai Bot commented Jun 13, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

ChatResponse.Usage gains optional cost, cost_details, and prompt_tokens_details fields, and OutputTokensDetails is extended with audio_tokens and image_tokens. Two new nested structs—PromptTokensDetails and CostDetails—are added. StreamingDelta.Usage removes its local OutputTokensDetails duplicate, reuses the ChatResponse.Usage types, and gains the same new fields. A new test file adds unit and integration coverage for cost decoding.

Changes

Usage Cost & Token Breakdown

Layer / File(s) Summary
ChatResponse.Usage model expansion
Sources/OpenRouterKit/Models/Chat/ChatResponse.swift
Usage gains optional cost, prompt_tokens_details, and cost_details fields. OutputTokensDetails is expanded with audio_tokens and image_tokens. New PromptTokensDetails (cached/audio/video tokens) and CostDetails (upstream inference cost breakdown) nested structs are added.
StreamingDelta.Usage consolidation and expansion
Sources/OpenRouterKit/Models/Chat/StreamingDelta.swift
Local OutputTokensDetails struct is removed; completion_tokens_details is repointed to ChatResponse.Usage.OutputTokensDetails. Optional cost, cost_details, and prompt_tokens_details are added using the shared ChatResponse.Usage types.
Unit and integration tests for cost decoding
Tests/OpenRouterKitTests/UsageCostTests.swift
Adds UsageCostTests with a full-payload decode test asserting all new numeric fields, a missing-fields test asserting nil optionals while base token counts remain correct, and a OPENROUTER_API_KEY-gated integration test that sends a live completion and asserts usage.cost is present and non-negative.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • ProJedi1234/OpenRouterKit#33: Modifies SSEParser and streaming delta usage decoding, which is directly affected by the StreamingDelta.Usage type changes and the removal of the local OutputTokensDetails struct in this PR.

Poem

🐇 Hippity-hop, I checked the bill,
The cost field now decoded with skill!
Prompt tokens cached, completions done right,
CostDetails nested, oh what a sight.
No more dropped coins in the JSON night! 🪙

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: decoding OpenRouter usage cost fields in ChatResponse.Usage, which aligns perfectly with the PR's primary objective.
Linked Issues check ✅ Passed All coding-related acceptance criteria from issue #49 are met: cost decoding [#49], cost_details decoding [#49], prompt_tokens_details decoding [#49], existing tests pass [#49], and new unit tests added [#49].
Out of Scope Changes check ✅ Passed All changes directly support issue #49's objectives. Extended completion_tokens_details (audio/image tokens) and StreamingDelta mirroring are reasonable supporting enhancements consistent with stated scope.

✏️ 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 cursor/decode-usage-cost-fields-e112

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.

@github-actions github-actions Bot added the bug Something isn't working label Jun 13, 2026
cursoragent and others added 2 commits June 13, 2026 20:01
Extend ChatResponse.Usage.OutputTokensDetails with optional
audio_tokens and image_tokens fields returned by OpenRouter.
StreamingDelta.Usage reuses the shared OutputTokensDetails type.

Co-authored-by: Aditya Dhar <[email protected]>
Assert that a simple non-streaming text chat completion returns
usage with a non-nil cost field from the OpenRouter API.

Co-authored-by: Aditya Dhar <[email protected]>
@ProJedi1234
ProJedi1234 marked this pull request as ready for review June 14, 2026 14:16
@ProJedi1234

Copy link
Copy Markdown
Owner Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jun 14, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
Tests/OpenRouterKitTests/UsageCostTests.swift (1)

102-108: ⚡ Quick win

Assert missing completion_tokens_details in the backward-compat test.

decodesWithoutCostFields validates other new optionals as nil, but it skips completion_tokens_details. Adding this assertion tightens compatibility coverage for the expanded Usage shape.

Proposed patch
         `#expect`(usage.cost == nil)
         `#expect`(usage.cost_details == nil)
         `#expect`(usage.prompt_tokens_details == nil)
+        `#expect`(usage.completion_tokens_details == nil)
🤖 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 `@Tests/OpenRouterKitTests/UsageCostTests.swift` around lines 102 - 108, The
decodesWithoutCostFields test is missing an assertion for the
completion_tokens_details field. Add a `#expect`(usage.completion_tokens_details
== nil) assertion alongside the existing nil checks for cost, cost_details, and
prompt_tokens_details to ensure complete backward compatibility coverage for the
expanded Usage shape.
🤖 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.

Nitpick comments:
In `@Tests/OpenRouterKitTests/UsageCostTests.swift`:
- Around line 102-108: The decodesWithoutCostFields test is missing an assertion
for the completion_tokens_details field. Add a
`#expect`(usage.completion_tokens_details == nil) assertion alongside the existing
nil checks for cost, cost_details, and prompt_tokens_details to ensure complete
backward compatibility coverage for the expanded Usage shape.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0ee76bfa-cf6e-497c-b68f-dc18ab0bbbb0

📥 Commits

Reviewing files that changed from the base of the PR and between 52680a6 and 852b829.

📒 Files selected for processing (3)
  • Sources/OpenRouterKit/Models/Chat/ChatResponse.swift
  • Sources/OpenRouterKit/Models/Chat/StreamingDelta.swift
  • Tests/OpenRouterKitTests/UsageCostTests.swift

@ProJedi1234 ProJedi1234 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Looks good

@ProJedi1234
ProJedi1234 merged commit d7fb35d into main Jun 14, 2026
7 checks passed
@ProJedi1234
ProJedi1234 deleted the cursor/decode-usage-cost-fields-e112 branch June 14, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: decode OpenRouter usage cost fields

2 participants