{"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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"}