Expand description
Provider usage, read as the provider reported it, and the one normalisation every comparison uses.
Providers do not agree on what their fields mean. Anthropic reports three disjoint input
quantities (uncached, written to the cache, read from the cache); OpenAI reports one input
total with the cached part inside it. Both are kept in their native names in the run
record (EconomicsRequest), and totals maps them onto one shape where
total_input = uncached + cache write + cache read holds for both.
What a session said is never kept. read_stream reads a Claude Code stream-json
transcript and keeps numbers and tool names; text, tool inputs and tool results are
dropped as they are read, so a run record cannot leak a prompt, a file or a secret.
use majordomus_cli::economics::usage::read_stream;
let stream = concat!(
r#"{"type":"assistant","message":{"id":"m1","model":"claude-sonnet-5","content":[{"type":"text","text":"secret"}],"usage":{"input_tokens":3,"cache_creation_input_tokens":100,"cache_read_input_tokens":0,"output_tokens":7}}}"#, "\n",
r#"{"type":"assistant","message":{"id":"m1","model":"claude-sonnet-5","content":[{"type":"tool_use","name":"Read","input":{"file_path":"/x"}}],"usage":{"input_tokens":3,"cache_creation_input_tokens":100,"cache_read_input_tokens":0,"output_tokens":7}}}"#, "\n",
r#"{"type":"result","subtype":"success","is_error":false,"num_turns":1,"total_cost_usd":0.25}"#, "\n",
);
let s = read_stream(stream).unwrap();
assert_eq!(s.requests.len(), 1, "one message, two content blocks, one request");
assert_eq!(s.tools.get("Read"), Some(&1));
assert_eq!(s.reported_cost_microusd, Some(250_000));
assert!(!serde_json::to_string(&s.requests).unwrap().contains("secret"));Structs§
- Stream
Facts - What
read_streamrecovers from one session’s transcript: the numeric and structural parts of anEconomicsSession. The caller adds what the transcript does not know (index, prompt digest, wall clock).
Enums§
- Shell
Effect - What a shell command does to the working tree, as far as its text shows. Agents edit
through the shell as often as through edit tools (
cat > f,sed -i, a heredoc script that opens a file for writing), so orientation cannot end only at anEditcall.
Functions§
- add
- The field-wise sum of two totals. Reasoning and cost stay known only when both are: an unknown on either side makes the sum unknown, never a smaller number.
- anthropic_
request - Read an Anthropic
usageobject into a request, native field names kept. - first_
request_ input - The input tokens of a session’s first request: everything the session was given before
the model had done anything (system prompt, instruction files, hook output, the prompt).
The three disjoint input fields are summed; the request’s output is not input and is
left out.
Nonewhen the session made no request. - openai_
request - Read an OpenAI
usageobject into the same shape. OpenAI’sinput_tokensincludes the cached part (input_tokens_details.cached_tokens), where Anthropic’s excludes it; the cached part is moved out so that the three input fields are disjoint for both. OpenAI bills no cache writes, so that field is zero, which is what it is. - read_
stream - Read a Claude Code
--output-format stream-json --verbosetranscript. Lines that are not JSON objects are skipped; a transcript with neither a request nor aresultevent is an error, because a session that reported nothing must be recorded as having reported nothing, not as having used zero tokens. - session_
totals - A session’s totals. The harness’s per-model totals are preferred, because they include
the calls the harness makes on its own account (titles, summaries) that the transcript
does not show as messages; the per-request sum is used only when they are absent.
Nonewhen the session reported neither: absence stays absence. - shell_
effect - Classify one shell command by its text alone; nothing is run. A command that writes a
file is an edit whatever else it does, so a write outranks the read or search it starts
with; otherwise the leading command decides, and a pipe into
grepmakes a search. A redirection to/dev/nullor to another descriptor (2>&1) writes nothing and is not an edit, and neither is a>inside a quoted argument, which is text, not a redirection. - totals
- A run’s totals: the sum of its sessions’.
Nonewhen any session reported nothing, because a partial sum would understate the run.