ALLM.Usage (allm v0.6.1)

Copy Markdown View Source

Token and cost usage for a single response — Layer A serializable data.

Every numeric field is optional and nil-able because not every provider returns every counter. Costs are populated either by an adapter that knows its own pricing or by an optional model-catalog integration.

nil means the provider did not report a counter; 0 means it reported zero. The bundled chat adapters never replace a counter missing from the provider's response with 0 (pinned for every cache counter by the "no cache fields on the wire → nil, never 0" test group in the repository's cache_usage_family_test.exs).

Cached prompt tokens

:input_tokens is defined as the total prompt size: it includes the tokens served from the provider's prompt cache (:cached_input_tokens) and the tokens written to it on this call (:cache_write_input_tokens). So when all three are integers, cached_input_tokens + cache_write_input_tokens <= input_tokens, and cached_input_tokens / input_tokens is the cache hit ratio whichever provider served the call. The bundled OpenAI (both endpoints), Anthropic and Gemini adapters report these fields this way on streaming and non-streaming calls alike (pinned per adapter by the "cached + write <= input holds on both paths" tests in the repository's cache_usage_family_test.exs).

Costs do not distinguish cached tokens. ALLM.Capability.populate_costs/2 prices every :input_tokens token at the model's plain input rate, including cached reads and cache writes, so :input_cost for a call with cache activity differs from what the provider bills. Cache-aware pricing is not implemented.

Fields

FieldTypeNotes
:input_tokensnon_neg_integer | nilTotal prompt tokens, including cached reads and cache writes.
:output_tokensnon_neg_integer | nil
:cached_input_tokensnon_neg_integer | nilPrompt tokens served from the provider's prompt cache.
:cache_write_input_tokensnon_neg_integer | nilPrompt tokens written to the provider's prompt cache on this call.
:reasoning_tokensnon_neg_integer | nilReasoning-model thinking tokens.
:total_tokensnon_neg_integer | nil
:input_costfloat | nilUSD; populated when the adapter knows pricing.
:output_costfloat | nil
:total_costfloat | nil
:tool_usagemapPer-tool tally (caller-derived).
:extramapProvider-specific spillover.

Summary

Functions

Build a %Usage{} from keyword opts.

Return the :total_tokens field when set, otherwise the sum of :input_tokens + :output_tokens when both are integers, otherwise nil.

Types

cost()

@type cost() :: float()

t()

@type t() :: %ALLM.Usage{
  cache_write_input_tokens: non_neg_integer() | nil,
  cached_input_tokens: non_neg_integer() | nil,
  extra: map(),
  input_cost: cost() | nil,
  input_tokens: non_neg_integer() | nil,
  output_cost: cost() | nil,
  output_tokens: non_neg_integer() | nil,
  reasoning_tokens: non_neg_integer() | nil,
  tool_usage: map(),
  total_cost: cost() | nil,
  total_tokens: non_neg_integer() | nil
}

Functions

new(opts)

@spec new(keyword()) :: t()

Build a %Usage{} from keyword opts.

Every field is optional. Unknown keys raise ArgumentError via struct!/2.

Examples

iex> u = ALLM.Usage.new(input_tokens: 10, output_tokens: 20)
iex> u.input_tokens
10
iex> u.tool_usage
%{}

total_tokens(usage)

@spec total_tokens(t()) :: non_neg_integer() | nil

Return the :total_tokens field when set, otherwise the sum of :input_tokens + :output_tokens when both are integers, otherwise nil.

Examples

iex> ALLM.Usage.total_tokens(ALLM.Usage.new(total_tokens: 42))
42

iex> ALLM.Usage.total_tokens(ALLM.Usage.new(input_tokens: 10, output_tokens: 20))
30

iex> ALLM.Usage.total_tokens(ALLM.Usage.new([]))
nil