Skip to main content

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.

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.
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.

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

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.
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:
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.
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

MethodPatchesEvent type emitted
mortem.wrapOpenAI(client)client.chat.completions.createllm_call
mortem.wrapAnthropic(client)client.messages.createllm_call
mortem.wrapOllama(client)client.chatllm_call
mortem.wrapTools(tools)Each tool’s execute functiontool_call
mortem.wrapLanguageModel(model)model.doGenerate, model.doStreamllm_call
mortem.langchainHandler()LangChain callback lifecyclellm_call, tool_call
mortem.wrapConnection(conn)conn.sendRawTransaction, conn.sendTransactionsolana_tx
Each wrapper is idempotent — calling it more than once on the same client returns the already-patched client without double-wrapping.