> ## 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 Vercel AI SDK models and tools with Mortem

> Wrap your Vercel AI SDK language model and tools with Mortem to capture every LLM call, tool invocation, token count, and cost estimate per trace.

Mortem integrates with the Vercel AI SDK by wrapping your language model and tools before passing them to `generateText`, `streamText`, or any other AI SDK function. The wrappers intercept every call transparently — no changes to your prompts, model configuration, or tool definitions required.

## What gets captured

Mortem records the following for each agent run:

* **LLM calls** — model ID, input messages, parameters, output text, and streamed content
* **Tool invocations** — tool name, input arguments, and output values
* **Token counts** — reported by the model provider where available
* **Cost estimates** — calculated from token usage and recorded on each `llm_call` event

## 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 the top of your agent module. Pass your API key, agent ID, and the environment your bot runs on.

    ```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 your model and tools">
    Call `mortem.wrapLanguageModel` and `mortem.wrapTools` before starting the session. Both wrappers mutate the object in place and return the same reference, so you can pass them directly to `generateText`.

    ```ts theme={null}
    import { openai } from "@ai-sdk/openai"

    const model = openai("gpt-4o")
    const tracedModel = mortem.wrapLanguageModel(model)

    const tools = {
      getPrice: {
        description: "Get the current token price",
        parameters: z.object({ mint: z.string() }),
        execute: async ({ mint }) => fetchPrice(mint),
      },
    }
    const tracedTools = mortem.wrapTools(tools)
    ```

    Both wrappers are idempotent — wrapping the same model or tools object twice has no effect.
  </Step>

  <Step title="Start a session and run the agent">
    Create a session with `mortem.startSession`, then execute your agent logic inside `session.run`. The `session.run` callback keeps Mortem's async trace context active so all child LLM and tool events are associated with this trace.

    ```ts theme={null}
    import { generateText } from "ai"

    const session = await mortem.startSession({
      inputSummary: "Evaluate whether the bot should open a token position",
      tags: ["swap", "devnet"],
    })

    try {
      const result = await session.run(async () => {
        return generateText({
          model: tracedModel,
          tools: tracedTools,
          maxSteps: 5,
          prompt: "Should I swap 1 SOL for JUP right now?",
        })
      })

      await session.complete(result.text)
    } 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 are delivered before the process exits.
    </Warning>
  </Step>
</Steps>

## Complete example

The following is the full integration pattern in a single file:

```ts theme={null}
import { Mortem } from "@mortemlabs/sdk"
import { openai } from "@ai-sdk/openai"
import { generateText } from "ai"
import { z } from "zod"

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 model = openai("gpt-4o")
const tracedModel = mortem.wrapLanguageModel(model)

const tools = {
  getPrice: {
    description: "Get the current token price",
    parameters: z.object({ mint: z.string() }),
    execute: async ({ mint }: { mint: string }) => fetchPrice(mint),
  },
}
const tracedTools = mortem.wrapTools(tools)

const session = await mortem.startSession({
  inputSummary: "Evaluate whether the bot should open a token position",
  tags: ["swap", "devnet"],
})

try {
  const result = await session.run(async () => {
    return generateText({
      model: tracedModel,
      tools: tracedTools,
      maxSteps: 5,
      prompt: "Should I swap 1 SOL for JUP right now?",
    })
  })

  await session.complete(result.text)
} catch (error) {
  await session.fail(error)
} finally {
  await mortem.close()
}
```

## Streaming

`mortem.wrapLanguageModel` captures streaming responses too. When you use `streamText` instead of `generateText`, the wrapper taps the underlying `doStream` method and accumulates content deltas as they arrive. The `llm_call` event is completed with the full assembled content when the stream closes.

No extra configuration is needed — the same `tracedModel` works for both `generateText` and `streamText`.

## Why `session.run` matters

`session.run` establishes an async context that child events — LLM calls, tool invocations — attach to. Without it, a top-level trace is created but the child events from inside `generateText` have no session to bind to and are silently dropped.

<Tip>
  If you call `generateText` or `streamText` from multiple async paths in the same run, nest all of them inside a single `session.run` callback. The context propagates through promise chains and async iterators automatically.
</Tip>
