Documentation Index
Fetch the complete documentation index at: https://docs.trodo.ai/docs/llms.txt
Use this file to discover all available pages before exploring further.
Understanding these core concepts will help you get the most out of Trodo’s observability platform.
How a trace is structured
A single agent execution produces a tree of spans. wrapAgent creates the root — the run — and everything that runs inside it becomes a child span: provider SDK calls, tool executions, retrieval steps, nested sub-steps.
run: support-agent kind: run
├─ llm openai.chat.completions kind: llm
│ prompt · completion · tokens · cost · model
├─ tool lookup_order kind: tool
│ input · output · toolName
├─ retrieval vector-search kind: retrieval
│ query · topK · results
└─ llm openai.chat.completions kind: llm
second LLM call in the same run
Auto-instrumentation creates the llm, tool, and retrieval child spans automatically when you use supported frameworks (OpenAI, Anthropic, LangChain, LlamaIndex, Vercel AI SDK, Bedrock, Cohere, Gemini, Vertex, Mistral). For everything else, you wrap calls with the typed helpers — trodo.tool(), trodo.llm(), trodo.retrieval(), trodo.trace() — or emit spans directly with withSpan.
Runs
A run is a single execution of your agent from start to finish. One call to wrapAgent (Node) or wrap_agent (Python) produces one run. It captures:
- Inputs — the initial state and parameters passed to your agent.
- Outputs — the final result produced by your agent.
- Spans — the nested operations within the execution.
- Timing — duration and timing of each operation.
- Metadata — additional context like environment, version, custom tags.
- Rollups — total tokens, total cost, span count, tool count, error count — all summed from child spans, never written by you.
Runs are built on OpenTelemetry, the industry-standard observability framework. Each run has a unique run_id and contains one or more spans organised hierarchically.
What you set vs. what’s rolled up
| You set on the run | Auto-rolled from spans |
|---|
agentName, distinctId, conversationId | duration_ms |
setInput / setOutput / setMetadata | total_tokens_in, total_tokens_out |
setErrorSummary | total_cost |
| — | span_count, tool_count, error_count |
| — | status (ok / error / running) |
Run ID
The run_id is Trodo’s identifier for a specific agent execution. wrapAgent returns it alongside the result:
const { result, runId } = await trodo.wrapAgent('my-agent', async (run) => { ... });
Use it to:
- Query and filter traces in the dashboard.
- Attach feedback after a run finishes.
- Link multi-turn conversations via a shared
conversationId.
- Propagate the run across service boundaries (see Propagation).
Distinct ID
A distinctId is the identifier of the end-user whose action triggered the run. Pass it to wrapAgent and every run for that user is filterable in the dashboard, attributable in the user timeline, and groupable for cohort analysis.
await trodo.wrapAgent('my-agent', fn, { distinctId: 'user-42' });
Conversation ID
A conversationId is an optional string you attach to multiple runs so Trodo can treat them as part of the same multi-turn flow. When you pass the same non-empty conversationId across several runs, the dashboard surfaces a Conversation · N indicator on the runs list, and the conversation view shows every turn stacked in order.
Conversation IDs are never inferred — you must pass them explicitly:
await trodo.wrapAgent('chat', fn, { distinctId: 'user-42', conversationId: 'thread-abc' });
Spans
Spans are the building blocks of a run. Each span represents a single operation within your agent’s execution:
- An LLM generation call.
- A tool or function invocation.
- A retrieval / vector search.
- A nested agent stage.
- Any custom operation you want to track.
Spans are hierarchical — a parent span can contain multiple child spans. The resulting tree represents your agent’s execution flow and makes it easy to see:
- Which operations happened in what order.
- How long each operation took.
- Where errors occurred in the execution path.
- What inputs each step received and what outputs it produced.
Span kinds
A span’s kind is a type tag that determines which fields the dashboard renders and which rollups the run inherits. Six kinds:
| Kind | Used for | Feeds run rollups |
|---|
llm | Model calls (OpenAI, Anthropic, Bedrock, …) | total_tokens_in, total_tokens_out, total_cost |
tool | Functions the agent invoked (search, DB, API) | tool_count |
retrieval | Vector search, KB lookup, RAG retriever | — |
agent | A nested sub-step rendered as an “agent stage” | — |
chain | A chain / graph step (LangChain, LangGraph) | — |
function | Generic instrumented function call | — |
LLM span fields
LLM spans carry the most structured data. For every supported provider, Trodo auto-populates these fields from the response — you don’t set them manually unless you’re calling the provider via raw HTTP.
| Field | What it is | Auto-captured? |
|---|
model | The requested model ID (e.g. gpt-4o-mini) | Yes |
provider | The vendor (e.g. openai, anthropic) | Yes |
input_tokens | Prompt tokens consumed | Yes (from response usage) |
output_tokens | Completion tokens produced | Yes |
cost | USD cost, computed from tokens + model | Yes (server-side pricing table) |
temperature | Sampling temperature used | Yes (from request body) |
input | Prompt messages | Yes |
output | Completion content | Yes |
error_type / error_message | Exception details if the call threw | Yes (on throw) |
| Field | What it is | Auto-captured? |
|---|
toolName | The tool/function name (e.g. search_kb) | Yes (LangChain, LlamaIndex, OpenAI Assistants tool calls) |
input | Arguments the tool was called with | Yes for framework-managed tools; manual otherwise |
output | Return value / tool result | Yes for framework-managed tools; manual otherwise |
Retrieval span fields
Retrieval spans have no required fields beyond name. Convention is to record the query as input and a summary of results (count, top IDs) as output so the dashboard waterfall shows what was searched and what came back.
Universal span fields
Every span, regardless of kind, carries:
| Field | What it is |
|---|
name | Free-text label (e.g. chat.completions, search_kb) |
kind | One of the six kinds above |
status | ok on success, error on thrown exception |
duration_ms | Wall-clock time from start to end |
attributes | Free-form key/value metadata (e.g. customer_tier, region) |
parent_run_id
Links two runs together. Use when a sub-agent has its own lifecycle (own name, own rollups) but is triggered by another agent. Lives on the child run. The parent shows in the dashboard as “run X triggered sub-run Y”, and you can navigate between them.
See Recipes → Sub-agents for the pattern.
Propagation
How the SDK knows which run a span belongs to — without you passing runId everywhere.
- In-process. Node uses
AsyncLocalStorage, Python uses contextvars. Every withSpan, tool(), llm(), retrieval(), trace(), or auto-instrumented provider call inside wrapAgent (or inside any function it awaits) attaches automatically. Context survives await, imports, helper functions — the whole async chain inside the process.
- Cross-service. Trodo’s SDK attaches an
X-Trodo-Run-Id header to outbound HTTP when you use propagationHeaders(). The receiving service’s middleware (expressMiddleware, fastapi_middleware) reads it and calls joinRun so its spans land on the caller’s run. See Recipes → Cross-service.
- Out-of-context. If you emit spans from a file outside an active
wrapAgent — worker threads, process pools, queue consumers — you must capture the runId in the parent, pass it across the boundary, and call joinRun(runId, fn) in the child. See Tracing → Spans outside wrapAgent.
Projects and sites
A site is the top-level container in Trodo. It groups:
- All runs from your agent(s) for a given environment.
- The users tracked in that environment.
- The dashboards, clusters, and monitors built on top of those runs.
Each site has:
- A site ID — used when calling
trodo.init() and for SDK authentication.
- A dashboard for viewing and analysing data.
Most teams run one site per application per environment — e.g. my-app-prod, my-app-staging. Separating environments by site keeps prod dashboards clean and makes staging experimentation safe.
Tracer provider
The tracer provider is the OpenTelemetry component responsible for creating and managing spans, exporting them to Trodo’s OTLP endpoint, and handling batching and retries. You normally never interact with it directly — trodo.init() sets it up.
Two cases where it matters:
- Dual export. You already run an OTel provider (Honeycomb, Datadog, Jaeger). Trodo attaches its own span processor alongside yours — both backends receive every span. See Recipes → Dual export.
- Advanced custom instrumentation. Use
trodo.getTracer() to get the underlying OTel tracer if you want to emit spans via the raw OTel API. They flow through to Trodo like any other span.
Next steps