feat: record and show the provider-reported cost of assistant messages #341
No reviewers
Labels
No labels
Epic 5
MCP
P1
PR-0
PR-1
api
api-layer
architecture
area:backend
area:frontend
arrow
authentication
backend
bug
chat
client
clipboard
common-module
compose
cross-cutting
cross-cutting-requirement
data-layer
data-models
database
desktop-client
documentation
duplicate
enhancement
epic
epic-1
epic-2
epic-3
epic-4
epic-5
epic-6
epic-7
epic-llm-config
error-handling
feature
frontend
good first issue
help wanted
implementation
infrastructure
installer
invalid
kotlin
ktor
llm
medium-priority
message
multiplatform
needs-review
needs-triage
packaging
performance
phase-1
phase-2
phase-3
phase-4
phase-5
phase3
phase:3
priority: high
question
refactor
refactoring
resource-sharing
security
server
server-module
services
session
session-management
settings
setup
shutdown
state
storage
streaming
sub-issue
tests
tooling
type:task
ui
ui/ux
user-accounts
user-management
ux
viewmodel
websocket
windows
windows-integration
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Torvian.eu/chatbot!341
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/assistant-message-cost-stats"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Records the charge a provider reports for a generation on the assistant message that produced it, sums it per conversation branch, and shows it to the user as a per-message amount in the message usage dialog and as a branch total in the thread stats dialog. Until now the token counters were displayed while the amount actually billed was discarded.
Changes
costtoUsageStatsand a nullablecosttotal toThreadStats, both defaulted tonullso older payloads keep decoding and a cost-free value stays absent on the wire.usage.costin the Chat Completions and Responses API dialects, streaming and non-streaming, through one internal reader that treats an unusable, negative or implausibly large value as "no cost" instead of failing the generation.Implementation notes
Doublenamedcost, not theLongnano-USD amount the architecture report planned; that report's D1 section is obsolete and the report is not part of this change. Amounts are stored exactly as the provider stated them, with no rounding, rescaling or unit conversion, because only the provider knows what its own figure means.usage_statsJSON, and it shares the fate of theUsageStatsobject it rides on (a truncated or abnormal ending still records nothing).nullmeans "the provider reported no charge" and0means "the provider reported a free request"; both the model and the UI keep them distinct, matching the existing token discipline.Acceptance criteria coverage
usage_statscolumn.MessageUsageDialog, formatted as a dollar amount, and a reported zero is shown as a zero amount rather than as "not available".ThreadStatsDialog, and a branch where no message reported a charge shows the not-available label.Validation
./gradlew :common:desktopTest./gradlew :server:test./gradlew :app:desktopTest./gradlew :server:classes,./gradlew :app:desktopMainClasses,./gradlew :app:compileKotlinWasmJs,./gradlew :worker:testClassesReported by the implementer:
:common:desktopTest203 tests / 0 failures,:server:test2342 tests / 0 failures / 1 skipped,:app:desktopTest1032 tests / 0 failures / 2 skipped. The validation runs on the final working tree; individual commits are not claimed to build or pass tests in isolation. The remediation of the review findings was not independently re-verified after it was applied.Risks and follow-up
prompt_tokens_detailsandcompletion_tokens_details, and the Spanish translation of thethread_stats_*strings, which is missing for the whole section and predates this change.