v2-refactor-temp/docs/breaking-changes/2026-06-11-ai-usage-records.md
A new ai_usage_record fact table stores one immutable, best-effort record per
observable successful provider invocation, including token/image usage, cost,
per-call performance, provider/model/source, and serving-credential snapshots.
Historical v1 assistant messages are represented by explicit
legacy-aggregate records with an estimated logical request count.
MessageStats usage, cost, and measured provider performance are now a
materialized aggregate of those records. Message persistence continues to own
content, status, and end-to-end runtime timing, but no longer creates, updates,
or repairs usage records.
These records are immutable analytics facts, not a financially reconcilable billing ledger. Provider invoices remain authoritative.
Usage analytics no longer disappear when a conversation, provider, assistant, or API key is deleted. New provider requests appear on an already-open Usage page through DataApi change notifications.
Costs stay separated by currency. Cost sorting and cost-ranked rollups require an explicit currency; the UI does not compare, convert, or sum CNY and USD.
Credential attribution shows its confidence:
explicit: the provider service selected this configured key;matched: a caller override matched a configured key;auth: provider-level authentication, with its OAuth/IAM/external-CLI
mechanism retained;unknown: no trustworthy serving credential identity is available.doStream /
doGenerate; embedding and image middleware report every actual SDK batch;
successful rerank calls are recorded even when usage and cost are unavailable.unknown; persistence and
migration never infer provider, model, source, key, or pricing from current
state.generateText invocation.AiUsageRecordMigrator. Source identity comes only from the message snapshot,
request count is estimated from raw blocks, per-call metrics remain null, and
historical API-key attribution is always unknown.explicit selection and matched overrides separate,
even when they refer to the same configured key.MessageStats.runtimeTiming; public message create
DTOs cannot write stats. Usage/cost/provider performance are replaced only
by the usage-record projector, while runtime persistence can write only the
runtime timeline.runtimeTiming is absent
and is never converted into a synthetic persisted timeline. Model TPS is
weighted across only provider steps with measurable output/duration;
end-to-end throughput includes tool and approval wall time. Direct Agent
tool duration comes from SDK PostToolUse/PostToolUseFailure, not chunk
timing./ai-usage-records list with paired messageKind/messageId filters to
draw the duration distribution. They do not render an unbounded per-step
detail list.runtimeTiming; one renderer view model adapts their
legacy scalar timings and preserves the existing display.$ (USD) or ¥/¥
(CNY) is dropped during migration rather than assigned an unreliable
currency.Nothing. Historical migrated usage and supported new requests appear automatically in Settings > Usage.
This accompanies the message-stats cost/cache work. ai_usage_record is the
only usage/cost/provider-performance fact source; MessageStats is its
materialized per-message projection plus separately owned runtime timing.
Records are insert-only and the renderer has read-only access. Aggregate
requests are limited to 366 days and server-ranked top-N groups with an
explicit Other remainder.