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

# Instrument OpenAI client calls with Mortem tracing

> Wrap your OpenAI client with Mortem to capture every chat completion — prompts, responses, model, token usage, and cost — without changing call sites.

Mortem wraps the OpenAI client structurally, patching `chat.completions.create` at runtime. It does not import the `openai` package itself, so the wrapper works with any version of the SDK and adds no extra dependency to your agent.

## What gets captured

For each call to `chat.completions.create`, Mortem records an `llm_call` event containing:

* **Prompts** — the full message array passed to the model
* **Completion** — the assistant message content returned
* **Model** — the model string from the request parameters
* **Token usage** — `prompt_tokens`, `completion_tokens`, and `total_tokens` from the response
* **Cost estimate** — calculated from token counts and recorded on the event
* **Finish reason** — `stop`, `length`, `tool_calls`, or any other finish reason the model returns
* **Tool calls** — structured tool call arguments when the model uses function calling

Both standard (non-streaming) and streaming responses are captured. For streaming, Mortem accumulates content deltas as they arrive and records the assembled completion when the stream ends.

## Prerequisites

Install the SDK and create an agent in the dashboard before continuing. You need `MORTEM_API_KEY` and `MORTEM_AGENT_ID` set in your environment.

## Integration

<Steps>
  <Step title="Initialize the Mortem client">
    Create a `Mortem` instance at module scope. This is typically done once at the start of your agent process.

    ```ts theme={null}
    import { Mortem } from "@mortemlabs/sdk"

    const mortem = new Mortem({
      apiKey: process.env.MORTEM_API_KEY ?? "",
      agentId: process.env.MORTEM_AGENT_ID,
      verifyToken: process.env.MORTEM_VERIFY_TOKEN, // remove after first verified run
      environment: "devnet",
    })
    ```

    <Note>
      `verifyToken` is only needed during your first deployment. Once the dashboard shows the agent as verified, remove `MORTEM_VERIFY_TOKEN` from your environment and code.
    </Note>
  </Step>

  <Step title="Wrap the OpenAI client">
    Pass your OpenAI client instance to `mortem.wrapOpenAI`. The wrapper patches `chat.completions.create` in place and returns the same client reference. All subsequent calls on the wrapped client are automatically traced.

    ```ts theme={null}
    import OpenAI from "openai"

    const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
    const tracedOpenAI = mortem.wrapOpenAI(openai)
    ```

    You can replace the original variable if you want tracing for every call in the module:

    ```ts theme={null}
    const openai = mortem.wrapOpenAI(new OpenAI({ apiKey: process.env.OPENAI_API_KEY }))
    ```
  </Step>

  <Step title="Start a session and run the agent">
    Create a session with `mortem.startSession`, then run your agent logic inside `session.run`. All `chat.completions.create` calls made through the wrapped client inside the callback are automatically associated with this trace.

    ```ts theme={null}
    const session = await mortem.startSession({
      inputSummary: "Analyze market conditions and decide whether to swap",
      tags: ["openai", "devnet"],
    })

    try {
      const result = await session.run(async () => {
        return tracedOpenAI.chat.completions.create({
          model: "gpt-4o",
          messages: [
            {
              role: "system",
              content: "You are a Solana trading bot. Analyze the data and respond with a trading decision.",
            },
            {
              role: "user",
              content: "SOL is at $145 and JUP is at $0.62. Should I swap 1 SOL for JUP?",
            },
          ],
        })
      })

      const decision = result.choices[0]?.message.content ?? ""
      await session.complete(decision)
    } catch (error) {
      await session.fail(error)
    } finally {
      await mortem.close()
    }
    ```

    <Warning>
      Always call `mortem.close()` in a `finally` block. It flushes the trace buffer and ensures all events reach the ingest service before the process exits.
    </Warning>
  </Step>
</Steps>

## Complete example

```ts theme={null}
import { Mortem } from "@mortemlabs/sdk"
import OpenAI from "openai"

const mortem = new Mortem({
  apiKey: process.env.MORTEM_API_KEY ?? "",
  agentId: process.env.MORTEM_AGENT_ID,
  verifyToken: process.env.MORTEM_VERIFY_TOKEN,
  environment: "devnet",
})

const openai = mortem.wrapOpenAI(new OpenAI({ apiKey: process.env.OPENAI_API_KEY }))

const session = await mortem.startSession({
  inputSummary: "Analyze market conditions and decide whether to swap",
  tags: ["openai", "devnet"],
})

try {
  const result = await session.run(async () => {
    return openai.chat.completions.create({
      model: "gpt-4o",
      messages: [
        {
          role: "system",
          content: "You are a Solana trading bot. Analyze the data and respond with a trading decision.",
        },
        {
          role: "user",
          content: "SOL is at $145 and JUP is at $0.62. Should I swap 1 SOL for JUP?",
        },
      ],
    })
  })

  const decision = result.choices[0]?.message.content ?? ""
  await session.complete(decision)
} catch (error) {
  await session.fail(error)
} finally {
  await mortem.close()
}
```

## Streaming

When you pass `stream: true` to `chat.completions.create`, the wrapper detects the async-iterable response and taps it with a generator that accumulates content deltas. The `llm_call` event is completed with the assembled text once the stream is exhausted.

```ts theme={null}
const stream = await openai.chat.completions.create({
  model: "gpt-4o",
  stream: true,
  messages: [{ role: "user", content: "Evaluate current market risk." }],
})

let text = ""
for await (const chunk of stream) {
  text += chunk.choices[0]?.delta?.content ?? ""
}

await session.complete(text)
```

No additional configuration is needed — the same wrapped client handles both streaming and non-streaming calls.

## Function calling

When the model returns tool calls, Mortem captures each tool call block under the `output.toolCalls` field of the `llm_call` event. Each entry includes the tool call ID, function name, and raw arguments string as returned by the API.

<Tip>
  If you want to trace the execution of tool functions themselves — not just the model's decision to call them — add a `tool_call` event with `session.beginEvent("tool_call", payload)` around your tool handler, or use the Vercel AI SDK wrapper which instruments tool execution automatically.
</Tip>
