pub struct SessionStats {
pub turns: u64,
pub input_tokens: u64,
pub output_tokens: u64,
pub cache_read_tokens: u64,
pub cache_write_tokens: u64,
pub cache_turns: u64,
pub tools: Vec<ToolStat>,
pub tool_errors: u64,
pub last_input_tokens: Option<u64>,
pub cost: Option<Cost>,
}Expand description
Aggregate view of a conversation for /usage: how many turns reported
usage, the summed token counts, and which tools ran.
Pure data — no timing, and no cost OF ITS OWN: cost is filled by
the caller that knows the provider (pricing::cost_of, called from
the actor’s Request::Usage reply), because pricing keys on a
base_url/model pair that an AgentMessage cannot carry. Every
other field folds from the transcript alone.
Derive order is fixed so tests can compare by value.
Fields§
§turns: u64Assistant messages that carried Some(usage).
input_tokens: u64§output_tokens: u64§cache_read_tokens: u64Cache counts absent on a call are treated as 0.
cache_write_tokens: u64§cache_turns: u64Assistant messages that reported AT LEAST ONE cache field — the honest
denominator for cache_hit_pct(). A provider that reports nothing must
OMIT the rate rather than print 0% (streamfn.rs:1255
cache_read_tokens: (usage.cached_input_tokens > 0).then_some(...):
zero is rig’s sentinel for “not reported”).
tools: Vec<ToolStat>One entry per distinct tool name, sorted by calls descending, ties
by name ascending. Empty when no tool has been called. (WAS
Vec<(String, u64)> — same sort, more facts.)
tool_errors: u64Σ tools[..].errors — the headline count summary() prints.
last_input_tokens: Option<u64>Provider-reported input tokens of the most recent Assistant message with
Some(usage). None until a turn reports usage. NOT summed by
aggregate() (see below): it is a “how full was the last request” fact.
cost: Option<Cost>The session’s $ cost. None from the pure fold (the fold has no
provider); Some once the actor prices it. skip_serializing_if keeps
it off the wire when absent — mandatory for an additive variant.
Implementations§
Source§impl SessionStats
impl SessionStats
Sourcepub fn summary(&self) -> String
pub fn summary(&self) -> String
The one-line summary both front-ends print: plain text — no colors, no layout, no context; front-ends add chrome.
Contract:
turns == 0⇒"(no usage reported)".- otherwise ⇒
"usage: N turns, X in, Y out[, C cache read][, W cache write][, tools: …]"where the cache fields are omitted when 0, thetools:segment is omitted when no tool ran, and the tool names are capped atTOOL_LIST_CAP, the remainder folded into"+K more". - appends
, N% cachewhencache_hit_pct()isSome,, N tool error(s)whentool_errors > 0, and, ~$d.ddddwhencostisSome—(N turns unpriced)whenCost::unpriced_turns > 0.% cachesits right after the cache tokens,tool error(s)aftertools:, and~$last. - The trailing
context U/Lreadout is intentionally NOT here: its limit comes from the front-end (REPLmodel_limit, TUIstatus.context_limit), so each wraps/appends it.
Sourcepub fn cache_hit_pct(&self) -> Option<u8>
pub fn cache_hit_pct(&self) -> Option<u8>
Cache hit rate as an integer percent, rounded to the nearest percent.
Contract:
Some((cache_read * 100 + input / 2) / input)wheninput_tokens > 0 && cache_turns > 0, elseNone: a session that reported no cache OMITS the clause instead of claiming0%.- the denominator is
input_tokensALONE. On the chat-completions wire a cached read is a SUBSET ofinput_tokens(rig mapsprompt_tokens_details.cached_tokens), so addingcache_readback would double-count. A provider on the Anthropic wire — whereinput_tokensEXCLUDES cache reads/writes — would understate here; wcode speaks the OpenAI-compatible wire only, and that is the documented assumption (D028). - saturates at 100: more cached tokens than input is a provider bug, and
120%on screen would be worse than the clamp. cache_writeis not a hit: it is reported separately and priced.
Sourcepub fn tool_error_pct(&self) -> Option<u8>
pub fn tool_error_pct(&self) -> Option<u8>
Share of tool calls that did not succeed, rounded to the nearest percent.
Some((tool_errors * 100 + calls / 2) / calls) over Σcalls when a tool
ran at all, else None. errors counts hook-blocked and synthesized
aborts (see ToolStat), so this is “not a success”, never “the tool is
buggy”; saturates at 100 for the same reason as cache_hit_pct.
Trait Implementations§
Source§impl Clone for SessionStats
impl Clone for SessionStats
Source§impl Debug for SessionStats
impl Debug for SessionStats
Source§impl Default for SessionStats
impl Default for SessionStats
Source§impl<'de> Deserialize<'de> for SessionStats
impl<'de> Deserialize<'de> for SessionStats
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for SessionStats
Source§impl PartialEq for SessionStats
impl PartialEq for SessionStats
Source§impl Serialize for SessionStats
impl Serialize for SessionStats
impl StructuralPartialEq for SessionStats
Auto Trait Implementations§
impl Freeze for SessionStats
impl RefUnwindSafe for SessionStats
impl Send for SessionStats
impl Sync for SessionStats
impl Unpin for SessionStats
impl UnsafeUnpin for SessionStats
impl UnwindSafe for SessionStats
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.