diff --git a/docs/core_docs/docs/integrations/memory/azure_cosmosdb_nosql.mdx b/docs/core_docs/docs/integrations/memory/azure_cosmosdb_nosql.mdx
new file mode 100644
index 000000000000..f70bbf15df73
--- /dev/null
+++ b/docs/core_docs/docs/integrations/memory/azure_cosmosdb_nosql.mdx
@@ -0,0 +1,42 @@
+---
+hide_table_of_contents: true
+---
+
+import CodeBlock from "@theme/CodeBlock";
+
+# Azure Cosmos DB NoSQL Chat Message History
+
+The AzureCosmosDBNoSQLChatMessageHistory uses Cosmos DB to store chat message history. For longer-term persistence across chat sessions, you can swap out the default in-memory `chatHistory` that backs chat memory classes like `BufferMemory`.
+If you don't have an Azure account, you can [create a free account](https://azure.microsoft.com/free/) to get started.
+
+## Setup
+
+You'll first need to install the [`@langchain/azure-cosmosdb`](https://www.npmjs.com/package/@langchain/azure-cosmosdb) package:
+
+```bash npm2yarn
+npm install @langchain/azure-cosmosdb @langchain/core
+```
+
+import IntegrationInstallTooltip from "@mdx_components/integration_install_tooltip.mdx";
+
+
+
+```bash npm2yarn
+npm install @langchain/openai @langchain/community @langchain/core
+```
+
+You'll also need to have an Azure Cosmos DB for NoSQL instance running. You can deploy a free version on Azure Portal without any cost, following [this guide](https://learn.microsoft.com/azure/cosmos-db/nosql/quickstart-portal).
+
+Once you have your instance running, make sure you have the connection string. If you are using Managed Identity, you need to have the endpoint. You can find them in the Azure Portal, under the "Settings / Keys" section of your instance.
+
+:::info
+
+When using Azure Managed Identity and role-based access control, you must ensure that the database and container have been created beforehand. RBAC does not provide permissions to create databases and containers. You can get more information about the permission model in the [Azure Cosmos DB documentation](https://learn.microsoft.com/azure/cosmos-db/how-to-setup-rbac#permission-model).
+
+:::
+
+## Usage
+
+import Example from "@examples/memory/azure_cosmosdb_nosql.ts";
+
+{Example}
diff --git a/docs/core_docs/docs/integrations/platforms/microsoft.mdx b/docs/core_docs/docs/integrations/platforms/microsoft.mdx
index e9f3d7fd4922..d3d9a6416f83 100644
--- a/docs/core_docs/docs/integrations/platforms/microsoft.mdx
+++ b/docs/core_docs/docs/integrations/platforms/microsoft.mdx
@@ -150,6 +150,22 @@ See a [usage example](/docs/integrations/llm_caching/azure_cosmosdb_nosql).
import { AzureCosmosDBNoSQLSemanticCache } from "@langchain/azure-cosmosdb";
```
+## Chat Message History
+
+### Azure Cosmos DB NoSQL Chat Message History
+
+> The AzureCosmosDBNoSQLChatMessageHistory uses Cosmos DB to store chat message history. For longer-term persistence across chat sessions, you can swap out the default in-memory `chatHistory` that backs chat memory classes like `BufferMemory`.
+
+```bash npm2yarn
+npm install @langchain/azure-cosmosdb @langchain/core
+```
+
+See [usage example](/docs/integrations/memory/azure_cosmosdb_nosql.mdx).
+
+```typescript
+import { AzureCosmosDBNoSQLChatMessageHistory } from "@langchain/azure-cosmosdb";
+```
+
## Document loaders
### Azure Blob Storage
diff --git a/examples/src/memory/azure_cosmosdb_nosql.ts b/examples/src/memory/azure_cosmosdb_nosql.ts
new file mode 100644
index 000000000000..2f3cddf4460f
--- /dev/null
+++ b/examples/src/memory/azure_cosmosdb_nosql.ts
@@ -0,0 +1,58 @@
+import { ChatOpenAI } from "@langchain/openai";
+import { AzureCosmsosDBNoSQLChatMessageHistory } from "@langchain/azure-cosmosdb";
+import { RunnableWithMessageHistory } from "@langchain/core/runnables";
+import { StringOutputParser } from "@langchain/core/output_parsers";
+import {
+ ChatPromptTemplate,
+ MessagesPlaceholder,
+} from "@langchain/core/prompts";
+
+const model = new ChatOpenAI({
+ model: "gpt-3.5-turbo",
+ temperature: 0,
+});
+
+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 AzureCosmsosDBNoSQLChatMessageHistory({
+ sessionId,
+ userId: "user-id",
+ databaseName: "DATABASE_NAME",
+ containerName: "CONTAINER_NAME",
+ });
+ return chatHistory;
+ },
+});
+
+const res1 = await chainWithHistory.invoke(
+ { input: "Hi! I'm Jim." },
+ { configurable: { sessionId: "langchain-test-session" } }
+);
+console.log({ res1 });
+/*
+{ res1: 'Hi Jim! How can 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 });
+
+/*
+ { res2: { response: 'You said your name was Jim.' }
+ */
diff --git a/libs/langchain-azure-cosmosdb/src/chat_histories.ts b/libs/langchain-azure-cosmosdb/src/chat_histories.ts
new file mode 100644
index 000000000000..033acc521334
--- /dev/null
+++ b/libs/langchain-azure-cosmosdb/src/chat_histories.ts
@@ -0,0 +1,204 @@
+import { Container, CosmosClient, CosmosClientOptions } from "@azure/cosmos";
+import { DefaultAzureCredential, TokenCredential } from "@azure/identity";
+import { BaseListChatMessageHistory } from "@langchain/core/chat_history";
+import {
+ BaseMessage,
+ mapChatMessagesToStoredMessages,
+ mapStoredMessagesToChatMessages,
+} from "@langchain/core/messages";
+import { getEnvironmentVariable } from "@langchain/core/utils/env";
+
+const USER_AGENT_SUFFIX = "langchainjs-cdbnosql-chathistory-javascript";
+const DEFAULT_DATABASE_NAME = "chatHistoryDB";
+const DEFAULT_CONTAINER_NAME = "chatHistoryContainer";
+
+/**
+ * Type for the input to the `AzureCosmosDBNoSQLChatMessageHistory` constructor.
+ */
+export interface AzureCosmosDBNoSQLChatMessageHistoryInput {
+ sessionId: string;
+ userId?: string;
+ client?: CosmosClient;
+ connectionString?: string;
+ endpoint?: string;
+ databaseName?: string;
+ containerName?: string;
+ credentials?: TokenCredential;
+ ttl?: number;
+}
+
+/**
+ * Class for storing chat message history with Cosmos DB NoSQL. It extends the
+ * BaseListChatMessageHistory class and provides methods to get, add, and
+ * clear messages.
+ *
+ * @example
+ * ```typescript
+ * const model = new ChatOpenAI({
+ * model: "gpt-3.5-turbo",
+ * temperature: 0,
+ * });
+ * 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 AzureCosmsosDBNoSQLChatMessageHistory({
+ * sessionId: sessionId,
+ * userId: "user-id",
+ * databaseName: "DATABASE_NAME",
+ * containerName: "CONTAINER_NAME",
+ * })
+ * return chatHistory;
+ * },
+ * });
+ * await chainWithHistory.invoke(
+ * { input: "What did I just say my name was?" },
+ * { configurable: { sessionId: "session-id" } }
+ * );
+ * ```
+ */
+
+export class AzureCosmsosDBNoSQLChatMessageHistory extends BaseListChatMessageHistory {
+ lc_namespace = ["langchain", "stores", "message", "azurecosmosdb"];
+
+ private container: Container;
+
+ private sessionId: string;
+
+ private databaseName: string;
+
+ private containerName: string;
+
+ private client: CosmosClient;
+
+ private userId: string;
+
+ private ttl: number | undefined;
+
+ private messageList: BaseMessage[] = [];
+
+ private initPromise?: Promise;
+
+ constructor(chatHistoryInput: AzureCosmosDBNoSQLChatMessageHistoryInput) {
+ super();
+
+ this.sessionId = chatHistoryInput.sessionId;
+ this.databaseName = chatHistoryInput.databaseName ?? DEFAULT_DATABASE_NAME;
+ this.containerName =
+ chatHistoryInput.containerName ?? DEFAULT_CONTAINER_NAME;
+ this.userId = chatHistoryInput.userId ?? "anonymous";
+ this.ttl = chatHistoryInput.ttl;
+ this.client = this.initializeClient(chatHistoryInput);
+ }
+
+ private initializeClient(
+ input: AzureCosmosDBNoSQLChatMessageHistoryInput
+ ): CosmosClient {
+ const connectionString =
+ input.connectionString ??
+ getEnvironmentVariable("AZURE_COSMOSDB_NOSQL_CONNECTION_STRING");
+ const endpoint =
+ input.endpoint ?? getEnvironmentVariable("AZURE_COSMOSDB_NOSQL_ENDPOINT");
+
+ if (!input.client && !connectionString && !endpoint) {
+ throw new Error(
+ "CosmosClient, connection string, or endpoint must be provided."
+ );
+ }
+
+ if (input.client) {
+ return input.client;
+ }
+
+ if (connectionString) {
+ const [endpointPart, keyPart] = connectionString.split(";");
+ const endpoint = endpointPart.split("=")[1];
+ const key = keyPart.split("=")[1];
+
+ return new CosmosClient({
+ endpoint,
+ key,
+ userAgentSuffix: USER_AGENT_SUFFIX,
+ });
+ } else {
+ return new CosmosClient({
+ endpoint,
+ aadCredentials: input.credentials ?? new DefaultAzureCredential(),
+ userAgentSuffix: USER_AGENT_SUFFIX,
+ } as CosmosClientOptions);
+ }
+ }
+
+ private async initializeContainer(): Promise {
+ if (!this.initPromise) {
+ this.initPromise = (async () => {
+ const { database } = await this.client.databases.createIfNotExists({
+ id: this.databaseName,
+ });
+ const { container } = await database.containers.createIfNotExists({
+ id: this.containerName,
+ partitionKey: "/userId",
+ defaultTtl: this.ttl,
+ });
+ this.container = container;
+ })().catch((error) => {
+ console.error("Error initializing Cosmos DB container:", error);
+ throw error;
+ });
+ }
+ return this.initPromise;
+ }
+
+ async getMessages(): Promise {
+ await this.initializeContainer();
+ const document = await this.container
+ .item(this.sessionId, this.userId)
+ .read();
+ const messages = document.resource?.messages || [];
+ this.messageList = mapStoredMessagesToChatMessages(messages);
+ return this.messageList;
+ }
+
+ async addMessage(message: BaseMessage): Promise {
+ await this.initializeContainer();
+ this.messageList = await this.getMessages();
+ this.messageList.push(message);
+ const messages = mapChatMessagesToStoredMessages(this.messageList);
+ await this.container.items.upsert({
+ id: this.sessionId,
+ userId: this.userId,
+ messages,
+ });
+ }
+
+ async clear(): Promise {
+ this.messageList = [];
+ await this.initializeContainer();
+ await this.container.item(this.sessionId, this.userId).delete();
+ }
+
+ async clearAllSessionsForUser(userId: string) {
+ await this.initializeContainer();
+ const query = {
+ query: "SELECT c.id FROM c WHERE c.userId = @userId",
+ parameters: [{ name: "@userId", value: userId }],
+ };
+ const { resources: userSessions } = await this.container.items
+ .query(query)
+ .fetchAll();
+ for (const userSession of userSessions) {
+ await this.container.item(userSession.id, userId).delete();
+ }
+ }
+}
diff --git a/libs/langchain-azure-cosmosdb/src/index.ts b/libs/langchain-azure-cosmosdb/src/index.ts
index e1160c548ef9..c5160397b474 100644
--- a/libs/langchain-azure-cosmosdb/src/index.ts
+++ b/libs/langchain-azure-cosmosdb/src/index.ts
@@ -1,3 +1,4 @@
export * from "./azure_cosmosdb_mongodb.js";
export * from "./azure_cosmosdb_nosql.js";
export * from "./caches.js";
+export * from "./chat_histories.js";
diff --git a/libs/langchain-azure-cosmosdb/src/tests/chat_histories.int.test.ts b/libs/langchain-azure-cosmosdb/src/tests/chat_histories.int.test.ts
new file mode 100644
index 000000000000..81f2070ceb81
--- /dev/null
+++ b/libs/langchain-azure-cosmosdb/src/tests/chat_histories.int.test.ts
@@ -0,0 +1,168 @@
+/* eslint-disable no-promise-executor-return */
+/* eslint-disable no-process-env */
+
+import { expect } from "@jest/globals";
+import { HumanMessage, AIMessage } from "@langchain/core/messages";
+import { CosmosClient } from "@azure/cosmos";
+import { DefaultAzureCredential } from "@azure/identity";
+import { ObjectId } from "mongodb";
+import { AzureCosmsosDBNoSQLChatMessageHistory } from "../chat_histories.js";
+
+const DATABASE_NAME = "langchainTestDB";
+const CONTAINER_NAME = "testContainer";
+
+/*
+ * To run this test, you need have an Azure Cosmos DB for NoSQL instance
+ * running. You can deploy a free version on Azure Portal without any cost,
+ * following this guide:
+ * https://learn.microsoft.com/azure/cosmos-db/nosql/vector-search
+ *
+ * You do not need to create a database or collection, it will be created
+ * automatically by the test.
+ *
+ * Once you have the instance running, you need to set the following environment
+ * variables before running the test:
+ * - AZURE_COSMOSDB_NOSQL_CONNECTION_STRING or AZURE_COSMOSDB_NOSQL_ENDPOINT
+ */
+beforeEach(async () => {
+ let client: CosmosClient;
+
+ if (process.env.AZURE_COSMOSDB_NOSQL_CONNECTION_STRING) {
+ client = new CosmosClient(
+ process.env.AZURE_COSMOSDB_NOSQL_CONNECTION_STRING
+ );
+ } else if (process.env.AZURE_COSMOSDB_NOSQL_ENDPOINT) {
+ client = new CosmosClient({
+ endpoint: process.env.AZURE_COSMOSDB_NOSQL_ENDPOINT,
+ aadCredentials: new DefaultAzureCredential(),
+ });
+ } else {
+ throw new Error(
+ "Please set the environment variable AZURE_COSMOSDB_NOSQL_CONNECTION_STRING or AZURE_COSMOSDB_NOSQL_ENDPOINT"
+ );
+ }
+ try {
+ await client.database(DATABASE_NAME).delete();
+ } catch {
+ // Ignore error if the database does not exist
+ }
+ try {
+ await client.database("DbWithTTL").delete();
+ } catch {
+ // Ignore error if the database does not exist
+ }
+});
+
+test("Test CosmosDB History Store", async () => {
+ const input = {
+ sessionId: new ObjectId().toString(),
+ userId: new ObjectId().toString(),
+ databaseName: DATABASE_NAME,
+ containerName: CONTAINER_NAME,
+ };
+ const chatHistory = new AzureCosmsosDBNoSQLChatMessageHistory(input);
+ const blankResult = await chatHistory.getMessages();
+ expect(blankResult).toStrictEqual([]);
+
+ await chatHistory.addUserMessage("Who is the best vocalist?");
+ await chatHistory.addAIMessage("Ozzy Osbourne");
+
+ const expectedMessages = [
+ new HumanMessage("Who is the best vocalist?"),
+ new AIMessage("Ozzy Osbourne"),
+ ];
+ const resultWithHistory = await chatHistory.getMessages();
+ expect(resultWithHistory).toEqual(expectedMessages);
+});
+
+test("Test clear CosmosDB history Store", async () => {
+ const input = {
+ sessionId: new ObjectId().toString(),
+ userId: new ObjectId().toString(),
+ databaseName: DATABASE_NAME,
+ containerName: CONTAINER_NAME,
+ };
+ const chatHistory = new AzureCosmsosDBNoSQLChatMessageHistory(input);
+
+ await chatHistory.addUserMessage("Who is the best vocalist?");
+ await chatHistory.addAIMessage("Ozzy Osbourne");
+
+ const expectedMessages = [
+ new HumanMessage("Who is the best vocalist?"),
+ new AIMessage("Ozzy Osbourne"),
+ ];
+ const resultWithHistory = await chatHistory.getMessages();
+ expect(resultWithHistory).toEqual(expectedMessages);
+
+ await chatHistory.clear();
+
+ const blankResult = await chatHistory.getMessages();
+ expect(blankResult).toStrictEqual([]);
+});
+
+test("Test CosmosDB history with a TTL", async () => {
+ const input = {
+ sessionId: new ObjectId().toString(),
+ userId: new ObjectId().toString(),
+ databaseName: "DbWithTTL",
+ ttl: 5,
+ };
+ const chatHistory = new AzureCosmsosDBNoSQLChatMessageHistory(input);
+
+ await chatHistory.addUserMessage("Who is the best vocalist?");
+ await chatHistory.addAIMessage("Ozzy Osbourne");
+
+ const expectedMessages = [
+ new HumanMessage("Who is the best vocalist?"),
+ new AIMessage("Ozzy Osbourne"),
+ ];
+ const resultWithHistory = await chatHistory.getMessages();
+ expect(resultWithHistory).toEqual(expectedMessages);
+
+ await new Promise((resolve) => setTimeout(resolve, 6000));
+
+ const expiredResult = await chatHistory.getMessages();
+ expect(expiredResult).toStrictEqual([]);
+});
+
+test("Test clear all sessions for a user", async () => {
+ const input1 = {
+ sessionId: new Date().toISOString(),
+ userId: "user1",
+ databaseName: "DbWithTTL",
+ ttl: 5,
+ };
+ const chatHistory1 = new AzureCosmsosDBNoSQLChatMessageHistory(input1);
+
+ await chatHistory1.addUserMessage("Who is the best vocalist?");
+ await chatHistory1.addAIMessage("Ozzy Osbourne");
+
+ const input2 = {
+ sessionId: new Date().toISOString(),
+ userId: "user1",
+ databaseName: "DbWithTTL",
+ ttl: 5,
+ };
+ const chatHistory2 = new AzureCosmsosDBNoSQLChatMessageHistory(input2);
+
+ await chatHistory2.addUserMessage("Who is the best vocalist?");
+ await chatHistory2.addAIMessage("Ozzy Osbourne");
+
+ const expectedMessages = [
+ new HumanMessage("Who is the best vocalist?"),
+ new AIMessage("Ozzy Osbourne"),
+ ];
+
+ const result1 = await chatHistory1.getMessages();
+ expect(result1).toEqual(expectedMessages);
+
+ const result2 = await chatHistory1.getMessages();
+ expect(result2).toEqual(expectedMessages);
+
+ await chatHistory1.clearAllSessionsForUser("user1");
+
+ const deletedResult1 = await chatHistory1.getMessages();
+ const deletedResult2 = await chatHistory2.getMessages();
+ expect(deletedResult1).toStrictEqual([]);
+ expect(deletedResult2).toStrictEqual([]);
+});