@langchain/community
Version:
Third-party integrations for LangChain.js
1 lines • 9.07 kB
Source Map (JSON)
{"version":3,"file":"firestore.cjs","names":["BaseListChatMessageHistory","FieldValue"],"sources":["../../../src/stores/message/firestore.ts"],"sourcesContent":["import type { AppOptions } from \"firebase-admin\";\nimport { getApps, initializeApp } from \"firebase-admin/app\";\nimport {\n getFirestore,\n DocumentData,\n Firestore,\n DocumentReference,\n FieldValue,\n} from \"firebase-admin/firestore\";\n\nimport { BaseListChatMessageHistory } from \"@langchain/core/chat_history\";\nimport {\n BaseMessage,\n StoredMessage,\n mapChatMessagesToStoredMessages,\n mapStoredMessagesToChatMessages,\n} from \"@langchain/core/messages\";\n\n/**\n * Interface for FirestoreDBChatMessageHistory. It includes the collection\n * name, session ID, user ID, and optionally, the app index and\n * configuration for the Firebase app.\n */\nexport interface FirestoreDBChatMessageHistory {\n /**\n * An array of collection names, should match the length of `docs` field.\n * @TODO make required variable in 0.2\n */\n collections?: string[];\n /**\n * An array of doc names, should match the length of `collections` field,\n * or undefined if the collections field has a length of 1. In this case,\n * it will default to use `sessionId` as the doc name.\n * @TODO make required variable in 0.2\n */\n docs?: string[];\n sessionId: string;\n userId: string;\n appIdx?: number;\n config?: AppOptions;\n}\n/**\n * Class for managing chat message history using Google's Firestore as a\n * storage backend. Extends the BaseListChatMessageHistory class.\n * @example\n * ```typescript\n * const chatHistory = new FirestoreChatMessageHistory({\n * collectionName: \"langchain\",\n * sessionId: \"lc-example\",\n * userId: \"a@example.com\",\n * config: { projectId: \"your-project-id\" },\n * });\n *\n * const chain = new ConversationChain({\n * llm: new ChatOpenAI({ model: \"gpt-4o-mini\" }),\n * memory: new BufferMemory({ chatHistory }),\n * });\n *\n * const response = await chain.invoke({\n * input: \"What did I just say my name was?\",\n * });\n * console.log({ response });\n * ```\n */\nexport class FirestoreChatMessageHistory extends BaseListChatMessageHistory {\n lc_namespace = [\"langchain\", \"stores\", \"message\", \"firestore\"];\n\n private collections: string[];\n\n private docs: string[];\n\n private sessionId: string;\n\n private userId: string;\n\n private appIdx: number;\n\n private config: AppOptions;\n\n private firestoreClient: Firestore;\n\n private document: DocumentReference<DocumentData> | null;\n\n constructor({\n collections,\n docs,\n sessionId,\n userId,\n appIdx = 0,\n config,\n }: FirestoreDBChatMessageHistory) {\n super();\n if (collections || docs) {\n // This checks that the 'collections' and 'docs' arrays have the same length,\n // which means each collection has a corresponding document name. The only exception allowed is\n // when there is exactly one collection provided and 'docs' is not defined. In this case, it is\n // assumed that the 'sessionId' will be used as the document name for that single collection.\n if (\n !(\n collections?.length === docs?.length ||\n (collections?.length === 1 && !docs)\n )\n ) {\n throw new Error(\n \"Collections and docs options must have the same length, or collections must have a length of 1 if docs is not defined.\"\n );\n }\n }\n\n this.collections = collections || [];\n this.docs = docs || ([sessionId] as string[]);\n this.sessionId = sessionId;\n this.userId = userId;\n this.document = null;\n this.appIdx = appIdx;\n if (config) this.config = config;\n\n try {\n this.ensureFirestore();\n } catch {\n throw new Error(`Unknown response type`);\n }\n }\n\n private ensureFirestore(): void {\n let app;\n // Check if the app is already initialized else get appIdx\n if (!getApps().length) app = initializeApp(this.config);\n else app = getApps()[this.appIdx];\n\n this.firestoreClient = getFirestore(app);\n\n this.document = this.collections.reduce<DocumentReference<DocumentData>>(\n (acc, collection, index) =>\n acc.collection(collection).doc(this.docs[index]),\n this.firestoreClient as unknown as DocumentReference<DocumentData>\n );\n }\n\n /**\n * Method to retrieve all messages from the Firestore collection\n * associated with the current session. Returns an array of BaseMessage\n * objects.\n * @returns Array of stored messages\n */\n async getMessages(): Promise<BaseMessage[]> {\n if (!this.document) {\n throw new Error(\"Document not initialized\");\n }\n\n const querySnapshot = await this.document\n .collection(\"messages\")\n .orderBy(\"createdAt\", \"asc\")\n .get()\n .catch((err) => {\n throw new Error(`Unknown response type: ${err.toString()}`);\n });\n\n const response: StoredMessage[] = [];\n querySnapshot.forEach((doc) => {\n const { type, data } = doc.data();\n response.push({ type, data });\n });\n\n return mapStoredMessagesToChatMessages(response);\n }\n\n /**\n * Method to add a new message to the Firestore collection. The message is\n * passed as a BaseMessage object.\n * @param message The message to be added as a BaseMessage object.\n */\n public async addMessage(message: BaseMessage) {\n const messages = mapChatMessagesToStoredMessages([message]);\n await this.upsertMessage(messages[0]);\n }\n\n private async upsertMessage(message: StoredMessage): Promise<void> {\n if (!this.document) {\n throw new Error(\"Document not initialized\");\n }\n\n await this.document.set(\n {\n id: this.sessionId,\n user_id: this.userId,\n },\n { merge: true }\n );\n await this.document\n .collection(\"messages\")\n .add({\n type: message.type,\n data: message.data,\n createdBy: this.userId,\n createdAt: FieldValue.serverTimestamp(),\n })\n .catch((err) => {\n throw new Error(`Unknown response type: ${err.toString()}`);\n });\n }\n\n /**\n * Method to delete all messages from the Firestore collection associated\n * with the current session.\n */\n public async clear(): Promise<void> {\n if (!this.document) {\n throw new Error(\"Document not initialized\");\n }\n\n await this.document\n .collection(\"messages\")\n .get()\n .then((querySnapshot) => {\n querySnapshot.docs.forEach((snapshot) => {\n snapshot.ref.delete().catch((err) => {\n throw new Error(`Unknown response type: ${err.toString()}`);\n });\n });\n })\n .catch((err) => {\n throw new Error(`Unknown response type: ${err.toString()}`);\n });\n await this.document.delete().catch((err) => {\n throw new Error(`Unknown response type: ${err.toString()}`);\n });\n }\n}\n"],"mappings":"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"}