> ## 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 LLM and Solana clients with Mortem wrappers

> Use mortem.wrapOpenAI(), wrapAnthropic(), wrapTools(), wrapLanguageModel(), langchainHandler(), and wrapConnection() to capture events automatically.

Wrapper methods instrument your existing provider clients without requiring you to emit events manually. Pass a client to the appropriate wrapper, use the returned client in place of the original, and Mortem records every LLM call, tool invocation, and Solana transaction as a typed event inside the active session.

All wrappers use structural typing — the SDK does not import or hard-depend on any provider package. Your bundle size is unaffected if a provider is not installed, and wrappers work across provider versions as long as the underlying API shape matches.

<Note>
  Wrappers only emit events when called inside an active `session.run()` context. If a wrapped client is called outside a session, the original method runs unchanged with no instrumentation overhead.
</Note>

## OpenAI

Wrap your OpenAI client once at startup and use it everywhere in your agent. The wrapper instruments `chat.completions.create` for both streaming and non-streaming responses.

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

const mortem = new Mortem({ apiKey: process.env.MORTEM_API_KEY ?? "" })
const openai = mortem.wrapOpenAI(new OpenAI())

// Inside session.run(), every call is recorded as an llm_call event
const response = await openai.chat.completions.create({
  model: "gpt-4o",
  messages: [{ role: "user", content: "Should I open a JUP position?" }],
})
```

## Anthropic

Wrap your Anthropic client in the same way. The wrapper instruments `messages.create` and captures streaming content deltas, tool use blocks, and token usage.

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

const anthropic = mortem.wrapAnthropic(new Anthropic())

const message = await anthropic.messages.create({
  model: "claude-opus-4-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Analyze this trade setup." }],
})
```

## Ollama

Wrap the Ollama client to capture `ollama.chat` calls, including JSON-mode requests and streamed responses. Ollama costs are billed externally through ollama.com, so the dashboard shows usage as tracked externally rather than as a USD estimate.

```ts theme={null}
import { Ollama } from "ollama"

const ollama = mortem.wrapOllama(new Ollama())

const response = await ollama.chat({
  model: "llama3.2",
  messages: [{ role: "user", content: "What is the current SOL sentiment?" }],
})
```

## Vercel AI SDK

Use `wrapTools` and `wrapLanguageModel` together to instrument Vercel AI SDK agent loops. `wrapTools` patches each tool's `execute` function to emit `tool_call` events. `wrapLanguageModel` patches `doGenerate` and `doStream` to emit `llm_call` events.

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

const tools = {
  fetchPrice: tool({
    description: "Fetch the current token price",
    parameters: z.object({ symbol: z.string() }),
    execute: async ({ symbol }) => fetchCurrentPrice(symbol),
  }),
}

const model = openai("gpt-4o")

const tracedTools = mortem.wrapTools(tools)
const tracedModel = mortem.wrapLanguageModel(model)

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

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()
}
```

<Tip>
  Always wrap both `tools` and `model` when using the Vercel AI SDK. Wrapping only the model captures LLM calls but misses individual tool invocations, and vice versa.
</Tip>

## LangChain

Use `mortem.langchainHandler()` to get a callback handler and add it to your LangChain chains or agents. The handler captures LLM start/end/error and tool start/end/error events without a hard dependency on `@langchain/core`.

```ts theme={null}
import { ChatOpenAI } from "@langchain/openai"
import { AgentExecutor, createOpenAIToolsAgent } from "langchain/agents"

const handler = mortem.langchainHandler()

const agent = await createOpenAIToolsAgent({
  llm: new ChatOpenAI({ model: "gpt-4o" }),
  tools,
  prompt,
})

const executor = AgentExecutor.fromAgentAndTools({
  agent,
  tools,
  callbacks: [handler],
})

await session.run(async () => {
  await executor.invoke({ input: "Should I open a JUP position?" })
})
```

If you need a handler that subclasses `BaseCallbackHandler` for compatibility with stricter LangChain type checks, use the async factory instead:

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

const handler = await createLangChainHandlerAsync()
```

`createLangChainHandlerAsync` dynamically imports `@langchain/core/callbacks/base` at runtime and returns a proper subclass when it is available, falling back to the structural handler if LangChain is not installed.

## Solana web3.js Connection

Wrap your Solana `Connection` to record `sendRawTransaction` and `sendTransaction` calls as `solana_tx` events. The wrapper also polls for confirmation in the background and updates the event with the confirmation status.

```ts theme={null}
import { Connection } from "@solana/web3.js"

const connection = mortem.wrapConnection(
  new Connection("https://api.devnet.solana.com", "confirmed")
)

// Inside session.run(), every transaction send is recorded automatically
const signature = await connection.sendTransaction(transaction, signers)
```

The wrapper detects the cluster from the RPC URL (`mainnet`, `devnet`, or `localnet`) and records instruction names from `sendTransaction` calls where the transaction object exposes them.

## Wrapper method reference

| Method                            | Patches                                           | Event type emitted      |
| --------------------------------- | ------------------------------------------------- | ----------------------- |
| `mortem.wrapOpenAI(client)`       | `client.chat.completions.create`                  | `llm_call`              |
| `mortem.wrapAnthropic(client)`    | `client.messages.create`                          | `llm_call`              |
| `mortem.wrapOllama(client)`       | `client.chat`                                     | `llm_call`              |
| `mortem.wrapTools(tools)`         | Each tool's `execute` function                    | `tool_call`             |
| `mortem.wrapLanguageModel(model)` | `model.doGenerate`, `model.doStream`              | `llm_call`              |
| `mortem.langchainHandler()`       | LangChain callback lifecycle                      | `llm_call`, `tool_call` |
| `mortem.wrapConnection(conn)`     | `conn.sendRawTransaction`, `conn.sendTransaction` | `solana_tx`             |

Each wrapper is idempotent — calling it more than once on the same client returns the already-patched client without double-wrapping.
