UNPKG

@langchain/community

Version:
1 lines • 9.26 kB
{"version":3,"file":"firestore.cjs","names":["BaseListChatMessageHistory","response: StoredMessage[]","message: BaseMessage","message: StoredMessage","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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"}