AgentSessionManager can estimate and persist per-run USD cost from token usage. The cost pipeline is model-aware and supports provider defaults, model prefixes, and cache-token pricing for Claude.
Overview
Cost tracking has four parts:
AgentSessionManager.Cost.CostCalculatorcomputes USD from token counts.Policy.Runtimeuses model-aware rates for{:max_cost_usd, ...}enforcement.SessionManagerstoresrun.cost_usdat successful finalization.- Query and rendering surfaces expose cost in summaries/output.
Configuration
Set a pricing table globally:
config :agent_session_manager,
pricing_table: %{
"claude" => %{
default: %{input: 0.000003, output: 0.000015},
models: %{
"claude-sonnet-4-5" => %{
input: 0.000003,
output: 0.000015,
cache_read: 0.0000003,
cache_creation: 0.00000375
}
}
}
}If unset, ASM uses CostCalculator.default_pricing_table/0.
How Cost Is Calculated
CostCalculator.calculate/4 computes:
cost =
input_tokens * input_rate +
output_tokens * output_rate +
cache_read_tokens * (cache_read_rate || input_rate) +
cache_creation_tokens * (cache_creation_rate || input_rate)Rate resolution order:
- Exact model match.
- Longest prefix model match (for dated model ids like
...-20250929). - Provider default rate.
If no provider rates exist, calculation returns {:error, :no_rates}.
Policy Enforcement
Policy.Runtime captures model from :run_started events and applies
model-specific rates for ongoing cost accumulation. If no model is captured,
provider default rates are used.
Legacy flat policy_cost_rates remain supported for backward compatibility.
Querying Cost
Use QueryAPI.get_cost_summary/2:
query = {AgentSessionManager.Adapters.EctoQueryAPI, MyApp.Repo}
{:ok, summary} =
AgentSessionManager.Ports.QueryAPI.get_cost_summary(query,
provider: "claude",
since: DateTime.add(DateTime.utc_now(), -86_400, :second)
)Returned keys:
total_cost_usdrun_countby_providerby_modelunmapped_runs
Runs without stored cost_usd are calculated post-hoc when possible.
Rendering and Telemetry
- Compact/verbose renderers include cost labels when
cost_usdis present. - JSONL compact sink includes a
$field ontoken_usage_updated. - Run-stop telemetry includes
:cost_usdmeasurement when available.
Enterprise Pricing Tables
You can supply a custom pricing_table for negotiated rates, regional rates,
or internal chargeback models. A per-query override is also supported in
get_cost_summary/2 via :pricing_table.
Migration from Flat policy_cost_rates
Old format:
%{"claude" => %{input: 0.000003, output: 0.000015}}New structured format:
%{
"claude" => %{
default: %{input: 0.000003, output: 0.000015},
models: %{}
}
}CostCalculator.normalize_legacy_rates/1 accepts both formats during
migration, so rollout can be incremental.