Skip to main content

Documentation Index

Fetch the complete documentation index at: https://tracepilot.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

TracePilot AI organizes every agent run into a structured hierarchy of traces and spans. A trace represents the full lifecycle of a single agent execution. Each individual operation within that run — an LLM call, a tool invocation, or any async step — is captured as a span. Together, these form a tree you can inspect, replay, and debug from the TracePilot dashboard.

What is a trace?

A trace is one complete agent run. You start a new trace at the entry point of your agent with tp.startTrace('agent-name'). Every span created after that call belongs to that trace, until the agent finishes.
import { TracePilot } from 'tracepilot-sdk';

const tp = new TracePilot('tp_live_YOUR_KEY');

async function runAgent() {
  await tp.startTrace('customer-support-agent');
  // All spans created here belong to this trace
}
Traces are named after the agent they represent. The name appears in the dashboard to help you identify and filter runs across your fleet.

What is a span?

A span is one captured operation within a trace. TracePilot creates a span every time you call tp.wrapOpenAI() or tp.wrapToolCall(). Each span records:
FieldDescription
inputThe messages or arguments passed into the call
outputThe response returned from the LLM or tool
tokensPrompt and completion token counts
latencyWall-clock time for the operation in milliseconds
errorAny exception thrown during the call
parentSpanIdThe ID of the parent span, if any
Every call to wrapOpenAI or wrapToolCall returns a spanId. You pass that ID as the parentSpanId of subsequent calls to link child spans to their parent.

Building a span tree

When your agent has multiple steps, spans form a tree. The root span has no parent. Child spans pass the parent’s spanId as their parentSpanId. This mirrors the actual execution flow and makes it easy to see which step triggered which downstream call. The stepOrder parameter numbers each span in the tree. TracePilot uses this to display spans in the correct sequence in the dashboard, regardless of when they were captured.
async function researchAgent(query: string) {
  await tp.startTrace('research-agent');

  const messages = [{ role: 'user', content: query }];

  // Step 1 — root span (no parent)
  const { result: plan, spanId: planSpanId } = await tp.wrapOpenAI(
    () => openai.chat.completions.create({ model: 'gpt-4o', messages }),
    messages
    // no parentSpanId — this is the root
    // no stepOrder — defaults to 1
  );

  // Step 2 — child of step 1 (tool call)
  const { result: searchResult, spanId: searchSpanId } = await tp.wrapToolCall(
    'web-search',
    () => webSearch(plan.choices[0].message.content),
    planSpanId, // parent span
    2           // step order
  );

  // Step 3 — child of step 2 (synthesis)
  const followUp = [
    ...messages,
    plan.choices[0].message,
    { role: 'tool', content: JSON.stringify(searchResult) }
  ];

  const { result: answer } = await tp.wrapOpenAI(
    () => openai.chat.completions.create({ model: 'gpt-4o', messages: followUp }),
    followUp,
    searchSpanId, // parent span
    3             // step order
  );

  return answer.choices[0].message.content;
}
This produces a three-level tree in the dashboard:
research-agent (trace)
└── Step 1: gpt-4o (planSpanId)
    └── Step 2: web-search (searchSpanId)
        └── Step 3: gpt-4o
stepOrder does not affect execution. It only controls the display order in the dashboard. Always set it to reflect the logical sequence of your agent’s steps.

Span data in the dashboard

Every span in the dashboard is expandable. Click a span to see its full input and output, the exact token breakdown (prompt vs. completion), latency in milliseconds, and any error message if the call failed.

LLM spans

Created by wrapOpenAI. Captures model name, messages in, completion out, and full token usage.

Tool spans

Created by wrapToolCall. Captures tool name, arguments, return value, and latency. Can be marked destructive.

Wrapping calls

1

Start a trace

Call tp.startTrace('agent-name') at the top of your agent function. This initializes the trace context for all subsequent spans.
2

Wrap LLM calls

Replace direct OpenAI calls with tp.wrapOpenAI(call, messages, parentSpanId?, stepOrder?). The result field contains the unmodified OpenAI response.
3

Wrap tool calls

Replace tool invocations with tp.wrapToolCall(toolName, call, parentSpanId, stepOrder, isDestructive?). Pass the spanId from the preceding LLM span as the parentSpanId.
4

Chain spans with parentSpanId

Each call returns a spanId. Pass it as parentSpanId to the next call to establish the parent-child link and build the execution tree.
You don’t need to change any logic in your agent. TracePilot wraps your existing calls and returns the original result untouched. Adding tracing is purely additive.