# Symphony Token Accounting: How OpenAI's Symphony Tracks Codex Usage

> Discover how Symphony's token accounting tracks Codex usage by monitoring cumulative totals and thread snapshots, ensuring efficient resource management.

- Repository: [OpenAI/symphony](https://github.com/openai/symphony)
- Tags: internals
- Published: 2026-05-08

---

**Symphony tracks Codex token usage by extracting absolute cumulative totals from `TokenUsageInfo` structs, comparing them against per-thread snapshots stored in the orchestrator, and ignoring delta values unless absolutely necessary.**

Symphony is an open-source orchestration framework developed by OpenAI that manages complex multi-turn conversations with Codex. When Codex processes requests, it emits token usage telemetry through the app-server protocol. Understanding how Symphony token accounting transforms this raw data into reliable per-thread metrics is essential for building accurate usage dashboards and billing systems.

## Understanding the TokenUsageInfo Payload Structure

Codex communicates token consumption via a `TokenUsageInfo` struct containing two critical fields:

* `total_token_usage` – An absolute, cumulative snapshot of tokens consumed on the thread since inception.
* `last_token_usage` – The incremental delta that produced the current snapshot.

```rust
pub struct TokenUsageInfo {
    pub total_token_usage: TokenUsage,
    pub last_token_usage: TokenUsage,
    pub model_context_window: Option<i64>,
}

```

Symphony's orchestrator prioritizes the **absolute total** over the delta. According to the design specifications in [`elixir/docs/token_accounting.md`](https://github.com/openai/symphony/blob/main/elixir/docs/token_accounting.md), the absolute value serves as the canonical source of truth, while deltas are treated as supplementary data only used when absolute totals are unavailable.

## Extracting Absolute Totals in the Orchestrator

The core extraction logic resides in [`lib/symphony_elixir/orchestrator.ex`](https://github.com/openai/symphony/blob/main/lib/symphony_elixir/orchestrator.ex), where Symphony processes incoming Codex events and normalizes token usage data.

### The Extraction Pipeline

The `extract_token_usage/1` function implements a defensive search strategy across multiple possible payload locations:

```elixir
defp extract_token_usage(update) do
  payloads = [
    update[:usage],
    Map.get(update, "usage"),
    Map.get(update, :usage),
    update[:payload],
    Map.get(update, "payload"),
    update
  ]

  Enum.find_value(payloads, &absolute_token_usage_from_payload/1) ||
    Enum.find_value(payloads, &turn_completed_usage_from_payload/1) ||
    %{}
end

```

This function attempts to locate token data in six potential locations, prioritizing explicit `usage` keys before falling back to generic `payload` objects or the raw update itself.

### Path Resolution Strategy

Once a candidate payload is identified, `absolute_token_usage_from_payload/1` traverses a prioritized list of nested paths to locate the absolute total:

```elixir
absolute_paths = [
  ["params", "msg", "payload", "info", "total_token_usage"],
  [:params, :msg, :payload, :info, :total_token_usage],
  ["params", "msg", "info", "total_token_usage"],
  [:params, :msg, :info, :total_token_usage],
  ["params", "tokenUsage", "total"],
  [:params, :tokenUsage, :total],
  ["tokenUsage", "total"],
  [:tokenUsage, :total]
]

```

The function returns the first valid token usage map found at any of these locations, supporting both string and atom-keyed maps to handle different serialization formats.

## Computing Token Deltas and Thread State

After extracting the absolute total, Symphony computes the actual delta against previously stored values using `compute_token_delta/4`:

```elixir
defp compute_token_delta(running_entry, token_key, usage, reported_key) do
  next_total = get_token_usage(usage, token_key)
  prev_reported = Map.get(running_entry, reported_key, 0)

  delta =
    if is_integer(next_total) and next_total >= prev_reported do
      next_total - prev_reported
    else
      0
    end

  %{delta: max(delta, 0), reported: if(is_integer(next_total), do: next_total, else: prev_reported)}
end

```

The orchestrator maintains three critical fields per thread:

- **absolute_total**: The latest cumulative snapshot received from Codex (e.g., `tokenUsage.total`).
- **accumulated_total**: The value exposed to UI and API consumers, derived from `absolute_total`.
- **last_seen_turn_id**: The identifier of the turn that produced the snapshot, used to prevent double-counting when events arrive out of order or duplicate.

## Rendering Token Usage in the Dashboard

The status dashboard module ([`lib/symphony_elixir/status_dashboard.ex`](https://github.com/openai/symphony/blob/main/lib/symphony_elixir/status_dashboard.ex)) reuses the same extraction helpers to generate human-readable descriptions of token consumption:

```elixir
defp humanize_codex_wrapper_event("token_count", payload) do
  usage = extract_first_path(payload, token_usage_paths())

  case format_usage_counts(usage) do
    nil -> "token count update"
    usage_text -> "token count update (#{usage_text})"
  end
end

```

This ensures consistency between the accounting logic and user-facing displays, confirming that only absolute totals—not intermediate deltas—surface in the interface.

## Token Accounting Design Principles

The [`elixir/docs/token_accounting.md`](https://github.com/openai/symphony/blob/main/elixir/docs/token_accounting.md) document establishes strict guidelines for Symphony token accounting:

1. **Prefer** `thread/tokenUsage/updated.tokenUsage.total` or `info.total_token_usage` as the authoritative source.
2. **Ignore** `last_token_usage`, `tokenUsage.last`, and generic `usage` maps when calculating cumulative totals.
3. **Never** double-count turn-completed usage payloads on top of live thread totals.
4. **Key** all accounting by `thread_id`, recognizing that conversations span multiple turns across different model invocations.

These rules prevent the "delta accumulation drift" that occurs when systems erroneously sum incremental values instead of relying on absolute snapshots.

## Summary

- Symphony extracts **absolute cumulative totals** (`total_token_usage`) from Codex events rather than summing deltas.
- The orchestrator in [`lib/symphony_elixir/orchestrator.ex`](https://github.com/openai/symphony/blob/main/lib/symphony_elixir/orchestrator.ex) implements a defensive search across multiple payload paths to locate token data.
- Per-thread state tracks `absolute_total`, `accumulated_total`, and `last_seen_turn_id` to ensure idempotent accounting.
- The system ignores `last_token_usage` unless absolute data is missing, preventing double-counting and drift.
- All accounting keys by `thread_id`, treating conversations as persistent streams rather than isolated request-response pairs.

## Frequently Asked Questions

### What is the difference between total_token_usage and last_token_usage in Symphony?

`total_token_usage` represents the absolute cumulative count of tokens consumed on a thread since its creation, while `last_token_usage` represents only the incremental delta from the most recent turn. Symphony exclusively uses `total_token_usage` for accounting calculations, treating `last_token_usage` as a fallback signal only when absolute totals are unavailable.

### How does Symphony prevent double-counting tokens?

Symphony prevents double-counting by storing the `last_seen_turn_id` alongside the `absolute_total` for each thread. When processing new events, the orchestrator compares the incoming absolute total against the stored snapshot. If the new total is less than or equal to the stored value, or if the turn ID matches a previously processed turn, the event is discarded.

### Where does Symphony extract token usage data from Codex events?

The extraction logic in [`lib/symphony_elixir/orchestrator.ex`](https://github.com/openai/symphony/blob/main/lib/symphony_elixir/orchestrator.ex) searches six potential payload locations including `update[:usage]`, `Map.get(update, "payload")`, and the raw update itself. Once a candidate payload is found, the system traverses eight predefined nested paths (such as `["params", "msg", "payload", "info", "total_token_usage"]`) to locate the absolute token usage struct.

### Why does Symphony prefer absolute totals over delta values for token accounting?

Absolute totals eliminate cumulative drift errors that accumulate when delta values are summed over long-running threads. By using the cumulative `total_token_usage` as the source of truth and computing deltas only through subtraction against stored snapshots, Symphony ensures that network retries, out-of-order events, or duplicate payloads cannot inflate usage counts.