Back to roadmaps langchain Course

Managing Conversational Memory and Persistence

To build interactive chatbots, your application must store previous message exchanges. Let us explore how LangChain manages history memory and integrates with Redis for database persistence.


1. Using ChatMessageHistory In-Memory

For simple applications or testing, keep messages in server RAM memory:

import { ChatMessageHistory } from "langchain/stores/message/in_memory";

const historyStore = new ChatMessageHistory();

// Append messages
await historyStore.addUserMessage("Hi, I am Mike.");
await historyStore.addAIChatMessage("Hello Mike! How can I help you?");

// Retrieve all messages
const messagesHistoryList = await historyStore.getMessages();

2. Persisting Session History in Redis

In production server environments, server instances are stateless, meaning memory is cleared between requests. To maintain state, save histories in a Redis database store:

First, install the Redis adapter dependency:

# Install the redis integration store package
npm install @langchain/community

Initialize your Redis connection and bind it to LangChain:

// src/services/redisHistory.ts
import { RedisChatMessageHistory } from "@langchain/community/stores/message/redis";
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate, MessagePlaceholder } from "@langchain/core/prompts";
import { RunnableWithMessageHistory } from "@langchain/core/runnables";

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

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "You are an AI assistant."],
  new MessagePlaceholder("chatHistory"),
  ["user", "{userInput}"]
]);

const baseChain = prompt.pipe(model);

// Wrap the chain with message history handlers
export const sessionChain = new RunnableWithMessageHistory({
  runnable: baseChain,
  
  // 1. Tell LangChain how to lookup the message store based on sessionId
  getMessageHistory: async (sessionId: string) => {
    return new RedisChatMessageHistory({
      sessionId: sessionId,
      sessionTTL: 3600, // Expire session after 1 hour of inactivity
      config: {
        url: process.env.REDIS_URL || "redis://localhost:6379",
      },
    });
  },
  
  // Define prompt configuration matching variables keys
  inputMessagesKey: "userInput",
  historyMessagesKey: "chatHistory",
});

export async function continueChatSession(userMessage: string, userSessionId: string) {
  const result = await sessionChain.invoke(
    { userInput: userMessage },
    // 2. Supply the session identifier inside the config block
    { configurable: { sessionId: userSessionId } }
  );

  return result.content;
}

3. Storage Cleanup Strategy

  • Always Set TTLs: Memory sessions in Redis should always include a Time-To-Live (TTL) expiration value to prevent database storage exhaustion.
  • Pruning Messages: For long sessions, create a custom helper to prune older messages from Redis, leaving only the most recent messages to fit within context limits.
Published on Last updated: