Traces & spans
- A trace is one end-to-end run (e.g. a single agent invocation, or a
Claude Code session). Spans in a trace share a
trace_id. - A span is a single operation within a trace — an LLM call, a tool call, a retrieval step. Spans carry timing, status, and attributes.
Observation types
Every span is classified so the UI can render it meaningfully:Promoted fields
RunAgain promotes the common gen_ai attributes to typed columns so they’re fast to query and chart:- Model — request and response model
- Tokens — input, output, cache-read, cache-write, reasoning, total
- Cost — computed from tokens, or taken from a provider-supplied
cost_usd - Identity —
session_id,conversation_id,user_id,agent_name - Deployment —
environment,version,release - Labels —
tags, plus a free-formmetadatabag - Tool —
tool_name, status
input / output payloads are offloaded to blob storage above a size
threshold and referenced by hash, keeping queries fast.
Reasoning & thinking
When a model emits extended thinking / reasoning, RunAgain captures it end to end: the reasoning token count lands in thereasoning column, and the reasoning text
is parsed out of the message payload and rendered as a distinct ✳ thinking block in the
transcript — kept separate from the assistant’s answer. The following shapes are
recognized automatically in gen_ai.input.messages / gen_ai.output.messages (and the
Langfuse / OpenInference / Vercel AI SDK equivalents):
- Anthropic — content blocks of type
thinking({ thinking, signature }) andredacted_thinking(shown as[redacted thinking]). - Vercel AI SDK —
reasoningcontent parts ({ type: "reasoning", text }). - OpenAI — a
reasoningoutput item with asummary, or a siblingreasoning_contentfield on the message.
gen_ai.usage.reasoning.output_tokens (or the
OpenInference llm.token_count.completion_details.reasoning alias). The
@runagain/ai-sdk integration emits both the reasoning
parts and the token count for you.
Custom metadata & identity
Set these as span attributes (or, for the deployment fields, resource attributes — a span attribute wins if both are present). RunAgain accepts the OTel/GenAI keys plus common vendor aliases, so most SDKs work with no mapping. First non-empty wins.
Deployment fields set on the OTLP resource apply to every span from that process — the natural
place for
environment / version / release. Environment is a column on the trace list (click a
value to filter); environment / version / release / user / tags / metadata all show on the trace.
Sessions
Spans that share asession_id (or, for Claude Code, session.id) are grouped into a
session — a multi-turn conversation you can replay end to end, with
every turn on one shared time axis.
The hierarchy is strict:
How this compares to other tools
The span → trace → session hierarchy is common across LLM observability tools; the main differences are what’s stored as the primitive and what the conversation grouping is called.
Two RunAgain choices worth calling out:
- Spans are the only stored primitive. Traces and sessions are rollups over spans
— a trace by
trace_id, a session bysession_idat query time. Tools like Langfuse and LangSmith persist the trace as a first-class object at ingest instead. The rollup approach keeps writes cheap and makes session grouping a plainGROUP BY. - OTel-aligned identity. RunAgain reads
session.idandgen_ai.conversation.id(see Custom metadata & identity), matching Phoenix and the OTel GenAI conventions — so an app instrumented for those is portable here with no remapping.
See it in the app
The Traces page renders the span waterfall, tokens, cost and status.