pub struct UsageRecord {
pub turn: usize,
pub model: String,
pub prompt_tokens: u64,
pub completion_tokens: u64,
pub total_tokens: u64,
pub cached_tokens: Option<u64>,
pub timestamp_ms: i64,
pub cost_usd: Option<f64>,
pub served_model: Option<String>,
}Expand description
One model round-trip’s token accounting, with the context a bare
Usage lacks: which turn it was and which model served it (D9 “model
provenance” — obligation 6’s “served-model provenance” row). Deliberately
flat/typed (not a formatted string) so it survives a save/load round trip
byte-for-byte in the fields that matter, and so a future reader (a
doctor/inspect stats command, a cost dashboard) can aggregate it
without re-parsing text.
Fields§
§turn: usize0-based index of the model round-trip this record covers (one per
crate::AgentEvent::TurnCompleted, the same cadence UX-23
already uses).
model: StringThe model id that served this turn (Config.model, or the
mid-session-switched model once obligation 10 lands — recorded
per-turn rather than once per session so a handoff is visible in the
log, not just implied).
prompt_tokens: u64Input tokens.
completion_tokens: u64Output tokens.
total_tokens: u64Total tokens (provider-reported; not always prompt + completion
exactly, so kept as its own field rather than derived).
cached_tokens: Option<u64>Prompt tokens served from the provider’s cache (B7), if reported.
timestamp_ms: i64Unix-ms wall-clock time the record was created.
cost_usd: Option<f64>BP-7 (catalog §4a “Per-turn cost/usage accounting” — the COST half
the row’s semantics name alongside tokens): this round-trip’s dollar
cost at the model’s resolved crate::pricing::ModelPrice.
None when this build cannot price the model — never a guess, and
never zero standing in for “unknown”.
served_model: Option<String>BP-13 (D9 “Model-served-vs-requested provenance”): the model the
PROVIDER reported as having produced the response, when it reported
one at all. Self::model above is what was REQUESTED; these two
can genuinely differ (a gateway resolving a floating name to a dated
snapshot, a routed tier, a fallback hop), and a record that carries
only the request can never show it. None means the provider said
nothing — never “they matched”.
Implementations§
Source§impl UsageRecord
impl UsageRecord
Sourcepub fn from_usage(
turn: usize,
model: &str,
usage: &Usage,
timestamp_ms: i64,
) -> UsageRecord
pub fn from_usage( turn: usize, model: &str, usage: &Usage, timestamp_ms: i64, ) -> UsageRecord
Build a record from a provider Usage plus the per-turn context a
bare Usage doesn’t carry.
Sourcepub fn priced(self, price: Option<ModelPrice>) -> UsageRecord
pub fn priced(self, price: Option<ModelPrice>) -> UsageRecord
BP-7: the same record with cost_usd filled in from price, or
unchanged when the model has no resolvable price.
Sourcepub fn with_served_model(self, served: Option<String>) -> UsageRecord
pub fn with_served_model(self, served: Option<String>) -> UsageRecord
BP-13: attach the provider-reported serving model. None leaves the
record saying nothing about it, which is the honest reading when the
response carried no model field.