Skip to main content

Overview

Upsonic records every model call into a single centralized usage registry. Tokens, cost, requests, tool calls, and timing are written exactly once per call, keyed by a unique entry_id, and tagged with the scopes the call belongs to. Reading metrics anywhere in the framework is a derived view over those rows — no manual rollups, no double-counting on retry. You never interact with the registry directly. Instead, every surface that has metrics exposes a single read-only .usage property:
All five return the same shape — an AggregatedUsage view — so once you learn the fields, you can read them from anywhere.

The AggregatedUsage shape

AggregatedUsage is a read-only dataclass derived from the registry on each access.
cost is None (rather than 0.0) when no contributing entry was priced. 0.0 means at least one entry was priced and the total came out free.

Scope tags

Every recorded UsageEntry carries scope tags so the same row can be filtered into multiple views: Scope tags are propagated through Python contextvars. Sub-pipeline LLM calls — memory summarization, reliability validator/editor, culture checks, policy enforcement, sub-agents — automatically inherit the parent’s tags, so their spend rolls up into agent.usage, chat.usage, etc., without any explicit propagation step.

Idempotency and retries

The registry is keyed by entry_id. Re-recording an entry with the same id replaces the prior row rather than adding a second one. Retried requests therefore never double-count, and there is no separate baseline/snapshot machinery to keep in sync.

Persistence

When you configure a storage backend on Chat, recorded entries are persisted alongside the conversation. Re-opening the same session_id re-hydrates the registry, so chat.usage continues from where it left off across processes and restarts. Supported backends: InMemory, JSON, SQLite, PostgreSQL, MongoDB, Redis.

Examples

Per-task vs. per-agent

Chat sessions

Teams

Migration from the legacy surface

The following legacy surfaces have been removed in favour of .usage. If you have older code, the replacements are: