Skip to content
Changelog

Token, cost and model metrics for ClaudeAgent and CodexAgent runs

October 12, 20261 min read

Agno's ClaudeAgent maps the Claude SDK's result message onto RunOutput.metrics, with input, output and cache tokens, the cost in USD, and one entry per model in metrics.details["model"], where Claude Code's helper model gets its own line and cost. CodexAgent takes the growth of the thread's token total over the turn, so a turn with two tool calls counts all three model requests. Agno times both, setting duration and, for streamed runs, time_to_first_token.

Completed runs add up into the session's session_metrics. AgentOS shows those totals in the sessions list, the session view and the daily /metrics aggregation.

Runs also carry model and model_provider, which the Models Runs card on the AgentOS metrics page counts. With no model on the agent or in its options, ClaudeAgent now runs claude-sonnet-4-6 and CodexAgent runs gpt-5.6-luna, and the run records what the SDK was asked to run. Earlier releases left an unset model to the CLI, so set model yourself if you relied on the CLI's default. A model in options (Claude) or thread_options and turn_options (Codex) overrides that default.

Before and after, real output from two CodexAgent runs through AgentOS with a fake Codex SDK standing in for the model. Before: run.model None, run.metrics None, GET /sessions/s1 total_tokens None, GET /metrics model_metrics empty. Now: run.model gpt-5.6-luna (OpenAI), run.metrics 49270 in, 164 out, 49434 total, GET /sessions/s1 total_tokens 98868, GET /metrics model_metrics gpt-5.6-luna, OpenAI, count 2.

On the Claude side, the fake SDK from the release's tests stands in for Claude Code and returns a fixed usage report:

agent = ClaudeAgent(id="claude", db=db)  # no model set
 
run = list(agent.run("hi", session_id="s", stream=True, yield_run_output=True))[-1]
print(run.model, run.model_provider)
m = run.metrics
print(m.input_tokens, m.output_tokens, m.cache_read_tokens, m.cache_write_tokens, m.cost)
print([(x.id, x.cost) for x in m.details["model"]])
claude-sonnet-4-6 Anthropic
3 5 14334 5369 0.0365982
[('claude-sonnet-4-6', 0.0365982)]

Claude's input_tokens leaves out the cached prefix, as the native Anthropic model does, while Codex's includes it, as the OpenAI API does. Codex reports no cost, so cost stays unset on CodexAgent runs.

See the cookbook and its Codex counterpart, and learn more about provider metrics in the documentation.

Frequently asked questions

Read run.metrics. It carries input, output and cache tokens, cost in USD, duration, time_to_first_token for streamed runs, and one entry per model in metrics.details["model"].

No. Codex reports tokens but no cost, so metrics.cost stays unset on CodexAgent runs. An unset cost does not mean the turn was free.

Agno asks the SDK to run claude-sonnet-4-6 for ClaudeAgent and gpt-5.6-luna for CodexAgent, and records that model on the run. A model set in the agent's options takes precedence over that default.

Earlier releases left an unset model to the Codex CLI, which picks its own default. Agno now pins gpt-5.6-luna, so set model on the agent to keep the model you had.

Shipped around the same time