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.

Mortem integrates with LangChain through a callback handler. You attach the handler to your chain or agent, and Mortem automatically records each LLM call and tool invocation as a trace event. No changes to your prompt templates, chains, or tool definitions are required.

What gets captured

The MortemCallbackHandler intercepts LangChain lifecycle events and records:
  • LLM calls — model name, prompts passed to the LLM, and the full output
  • Tool calls — tool name, input string, and output string
  • Errors — failures on any LLM or tool step are recorded as failed events on the trace

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. LangChain (langchain and @langchain/core) must be installed separately — Mortem does not depend on them.

Integration

1

Initialize the Mortem client

Create a Mortem instance at module scope.
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",
})
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.
2

Create the callback handler

Call mortem.langchainHandler() to get a MortemCallbackHandler instance. This is a synchronous call that returns the handler immediately.
const handler = mortem.langchainHandler()
Alternatively, import createLangChainHandlerAsync directly from the SDK if you want a handler that dynamically subclasses BaseCallbackHandler from @langchain/core. This is useful when you need the handler to satisfy strict instanceof checks in framework code:
import { createLangChainHandlerAsync } from "@mortemlabs/sdk"

const handler = await createLangChainHandlerAsync()
Use createLangChainHandlerAsync when passing the handler to LangChain APIs that require an instance of BaseCallbackHandler. The synchronous mortem.langchainHandler() works for most use cases where duck-typing is sufficient.
3

Attach the handler to your chain or agent

Pass the handler in the callbacks array when invoking your chain. You can add it at the chain level, the model level, or at the point of invocation — LangChain propagates callbacks through the call tree.
import { ChatOpenAI } from "@langchain/openai"
import { HumanMessage } from "@langchain/core/messages"

const model = new ChatOpenAI({ model: "gpt-4o" })

const response = await model.invoke(
  [new HumanMessage("Should I swap 1 SOL for JUP right now?")],
  { callbacks: [handler] }
)
4

Wrap the invocation in a Mortem session

Create a session with mortem.startSession and run the chain inside session.run. This links the callback handler’s events to the active trace.
const session = await mortem.startSession({
  inputSummary: "Evaluate trade setup and produce a recommendation",
  tags: ["langchain", "devnet"],
})

try {
  const result = await session.run(async () => {
    return chain.invoke(
      { question: "Should I swap 1 SOL for JUP right now?" },
      { callbacks: [handler] }
    )
  })

  await session.complete(result)
} catch (error) {
  await session.fail(error)
} finally {
  await mortem.close()
}
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.

Complete example

import { Mortem, createLangChainHandlerAsync } from "@mortemlabs/sdk"
import { ChatOpenAI } from "@langchain/openai"
import { PromptTemplate } from "@langchain/core/prompts"
import { StringOutputParser } from "@langchain/core/output_parsers"

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 handler = await createLangChainHandlerAsync()

const prompt = PromptTemplate.fromTemplate(
  "You are a Solana trading bot. {question}"
)
const model = new ChatOpenAI({ model: "gpt-4o" })
const parser = new StringOutputParser()

const chain = prompt.pipe(model).pipe(parser)

const session = await mortem.startSession({
  inputSummary: "Evaluate trade setup and produce a recommendation",
  tags: ["langchain", "devnet"],
})

try {
  const result = await session.run(async () => {
    return chain.invoke(
      { question: "Should I swap 1 SOL for JUP right now?" },
      { callbacks: [handler] }
    )
  })

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

Handler variants

The SDK exports three ways to create a LangChain callback handler:
MethodDescription
mortem.langchainHandler()Synchronous. Returns a MortemCallbackHandler instance immediately. Works via duck-typing for most LangChain APIs.
createLangChainHandler()Same as above, exported directly from @mortemlabs/sdk. Use this if you import the function directly rather than through the Mortem client.
createLangChainHandlerAsync()Async. Dynamically imports @langchain/core/callbacks/base and subclasses BaseCallbackHandler. Use when strict instanceof checks are required. Falls back to the synchronous handler if @langchain/core is not installed.
You can import createLangChainHandler and createLangChainHandlerAsync directly from @mortemlabs/sdk without a Mortem client instance. This is useful if you manage the handler lifecycle separately from the client.

Agent callbacks

For LangChain agents, pass the handler in the callbacks array at the agent executor level. LangChain propagates the handler to all nested chains and tools automatically:
import { AgentExecutor } from "langchain/agents"

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

const result = await session.run(() =>
  executor.invoke({ input: "Should I swap 1 SOL for JUP?" })
)