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

# Add Mortem observability tracing to LangChain agents

> Attach a Mortem callback handler to any LangChain chain or agent to capture LLM calls and tool invocations as structured trace events.

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

<Steps>
  <Step title="Initialize the Mortem client">
    Create a `Mortem` instance at module scope.

    ```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="Create the callback handler">
    Call `mortem.langchainHandler()` to get a `MortemCallbackHandler` instance. This is a synchronous call that returns the handler immediately.

    ```ts theme={null}
    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:

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

    const handler = await createLangChainHandlerAsync()
    ```

    <Tip>
      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.
    </Tip>
  </Step>

  <Step title="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.

    <CodeGroup>
      ```ts Chain invocation theme={null}
      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] }
      )
      ```

      ```ts LCEL chain theme={null}
      import { ChatOpenAI } from "@langchain/openai"
      import { PromptTemplate } from "@langchain/core/prompts"
      import { StringOutputParser } from "@langchain/core/output_parsers"

      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 response = await chain.invoke(
        { question: "Should I swap 1 SOL for JUP right now?" },
        { callbacks: [handler] }
      )
      ```
    </CodeGroup>
  </Step>

  <Step title="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.

    ```ts theme={null}
    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()
    }
    ```

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

| Method                          | Description                                                                                                                                                                                                                      |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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. |

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

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

```ts theme={null}
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?" })
)
```
