> ## Documentation Index
> Fetch the complete documentation index at: https://docs.upsonic.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Usage Registry

> One append-only registry of every model call — read uniformly through agent.usage, task.usage, chat.usage, team.usage, and output.usage.

## 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:

```python theme={null}
agent.usage       # rolled up across every model call this agent participated in
task.usage        # filtered to this task's scope
chat.usage        # filtered to this chat session
team.usage        # filtered across a team and its members
output.usage      # the per-run snapshot returned alongside an agent run
```

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.

| Field | Type | Description |
| - | - | - |
| `input_tokens` | `int` | Prompt/input tokens |
| `output_tokens` | `int` | Completion/output tokens |
| `total_tokens` | `int` | `input_tokens + output_tokens` |
| `cache_read_tokens` | `int` | Tokens read from prompt cache |
| `cache_write_tokens` | `int` | Tokens written to prompt cache |
| `reasoning_tokens` | `int` | Chain-of-thought / reasoning tokens |
| `requests` | `int` | Number of model requests |
| `tool_calls` | `int` | Number of tool calls |
| `cost` | `float \| None` | Sum of `cost_usd` across contributing entries; `None` if nothing priced |
| `duration` | `float` | Sum of per-call durations recorded on entries |
| `model_execution_time` | `float` | Time spent inside model calls |
| `tool_execution_time` | `float` | Time spent inside tool calls |
| `upsonic_execution_time` | `float` | Framework overhead = `duration − model − tool` |
| `time_to_first_token` | `float \| None` | Earliest TTFT across contributing entries |
| `entry_count` | `int` | Number of contributing `UsageEntry` rows |
| `models` | `list[str]` | Distinct models that contributed, first-seen order |

```python theme={null}
u = agent.usage
print(u.input_tokens, u.output_tokens, u.cost, u.models)
print(u.to_dict())  # JSON-friendly flat dict for logs/dashboards
```

`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:

| Tag | Set by | Visible as |
| - | - | - |
| `chat_usage_id` | `Chat` session | `chat.usage` |
| `agent_usage_id` | `Agent` instance | `agent.usage` |
| `task_usage_id` | `Task` | `task.usage` |
| `team_usage_id` | `Team` | `team.usage` |
| `workflow_usage_id` | StateGraph / workflow | (registry queries) |
| `system_usage_id` | System-level groupings | (registry queries) |
| `run_id` | Per-run identifier | `output.usage` (one run) |
| `user_id` | Per-user identifier | (registry queries) |

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

```python theme={null}
from upsonic import Agent, Task

agent = Agent("anthropic/claude-sonnet-4-5")
t1 = Task("Say hello.")
t2 = Task("Say goodbye.")
agent.do(t1)
agent.do(t2)

print(t1.usage.total_tokens, t2.usage.total_tokens)  # per-task
print(agent.usage.total_tokens)                       # both tasks + any sub-pipeline calls
```

### Chat sessions

```python theme={null}
import asyncio
from upsonic import Agent, Chat

async def main():
    chat = Chat(session_id="s1", user_id="u1", agent=Agent("anthropic/claude-sonnet-4-5"))
    await chat.invoke("Hello")
    await chat.invoke("How are you?")

    u = chat.usage
    if u.cost is not None:
        print(f"${u.cost:.4f} across {u.requests} requests using {u.models}")
    print(f"Wall-clock session length: {chat.duration:.1f}s")

asyncio.run(main())
```

### Teams

```python theme={null}
from upsonic import Agent, Team

team = Team(agents=[Agent("openai/gpt-4o-mini"), Agent("anthropic/claude-sonnet-4-5")])
team.do("Plan and review a small feature.")

print(team.usage.to_dict())  # spend across every member + sub-pipeline call
```

## Migration from the legacy surface

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

| Legacy | Replacement |
| - | - |
| `task.price_id`, `task.get_total_cost()`, `task.total_input_token`, `task.total_output_token` | `task.task_usage_id`, `task.usage.X` |
| `task.duration`, `task.model_execution_time`, `task.tool_execution_time`, `task.upsonic_execution_time` | `task.usage.X` (per-call sums); for wall-clock use `task.end_time - task.start_time` |
| `agent.cost` (dict) | `agent.usage.to_dict()` |
| `chat.input_tokens`, `chat.output_tokens`, `chat.total_tokens`, `chat.total_cost`, `chat.total_requests`, `chat.total_tool_calls`, `chat.run_duration`, `chat.time_to_first_token` | `chat.usage.X` |
| `chat.get_usage()`, `chat.get_session_metrics()`, `chat.get_session_summary()` | `chat.usage`; for message count `len(chat.all_messages)`; for wall-clock `chat.duration` |
| `SessionMetrics` dataclass | `chat.usage` + `chat.duration` + `len(chat.all_messages)` |
| `UPSONIC_LEGACY_USAGE` env flag | (removed; the unified registry is always on) |

## Related Documentation

* [Agent Metrics](/concepts/agents/metrics)
* [Task Metrics](/concepts/tasks/metrics)
* [Chat Metrics](/concepts/chat/metrics)
* [Team Metrics](/concepts/team/metrics)
* [Tracing Overview](/concepts/tracing/overview)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.