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