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

# tp.wrapOpenAI() method — TracePilot SDK reference

> Wrap any OpenAI chat completion call to capture a span. Returns the original ChatCompletion result unchanged, plus a spanId for linking child spans.

`tp.wrapOpenAI()` intercepts an OpenAI chat completion call, records the input messages, output, token usage, latency, and any errors as a structured span, and then returns the original `ChatCompletion` object without modification. You can use the returned `spanId` to attach child spans and build a nested execution tree that is visible in the dashboard.

## Signature

```typescript theme={null}
tp.wrapOpenAI(
  call: () => Promise<ChatCompletion>,
  messages: ChatCompletionMessageParam[],
  parentSpanId?: string,
  stepOrder?: number
): Promise<{ result: ChatCompletion; spanId: string }>
```

## Parameters

<ParamField path="call" type="() => Promise<ChatCompletion>" required>
  A zero-argument function that calls `openai.chat.completions.create(...)` and returns a `Promise<ChatCompletion>`. Wrap your call in an arrow function so TracePilot can time it and catch errors.
</ParamField>

<ParamField path="messages" type="ChatCompletionMessageParam[]" required>
  The messages array you pass to the completion. TracePilot records these as the span input so you can inspect and edit them in the dashboard's Fork & Rerun flow.
</ParamField>

<ParamField path="parentSpanId" type="string">
  The `spanId` of a parent span. Providing this value links the current span as a child of the parent, building a hierarchical execution tree in the dashboard. Omit for root-level LLM calls.
</ParamField>

<ParamField path="stepOrder" type="number">
  An integer indicating this span's position in the execution sequence. Used to order sibling spans in the dashboard when multiple spans share the same parent.
</ParamField>

## Return value

<ResponseField name="result" type="ChatCompletion">
  The original OpenAI `ChatCompletion` response, returned unchanged. Your agent code can use this exactly as it would without TracePilot.
</ResponseField>

<ResponseField name="spanId" type="string">
  The ID of the span created for this call. Pass this as `parentSpanId` to subsequent `wrapOpenAI` or `wrapToolCall` calls to nest them under this span.
</ResponseField>

## Examples

<CodeGroup>
  ```typescript Basic usage theme={null}
  import { TracePilot } from 'tracepilot-sdk';
  import OpenAI from 'openai';

  const tp = new TracePilot(process.env.TRACEPILOT_API_KEY!);
  const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

  async function runAgent() {
    await tp.startTrace('my-agent');

    const messages = [{ role: 'user', content: 'Summarise this document.' }];

    const { result, spanId } = await tp.wrapOpenAI(
      () => openai.chat.completions.create({ model: 'gpt-4o-mini', messages }),
      messages
    );

    console.log(result.choices[0].message.content);
  }
  ```

  ```typescript With all parameters theme={null}
  import { TracePilot } from 'tracepilot-sdk';
  import OpenAI from 'openai';

  const tp = new TracePilot(process.env.TRACEPILOT_API_KEY!);
  const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

  async function runAgent() {
    await tp.startTrace('research-agent');

    const messages = [{ role: 'user', content: 'What is the capital of France?' }];

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

    // Step 2 — child of planSpanId
    const followUp = [
      ...messages,
      plan.choices[0].message,
      { role: 'tool', content: 'Paris' }
    ];

    const { result: answer } = await tp.wrapOpenAI(
      () => openai.chat.completions.create({ model: 'gpt-4o', messages: followUp }),
      followUp,
      planSpanId, // links this span under the first call
      2
    );

    return answer.choices[0].message.content;
  }
  ```
</CodeGroup>

## Building span trees

Every `wrapOpenAI` call returns a `spanId`. Passing that `spanId` as `parentSpanId` in a later call creates a parent-child relationship between the two spans. The dashboard renders these relationships as an indented execution tree, making it easy to see which LLM call triggered which downstream steps.

```typescript theme={null}
// Step 1 — initial reasoning
const { spanId: step1Id } = await tp.wrapOpenAI(call1, messages1, undefined, 1);

// Step 2 — tool call triggered by step 1
const { spanId: step2Id } = await tp.wrapToolCall('web-search', search, step1Id, 2);

// Step 3 — synthesis based on tool output
const { spanId: step3Id } = await tp.wrapOpenAI(call3, messages3, step2Id, 3);
```

When a span in the tree fails, you can open the dashboard, select that span, and use **Fork & Rerun** to edit its input and re-execute from that exact point — without redeploying your agent.
