♻️ refactor(message): prefer dedicated usage column over metadata.usage#15457
Conversation
Token usage was promoted out of metadata.usage into a dedicated messages.usage column, but nothing populated it and all reads still went through metadata.usage. - Centralize write-side promotion in the DB model (update / updateMetadata / create), so all executor callers populate the usage column from a top-level usage payload, falling back to metadata.usage. metadata.usage stays dual-written for backward-compatible reads. - Reads prefer the usage column and fall back to metadata.usage: message queries, getTokenHeatmaps, recomputeTopicUsage, the usage record service, and context token accounting. - Add top-level usage to UpdateMessageParams + DBMessageItem types. - Mark metadata.usage and the legacy flat token fields as @deprecated, pointing to the top-level usage field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: edb9f7ddbf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .set({ | ||
| ...message, | ||
| ...(mergedMetadata && { metadata: mergedMetadata }), | ||
| ...(usageToWrite && { usage: usageToWrite }), |
There was a problem hiding this comment.
Preserve metadata.usage for top-level usage updates
When callers use the newly added top-level usage parameter without also sending metadata.usage, this update writes only messages.usage; metadata.usage is left absent or stale because mergedMetadata is only built from the metadata payload. During the stated dual-write transition, older readers/rollback paths that still consume metadata.usage will miss the recorded usage for those messages, so the update path should also merge usageToWrite into metadata when it is present.
Useful? React with 👍 / 👎.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## canary #15457 +/- ##
=========================================
Coverage 70.84% 70.85%
=========================================
Files 3253 3253
Lines 320623 320645 +22
Branches 34937 28350 -6587
=========================================
+ Hits 227160 227177 +17
- Misses 93282 93287 +5
Partials 181 181
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
When a caller passed the new top-level `usage` param without also sending `metadata.usage`, the update wrote only `messages.usage` and left `metadata.usage` stale/absent — legacy readers and rollback paths still consume it during the dual-write transition. Fold the resolved usage into the metadata patch so `metadata.usage` stays in sync regardless of how usage was passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
💻 Change Type
🔗 Related Issue
🔀 Description of Change
Token usage was previously promoted out of
metadata.usageinto a dedicatedmessages.usagejsonb column — but nothing populated that column, and every read still went throughmetadata.usage. This PR wires up the dual-write / preferred-read so the column becomes the source of truth while staying backward-compatible.Write side — centralized in the DB model so all executor callers (Gateway, hetero-agent, client) are covered without touching them:
update()/updateMetadata()/buildMessageInsertValue()promote token usage into theusagecolumn, preferring a top-levelusagepayload and falling back tometadata.usage.metadata.usagestays dual-written during the transition for backward-compatible reads.Read side — prefer the
usagecolumn, fall back tometadata.usagefor legacy rows:queryByIds/queryWithWhereselect the column and expose it asUIChatMessage.usage.getTokenHeatmapsandrecomputeTopicUsageuseCOALESCE(usage, metadata->'usage')in SQL.UsageRecordService.findByDateRangeand context-enginetokenAccountingprefer the top-level usage.Types:
usagetoUpdateMessageParamsandDBMessageItem.metadata.usageand the legacy flat token fields (totalTokens,cost,inputXxxTokens, …) as@deprecated, pointing to the top-levelusagefield. Performance fields are untouched.Note:
metadata.usageis intentionally still written and still listed in the zod schema, so it's not yet removable — a follow-up can do the full cut-over (stop writingmetadata.usage, drop the read fallbacks, backfill legacy rows).🧪 How to Test
Added/extended tests asserting the column wins over
metadata.usage(and legacy rows fall back) across: message create / query / update / stats(heatmap), topic usage rollup, usage record service, and token accounting. Full repotype-checkpasses; all touched suites green.📸 Screenshots / Videos
No UI changes.
📝 Additional Information
Backward-compatible and additive — no migration required (the
usagecolumn already exists on trunk). Dual-write means rollback-safe.