Skip to main content

Custom chat history

To create your own custom chat history class for a backing store, you can extend the BaseListChatMessageHistory class. This requires you to implement the following methods:

  • addMessage, which adds a BaseMessage to the store for the current session. This usually involves serializing them into a simple object representation (defined as StoredMessage below) that the backing store can handle.
  • getMessages, which loads messages for a session and returns them as an array of BaseMessages. For most databases, this involves deserializing stored messages into BaseMessages.

In addition, there are some optional methods that are nice to override:

  • clear, which removes all messages from the store.
  • addMessages, which will add multiple messages at a time to the current session. This can save round-trips to and from the backing store if many messages are being saved at once. The default implementation will call addMessage once per input message.

Here’s an example that stores messages in-memory. For long-term persistence, you should use a real database. You’ll notice we use the mapChatMessagesToStoredMessages and mapStoredMessagesToChatMessages helper methods for consistent serialization and deserialization:

import { BaseListChatMessageHistory } from "@langchain/core/chat_history";
import {
BaseMessage,
StoredMessage,
mapChatMessagesToStoredMessages,
mapStoredMessagesToChatMessages,
} from "@langchain/core/messages";

// Not required, but usually chat message histories will handle multiple sessions
// for different users, and should take some kind of sessionId as input.
export interface CustomChatMessageHistoryInput {
sessionId: string;
}

export class CustomChatMessageHistory extends BaseListChatMessageHistory {
lc_namespace = ["langchain", "stores", "message"];

sessionId: string;

// Simulate a real database layer. Stores serialized objects.
fakeDatabase: Record<string, StoredMessage[]> = {};

constructor(fields: CustomChatMessageHistoryInput) {
super(fields);
this.sessionId = fields.sessionId;
}

async getMessages(): Promise<BaseMessage[]> {
const messages = this.fakeDatabase[this.sessionId] ?? [];
return mapStoredMessagesToChatMessages(messages);
}

async addMessage(message: BaseMessage): Promise<void> {
if (this.fakeDatabase[this.sessionId] === undefined) {
this.fakeDatabase[this.sessionId] = [];
}
const serializedMessages = mapChatMessagesToStoredMessages([message]);
this.fakeDatabase[this.sessionId].push(serializedMessages[0]);
}

async addMessages(messages: BaseMessage[]): Promise<void> {
if (this.fakeDatabase[this.sessionId] === undefined) {
this.fakeDatabase[this.sessionId] = [];
}
const existingMessages = this.fakeDatabase[this.sessionId];
const serializedMessages = mapChatMessagesToStoredMessages(messages);
this.fakeDatabase[this.sessionId] =
existingMessages.concat(serializedMessages);
}

async clear(): Promise<void> {
delete this.fakeDatabase[this.sessionId];
}
}

You can then use this chat history as usual:

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

const chatHistory = new CustomChatMessageHistory({ sessionId: "test" });

await chatHistory.addMessages([
new HumanMessage("Hello there!"),
new AIMessage("Hello to you too!"),
]);

await chatHistory.getMessages();
[
HumanMessage {
lc_serializable: true,
lc_kwargs: { content: "Hello there!", additional_kwargs: {} },
lc_namespace: [ "langchain_core", "messages" ],
content: "Hello there!",
name: undefined,
additional_kwargs: {}
},
AIMessage {
lc_serializable: true,
lc_kwargs: { content: "Hello to you too!", additional_kwargs: {} },
lc_namespace: [ "langchain_core", "messages" ],
content: "Hello to you too!",
name: undefined,
additional_kwargs: {}
}
]

Help us out by providing feedback on this documentation page: