# LangChain Chat History and Memory | DialogueDB

> A drop-in chat history and memory for LangChain chains and agents. Persistent messages, cross-session memory, and semantic search, no vector database to run.

Canonical: https://dialoguedb.com/integrations/langchain

LangChain Integration

# Drop-in chat history and memory for LangChain

A managed chat history for LangChain chains and agents. Long-term user memory and [semantic search](https://dialoguedb.com/architecture/search) included, [no vector database to run](https://dialoguedb.com/architecture/auto-vectorization).

[Get Your Free API Key](https://app.dialoguedb.com/signup) [See Example Code](https://github.com/dialoguedb/examples/tree/main/langchain)

`BufferMemory`

`ConversationSummaryMemory`

`RunnableWithMessageHistory`

implements BaseListChatMessageHistory

DialogueChatHistory

Backed by DialogueDB

Messages

Memory

Search

Where the default chat history stops

## The default chat history is scoped to a single process

Every LangChain memory class delegates to a chat history object. The default, InMemoryChatMessageHistory, keeps messages in an in-process array and drops them on restart. Fine for local development, and short on the guarantees a chatbot needs when it runs on serverless, in a container, or across multiple worker instances.

How the default is built

- Every message lives in a plain array on one instance
- One chain instance per session, no shared state model
- Message history only, nothing for facts or preferences
- Retrieval walks the array, no semantic query

Where it hits the wall

- Restarts, deploys, and serverless cold starts wipe every user
- Multi-instance workers don't see each other's history
- Nothing to carry facts about a user across sessions
- "What did we discuss about X" needs a separate search stack

What DialogueDB adds

## A managed chat history for LangChain, plus three things the defaults leave out

The chat history class implements the interface every LangChain memory surface already accepts, so nothing about the chain changes. DialogueDB then covers the three concerns the defaults leave for the developer to solve.

### Chat history that survives every restart

Every message from every user persists to DialogueDB. Chains and agents keep their context across deploys, cold starts, and multi-instance workers.

### Namespaces for multi-user chains

Pass a namespace on every read and write. Conversations, memory, and search stay isolated per user, tenant, or workspace at the data layer.

### Memory that carries across sessions

Store facts and preferences the assistant should remember beyond a single conversation. Retrieve them on the next run to seed the system prompt.

### Semantic search over past runs

Query past messages and memory by meaning, not keyword. Inject into a prompt or expose as a tool the agent can call, no vector database to run.

The integration pattern

## Add DialogueChatHistory to the memory constructor, and the chain runs exactly as it did before

One line in the constructor persists every message the chain touches. Two optional calls around the run cover long-term memory and semantic search, and nothing else about the chain code has to change.

1 Construct DialogueChatHistory One instance per session, keyed by a dialogue id you pass (created on the first turn, resumed on every one after). The namespace, a user or tenant id, isolates each user's history at the data layer. 2 Wire it into the memory class Pass it as chatHistory to BufferMemory (or getMessageHistory in RunnableWithMessageHistory). 3 Search memory (optional) Before the run, call db.searchMemories with the user input. Inject the top matches into the system prompt. 4 Run the chain Nothing about the chain code changes. Every read and write of chat history flows through DialogueChatHistory into DialogueDB. 5 Save new facts (optional) After the run, if the assistant learned something durable, call db.createMemory so the next session starts with it in scope.

The pattern chain-run.ts

```
const db = new DialogueDB()

// 1. Construct DialogueChatHistory
const history = new DialogueChatHistory({
  dialogueId: sessionId,
  namespace: userId,
})

// 2. Wire it into the memory class
const memory = new BufferMemory({
  chatHistory: history,
  returnMessages: true,
})

// 3. Search memory (optional)
const facts = await db.searchMemories(
  userInput, { namespace: userId }
)

// 4. Run the chain
const chain = new ConversationChain({ llm, memory })
const result = await chain.call({ input: userInput })

// 5. Save new facts (optional)
await db.createMemory({ value: newFact, namespace: userId })
```

## What this unlocks in your LangChain app

Six patterns the integration makes shorter to build.

### Chatbots that survive every deploy

A chain that talked to a user yesterday picks up the same conversation today, even if the process restarted in between.

`DialogueChatHistory`

### Support agents that carry ticket history

Every past conversation about a customer is a load call away. The agent picks up mid-thread without you re-hydrating context.

`dialogueId`

### Onboarding chains with real user profiles

As the assistant learns preferences and answers, store them as Memory objects. The next session starts with a profile, not a blank slate.

`db.createMemory()`

### Agent conversations that pick up where they stopped

On restart, the agent reloads every prior message from DialogueDB and continues with full context of what the last run said and answered.

`DialogueChatHistory`

### Multi-tenant SaaS with per-user isolation

One codebase, many customers, each with their own namespace. No cross-tenant leakage, no WHERE clauses in your chain code.

`namespace: userId`

### Recall past runs by meaning

Search over every past conversation, scoped to a namespace. Answer "what did we discuss about X" even when the reader phrased it differently.

`db.searchMessages()`

## Install in 3 steps

From npm install to the first message that survives a restart, in under five minutes.

1

### Install the client

```
npm install dialogue-db
```

2

### Set the API key

```
DIALOGUE_DB_API_KEY=...
```

3

### Drop in the chat history

```
new BufferMemory({
  chatHistory: new DialogueChatHistory({
    dialogueId: sessionId,
    namespace: userId,
  }),
})
```

Copy the class from the [examples repo](https://github.com/dialoguedb/examples/blob/main/langchain/src/lib/dialogue-history.ts).

[Get Your Free API Key](https://app.dialoguedb.com/signup)

No credit card. Free tier to start. Starter is [$29/month](https://dialoguedb.com/pricing) when usage grows.

## The reference example

A LangChain example repo with two runnable samples, cloneable and locally runnable in about a minute.

[hello-world.ts View source → A conversation chain that survives a cold restart A BufferMemory and ConversationChain with a DialogueDB-backed chat history. Runs a short exchange, restarts the process, and continues with the earlier context intact.](https://github.com/dialoguedb/examples/blob/main/langchain/src/hello-world.ts) [agent-with-tools.ts View source → A tool-calling agent that resumes on the next call A createToolCallingAgent with two example tools and a DialogueDB-backed chat history. Runs a multi-step query, restarts the process, and picks up a follow-up in the same thread.](https://github.com/dialoguedb/examples/blob/main/langchain/src/agent-with-tools.ts)

### Read the DialogueChatHistory class

The full implementation lives in the same example repo. Select a section to jump to its lines.

1 Implements the interface LangChain kept BaseListChatMessageHistory is the interface every LangChain memory class and RunnableWithMessageHistory reads from. Implementing it means every LangChain surface that accepts a chat history accepts this one. (The lc\_namespace array is LangChain's own serialization path, unrelated to DialogueDB namespaces.) 2 Keys the session by dialogue id The dialogue id and namespace enter through the constructor, and ensureDialogue calls getOrCreateDialogue, so passing a session id creates the dialogue on the first turn and resumes it on every one after. 3 Loads and converts messages getMessages fetches every stored message on the dialogue, then maps each role to HumanMessage, AIMessage, or SystemMessage so LangChain receives the exact types it expects. Structured content written by another SDK is serialized first, since LangChain reads a non-string argument as message fields rather than content. 4 Persists each turn as it happens addMessage writes to DialogueDB on every append. No batching, no local buffer, no lost writes if the process dies mid-turn.

[Clone the full example repo](https://github.com/dialoguedb/examples/tree/main/langchain)

src/lib/dialogue-history.ts

import { BaseListChatMessageHistory } from "@langchain/core/chat\_history"

import { BaseMessage, HumanMessage, AIMessage, SystemMessage } from "@langchain/core/messages"

import { DialogueDB, type Dialogue } from "dialogue-db"

export class DialogueChatHistory extends BaseListChatMessageHistory {

lc\_namespace = \[ "langchain", "stores", "message", "dialoguedb" \]

private db = new DialogueDB ()

private dialogue: Dialogue | null \= null

constructor ( private opts: { dialogueId: string; namespace: string }) { super () }

private async ensureDialogue () {

this.dialogue ??= await this.db.getOrCreateDialogue ({

id: this.opts.dialogueId, namespace: this.opts.namespace

})

return this.dialogue

}

async getMessages (): Promise<BaseMessage\[\]> {

const dialogue = await this.ensureDialogue ()

await dialogue.loadMessages ({ order: "asc" })

return dialogue.messages.map ((m) => {

const content = typeof m.content === "string" ? m.content : JSON.stringify (m.content)

switch (m.role) {

case "user": return new HumanMessage (content)

case "assistant": return new AIMessage (content)

case "system": return new SystemMessage (content)

default: return new HumanMessage (content)

}

})

}

async addMessage (message: BaseMessage): Promise<void\> {

const dialogue = await this.ensureDialogue ()

const role = toRole (message)

await dialogue.saveMessage ({ role, content: typeof message.content === "string" ? message.content : JSON.stringify (message.content) })

}

}

Condensed view. Full source at [github.com/dialoguedb/examples](https://github.com/dialoguedb/examples/blob/main/langchain/src/lib/dialogue-history.ts).

## Where DialogueDB fits

Options for LangChain chat history and cross-session memory, side by side.

| InMemoryChatMessageHistory LangChain default | Redis or SQL history @langchain/community classes | Standalone memory service Facts and preferences only | DialogueDB            |                          |
| -------------------------------------------- | ------------------------------------------------- | ---------------------------------------------------- | --------------------- | ------------------------ |
| Persists messages                            | No, in-process                                    | Yes                                                  | Facts only            | Yes, every message       |
| Multi-user isolation                         | None                                              | Bring your own                                       | By user ID            | Namespaces, first-class  |
| Long-term memory across sessions             | No                                                | No                                                   | Yes                   | Yes, Memory objects      |
| Semantic search over past runs               | No                                                | Build it yourself                                    | Facts only            | Every message and memory |
| Infra to run                                 | None (in-process)                                 | Redis or DB you host                                 | Their managed service | Managed                  |

## If you know LangChain, you already know DialogueDB

The concepts map one-to-one to what LangChain already exposes.

| LangChain                              | DialogueDB                                                   |
| -------------------------------------- | ------------------------------------------------------------ |
| BaseListChatMessageHistory             | DialogueChatHistory                                          |
| HumanMessage, AIMessage, SystemMessage | message with role                                            |
| Session identifier from your app       | dialogueId                                                   |
| User from your auth layer              | namespace                                                    |
| Tool call on an AIMessage              | message content and metadata (extend the class to serialize) |
| Facts the assistant should carry       | memory                                                       |

## Frequently asked questions

Does DialogueChatHistory work with RunnableWithMessageHistory?

Yes, DialogueChatHistory works with RunnableWithMessageHistory. Return a new DialogueChatHistory({ dialogueId: sessionId, namespace: userId }) from the getMessageHistory callback and the modern LCEL pattern persists to DialogueDB.

Does it work with BufferMemory and ConversationChain?

Yes, DialogueChatHistory works with BufferMemory and ConversationChain. Pass new DialogueChatHistory({ dialogueId: sessionId, namespace: userId }) as the chatHistory prop on BufferMemory (or ConversationSummaryMemory, ConversationTokenBufferMemory, and other classic memory classes) and everything keeps working with persistent storage.

What happens to tool calls and tool results?

The reference DialogueChatHistory class persists each message as a role and text content. For agent runs, that is typically enough context to resume the conversation and let the model plan the next tool call. If the app needs to replay structured tool\_call objects verbatim, extend the class to serialize the full BaseMessage into the DialogueDB content and metadata fields.

Can I use DialogueChatHistory inside a LangGraph node?

Yes, DialogueChatHistory works inside a LangGraph node. LangGraph nodes accept LangChain message history classes through the standard interface, so instantiate DialogueChatHistory inside the node and read or write to it exactly as you would in a chain.

How do I scope conversations per user with Clerk, Auth.js, or NextAuth?

Pass the user ID from your auth provider as the namespace on every read and write. Namespaces are the DialogueDB way of isolating one user, tenant, or workspace at the data layer, so scoping stays out of your chain code.

I already use a Redis-backed history class. How do I migrate?

Read your existing Redis conversation keys, group messages by session ID, and write each session as a DialogueDB dialogue with dialogue.saveMessages. Namespaces map to your user identifier. Once the backfill completes, swap the chatHistory field in your memory constructor and remove the Redis dependency.

Is there a Python version of the DialogueDB SDK?

No, DialogueDB currently offers a TypeScript client. LangChain.js is fully supported. A Python client is on the roadmap.

Where does DialogueDB not fit for LangChain?

DialogueDB stores conversational content: messages, memory values, and dialogue state. Large binary attachments, embeddings for your own document corpus, and RAG over private knowledge bases remain the job of LangChain retrievers and dedicated vector stores.

## One API for messages, memory, and search

Add it to your LangChain app in minutes.

[Get Your Free API Key](https://app.dialoguedb.com/signup) [See Example Code](https://github.com/dialoguedb/examples/tree/main/langchain)
