Skip to content

OpenRouter actual model attribution, error-state cost reliability, and pre-send pricing UI#31

Merged
sidharthmirch merged 6 commits into
mainfrom
claude/openrouter-cost-attribution-and-pricing-ui
Apr 8, 2026
Merged

OpenRouter actual model attribution, error-state cost reliability, and pre-send pricing UI#31
sidharthmirch merged 6 commits into
mainfrom
claude/openrouter-cost-attribution-and-pricing-ui

Conversation

@sidharthmirch

Copy link
Copy Markdown
Owner

Summary

Three integrated features for cost tracking and model attribution:

  • OpenRouter actual model attribution: When OpenRouter auto-routes to a different model, we now persist the actual model ID (actual_model_id) in the messages table, display the real model name in the message header with a "via auto" badge, and return actualModel from the OpenRouter generation API.
  • Error-state cost reliability: Error paths (legacy onError, useStopMessageStreaming) now recompute and persist chat/project cost totals and invalidate relevant queries. Sidebar cost display is hardened with Number() coercion and Number.isFinite() checks.
  • Pre-send model pricing UI: New ModelPricingDisplay component shows per-1M token input/output pricing before sending. Supports single-model display and range display for multi-model compare mode. Respects the showCost user setting.

Files changed

File Feature(s)
src-tauri/src/migrations.rs F1: actual_model_id column
src/core/chorus/ChatState.ts F1: actualModelId field
src/core/chorus/api/CostAPI.ts F1: actualModel return + F3: formatPricePerMillion()
src/core/chorus/api/MessageAPI.ts F1: actual model DB persistence + F2: error-path cost recomputation
src/ui/components/AppSidebar.tsx F2: cost display reliability
src/ui/components/ChatInput.tsx F3: ModelPricingDisplay integration
src/ui/components/MultiChat.tsx F1: actual model UI display
src/ui/components/ModelPricingDisplay.tsx F3: new pricing component

Test plan

  • F1 - Actual model attribution: Send a message using an OpenRouter auto-routed model (e.g., openrouter/auto). Verify the message header shows the actual model name with a "via auto" badge instead of the generic model name.
  • F1 - Data persistence: After the auto-routed message, reload the app. Confirm the actual model attribution persists across restarts.
  • F2 - Error-state costs: Trigger an error during streaming (e.g., disconnect network mid-stream or use an invalid API key). Verify the sidebar cost totals update correctly after the error.
  • F2 - Stop-message costs: Start a streaming message and stop it mid-stream. Verify cost totals in the sidebar reflect any partial usage.
  • F2 - Sidebar cost display: Verify project and chat costs in the sidebar display correctly and don't show NaN or undefined for edge cases.
  • F3 - Single model pricing: With showCost enabled, open a chat with one model selected. Verify the input/output pricing per 1M tokens appears near the send button.
  • F3 - Multi-model pricing: With showCost enabled and multiple models in compare mode, verify a price range is shown.
  • F3 - No pricing data: Select a model without pricing info. Verify no pricing label is shown (graceful fallback).
  • F3 - showCost disabled: With showCost disabled in settings, verify no pricing label appears.
  • Regression - Normal streaming: Send a normal (non-OpenRouter) message. Verify streaming, cost tracking, and message display work as before.
  • Regression - Tools mode: Send a tools message with tool calls. Verify the full tool-call loop completes and costs are tracked.

Made with Cursor

…ty, and pre-send pricing UI

Three features combined in one commit:

1. OpenRouter actual model + cost attribution
   - Add actual_model_id column to messages table (migration 145)
   - Persist actual routed model from OpenRouter generation API
   - Display actual model name with "via auto" badge in ToolsMessageView
   - Full data path: API -> DB -> ChatState -> MultiChat UI

2. Error-state cost reliability
   - Recompute and persist chat/project cost totals on error paths
   - Add cost invalidation in legacy onError and useStopMessageStreaming
   - Harden sidebar cost display with Number() coercion and Number.isFinite checks

3. Pre-send model pricing UI
   - Add formatPricePerMillion() utility to CostAPI
   - New ModelPricingDisplay component showing per-1M token pricing
   - Supports single-model and range display for multi-model compare
   - Integrated into ChatInput, respects showCost setting

Made-with: Cursor
@sidharthmirch sidharthmirch added the by-claude Created by Claude label Apr 8, 2026
Copilot AI review requested due to automatic review settings April 8, 2026 20:00
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@sidharthmirch sidharthmirch added bug Something isn't working and removed by-claude Created by Claude labels Apr 8, 2026
Apply Prettier formatting fixes for ModelPricingDisplay and ToolsMessageView so lint/style checks pass.

Made-with: Cursor

Copilot AI 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.

Pull request overview

This PR improves cost tracking accuracy and model attribution (especially for OpenRouter auto-routing) and adds a pre-send UI affordance to show model pricing.

Changes:

  • Persist and display OpenRouter “actual model” attribution via actual_model_id / actualModelId, including a “via auto” indicator in message headers.
  • Harden cost reliability in error/stop streaming paths by recomputing chat/project totals and invalidating relevant queries; sidebar rendering now guards against NaN/non-numeric values.
  • Add a ModelPricingDisplay UI component to show input/output pricing per 1M tokens (single model or range) before sending, gated by showCost.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src-tauri/src/migrations.rs Adds messages.actual_model_id column migration.
src/core/chorus/ChatState.ts Extends Message with optional actualModelId.
src/core/chorus/api/CostAPI.ts Returns actualModel from OpenRouter cost fetch; adds per-1M pricing formatter.
src/core/chorus/api/MessageAPI.ts Persists actual_model_id during streaming; recomputes costs + invalidates queries on error/stop.
src/ui/components/AppSidebar.tsx Makes sidebar cost display robust via numeric coercion and finite checks.
src/ui/components/ChatInput.tsx Integrates pricing display into the send-row UI.
src/ui/components/MultiChat.tsx Displays the “actual model” name and “via auto” badge in the message header.
src/ui/components/ModelPricingDisplay.tsx New component rendering per-1M token pricing label (single or range).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2674 to +2681
Migration {
version: 145,
description: "add actual_model_id to messages",
kind: MigrationKind::Up,
sql: r#"
ALTER TABLE messages ADD COLUMN actual_model_id TEXT DEFAULT NULL;
"#,
},

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

The migrations vector is not in ascending version order (e.g., version 144 appears before 143). Since tauri_plugin_sql::Builder::add_migrations(DB_URL, migrations) receives this list as-is (src-tauri/src/lib.rs:136-138), an out-of-order list can result in migrations being applied in the wrong sequence or skipped depending on the plugin's migration runner. Please reorder the migrations so they are strictly increasing and place this new version 145 migration after version 144.

Copilot uses AI. Check for mistakes.

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.

WARNING!!! PR #30 introduces a new migration as well. Fix this based on merge order

@sidharthmirch

Copy link
Copy Markdown
Owner Author

WARNING CHECK BEFORE MERGING #29 #30 #31 DUE TO CONFLICTING MIGRATION #'s

sidharthmirch and others added 3 commits April 8, 2026 13:24
Drop the chat-input model pricing display to keep the compose area uncluttered while preserving existing post-send cost behavior.

Made-with: Cursor
The OpenRouter generation endpoint (/api/v1/generation) was returning
negative total_cost values, and the actual model wasn't being captured.

OpenRouter includes both usage.cost and the model field directly in the
streaming response. This commit:

- Captures chunk.model from the streaming response in ProviderOpenRouter
- Reads usage.cost (OpenRouter extension) from the final chunk
- Adds model and cost fields to the UsageData type
- Prefers response-level cost/model in MessageAPI, falling back to the
  generation endpoint only when the response doesn't include them
- Guards against negative cost values

Made-with: Cursor
@sidharthmirch sidharthmirch added the enhancement New feature or request label Apr 8, 2026
@sidharthmirch
sidharthmirch merged commit 3c29332 into main Apr 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants