For longer-term persistence across chat sessions, you can swap out the default in-memory chatHistory for the serverless PostgreSQL-compatible Amazon Aurora DSQL Database. This is very similar to the PostgreSQL integration with a few differences to make it compatible with DSQL:
  1. The id column in PostgreSQL is SERIAL auto-incrementent, and DSQL is UUID using the database function gen_random_uuid.
  2. A created_at column is created to track the order and history of the messages.
  3. The message column in PostgreSQL is JSONB, and DSQL is TEXT with Javascript parsing handling

Setup

Go to you AWS Console and create an Aurora DSQL Cluster, https://console.aws.amazon.com/dsql/clusters
npm
npm install @langchain/openai @langchain/community @langchain/core pg @aws-sdk/dsql-signer

Usage

Each chat history session is stored in a Aurora DSQL (Postgres-compatible) database and requires a session id. The connection to Aurora DSQL is handled through a PostgreSQL pool. You can either pass an instance of a pool via the pool parameter or pass a pool config via the poolConfig parameter. See pg-node docs on pools for more information. A provided pool takes precedence, thus if both a pool instance and a pool config are passed, only the pool will be used. For options on how to do the authentication and authorization for DSQL please check https://docs.aws.amazon.com/aurora-dsql/latest/userguide/authentication-authorization.html. The following example uses the AWS-SDK to generate an authentication token that is passed to the pool configuration:
import pg from "pg";

import { DsqlSigner } from "@aws-sdk/dsql-signer";
import { AuroraDsqlChatMessageHistory } from "@langchain/community/stores/message/aurora_dsql";
import { ChatOpenAI } from "@langchain/openai";
import { RunnableWithMessageHistory } from "@langchain/core/runnables";

import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";

async function getPostgresqlPool() {
  const signer = new DsqlSigner({
    hostname: process.env.DSQL_ENDPOINT!,
  });

  const token = await signer.getDbConnectAdminAuthToken();

  if (!token) throw new Error("Auth token error for DSQL");

  const poolConfig: pg.PoolConfig = {
    host: process.env.DSQL_ENDPOINT,
    port: 5432,
    user: "admin",
    password: token,
    ssl: true,
    database: "postgres",
  };

  const pool = new pg.Pool(poolConfig);
  return pool;
}

const pool = await getPostgresqlPool();

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

const prompt = ChatPromptTemplate.fromMessages([
  [
    "system",
    "You are a helpful assistant. Answer all questions to the best of your ability.",
  ],
  new MessagesPlaceholder("chat_history"),
  ["human", "{input}"],
]);

const chain = prompt.pipe(model).pipe(new StringOutputParser());

const chainWithHistory = new RunnableWithMessageHistory({
  runnable: chain,
  inputMessagesKey: "input",
  historyMessagesKey: "chat_history",
  getMessageHistory: async (sessionId) => {
    const chatHistory = new AuroraDsqlChatMessageHistory({
      sessionId,
      pool,
      // Can also pass `poolConfig` to initialize the pool internally,
      // but easier to call `.end()` at the end later.
    });
    return chatHistory;
  },
});

const res1 = await chainWithHistory.invoke(
  {
    input: "Hi! I'm MJDeligan.",
  },
  { configurable: { sessionId: "langchain-test-session" } }
);
console.log(res1);
/*
  "Hello MJDeligan! It's nice to meet you. My name is AI. How may I assist you today?"
*/

const res2 = await chainWithHistory.invoke(
  { input: "What did I just say my name was?" },
  { configurable: { sessionId: "langchain-test-session" } }
);
console.log(res2);

/*
  "You said your name was MJDeligan."
*/

// If you provided a pool config you should close the created pool when you are done
await pool.end();