{"version":3,"file":"time_weighted.cjs","names":["BaseRetriever"],"sources":["../../src/retrievers/time_weighted.ts"],"sourcesContent":["import { BaseRetriever, BaseRetrieverInput } from \"@langchain/core/retrievers\";\nimport type { VectorStoreInterface } from \"@langchain/core/vectorstores\";\nimport type { DocumentInterface } from \"@langchain/core/documents\";\nimport { CallbackManagerForRetrieverRun } from \"@langchain/core/callbacks/manager\";\n\n/**\n * Interface for the fields required to initialize a\n * TimeWeightedVectorStoreRetriever instance.\n */\nexport interface TimeWeightedVectorStoreRetrieverFields extends BaseRetrieverInput {\n  vectorStore: VectorStoreInterface;\n  searchKwargs?: number;\n  memoryStream?: DocumentInterface[];\n  decayRate?: number;\n  k?: number;\n  otherScoreKeys?: string[];\n  defaultSalience?: number;\n}\n\nexport const LAST_ACCESSED_AT_KEY = \"last_accessed_at\";\nexport const BUFFER_IDX = \"buffer_idx\";\n\n/**\n * TimeWeightedVectorStoreRetriever retrieves documents based on their time-weighted relevance.\n * ref: https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/retrievers/time_weighted_retriever.py\n * @example\n * ```typescript\n * const retriever = new TimeWeightedVectorStoreRetriever({\n *   vectorStore: new MemoryVectorStore(new OpenAIEmbeddings()),\n *   memoryStream: [],\n *   searchKwargs: 2,\n * });\n * await retriever.addDocuments([\n *   { pageContent: \"My name is John.\", metadata: {} },\n *   { pageContent: \"My favourite food is pizza.\", metadata: {} },\n *\n * ]);\n * const results = await retriever.invoke(\n *   \"What is my favourite food?\",\n * );\n * ```\n */\nexport class TimeWeightedVectorStoreRetriever extends BaseRetriever {\n  static lc_name() {\n    return \"TimeWeightedVectorStoreRetriever\";\n  }\n\n  get lc_namespace() {\n    return [\"langchain\", \"retrievers\", \"time_weighted\"];\n  }\n\n  /**\n   * The vectorstore to store documents and determine salience.\n   */\n  private vectorStore: VectorStoreInterface;\n\n  /**\n   * The number of top K most relevant documents to consider when searching.\n   */\n  private searchKwargs: number;\n\n  /**\n   * The memory_stream of documents to search through.\n   */\n  private memoryStream: DocumentInterface[];\n\n  /**\n   * The exponential decay factor used as (1.0-decay_rate)**(hrs_passed).\n   */\n  private decayRate: number;\n\n  /**\n   * The maximum number of documents to retrieve in a given call.\n   */\n  private k: number;\n\n  /**\n   * Other keys in the metadata to factor into the score, e.g. 'importance'.\n   */\n  private otherScoreKeys: string[];\n\n  /**\n   * The salience to assign memories not retrieved from the vector store.\n   */\n  private defaultSalience: number | null;\n\n  /**\n   * Constructor to initialize the required fields\n   * @param fields - The fields required for initializing the TimeWeightedVectorStoreRetriever\n   */\n  constructor(fields: TimeWeightedVectorStoreRetrieverFields) {\n    super(fields);\n    this.vectorStore = fields.vectorStore;\n    this.searchKwargs = fields.searchKwargs ?? 100;\n    this.memoryStream = fields.memoryStream ?? [];\n    this.decayRate = fields.decayRate ?? 0.01;\n    this.k = fields.k ?? 4;\n    this.otherScoreKeys = fields.otherScoreKeys ?? [];\n    this.defaultSalience = fields.defaultSalience ?? null;\n  }\n\n  /**\n   * Get the memory stream of documents.\n   * @returns The memory stream of documents.\n   */\n  getMemoryStream(): DocumentInterface[] {\n    return this.memoryStream;\n  }\n\n  /**\n   * Set the memory stream of documents.\n   * @param memoryStream The new memory stream of documents.\n   */\n  setMemoryStream(memoryStream: DocumentInterface[]) {\n    this.memoryStream = memoryStream;\n  }\n\n  /**\n   * Get relevant documents based on time-weighted relevance\n   * @param query - The query to search for\n   * @returns The relevant documents\n   */\n  async _getRelevantDocuments(\n    query: string,\n    runManager?: CallbackManagerForRetrieverRun\n  ): Promise<DocumentInterface[]> {\n    const now = Math.floor(Date.now() / 1000);\n    const memoryDocsAndScores = this.getMemoryDocsAndScores();\n\n    const salientDocsAndScores = await this.getSalientDocuments(\n      query,\n      runManager\n    );\n    const docsAndScores = { ...memoryDocsAndScores, ...salientDocsAndScores };\n\n    return this.computeResults(docsAndScores, now);\n  }\n\n  /**\n   * NOTE: When adding documents to a vector store, use addDocuments\n   * via retriever instead of directly to the vector store.\n   * This is because it is necessary to process the document\n   * in prepareDocuments.\n   *\n   * @param docs - The documents to add to vector store in the retriever\n   */\n  async addDocuments(docs: DocumentInterface[]): Promise<void> {\n    const now = Math.floor(Date.now() / 1000);\n    const savedDocs = this.prepareDocuments(docs, now);\n\n    this.memoryStream.push(...savedDocs);\n    await this.vectorStore.addDocuments(savedDocs);\n  }\n\n  /**\n   * Get memory documents and their scores\n   * @returns An object containing memory documents and their scores\n   */\n  private getMemoryDocsAndScores(): Record<\n    number,\n    { doc: DocumentInterface; score: number }\n  > {\n    const memoryDocsAndScores: Record<\n      number,\n      { doc: DocumentInterface; score: number }\n    > = {};\n    for (const doc of this.memoryStream.slice(-this.k)) {\n      const bufferIdx = doc.metadata[BUFFER_IDX];\n      if (bufferIdx === undefined) {\n        throw new Error(\n          `Found a document in the vector store that is missing required metadata. This retriever only supports vector stores with documents that have been added through the \"addDocuments\" method on a TimeWeightedVectorStoreRetriever, not directly added or loaded into the backing vector store.`\n        );\n      }\n      memoryDocsAndScores[bufferIdx] = {\n        doc,\n        score: this.defaultSalience ?? 0,\n      };\n    }\n    return memoryDocsAndScores;\n  }\n\n  /**\n   * Get salient documents and their scores based on the query\n   * @param query - The query to search for\n   * @returns An object containing salient documents and their scores\n   */\n  private async getSalientDocuments(\n    query: string,\n    runManager?: CallbackManagerForRetrieverRun\n  ): Promise<Record<number, { doc: DocumentInterface; score: number }>> {\n    const docAndScores: [DocumentInterface, number][] =\n      await this.vectorStore.similaritySearchWithScore(\n        query,\n        this.searchKwargs,\n        undefined,\n        runManager?.getChild()\n      );\n    const results: Record<number, { doc: DocumentInterface; score: number }> =\n      {};\n    for (const [fetchedDoc, score] of docAndScores) {\n      const bufferIdx = fetchedDoc.metadata[BUFFER_IDX];\n      if (bufferIdx === undefined) {\n        throw new Error(\n          `Found a document in the vector store that is missing required metadata. This retriever only supports vector stores with documents that have been added through the \"addDocuments\" method on a TimeWeightedVectorStoreRetriever, not directly added or loaded into the backing vector store.`\n        );\n      }\n      const doc = this.memoryStream[bufferIdx];\n      results[bufferIdx] = { doc, score };\n    }\n    return results;\n  }\n\n  /**\n   * Compute the final result set of documents based on the combined scores\n   * @param docsAndScores - An object containing documents and their scores\n   * @param now - The current timestamp\n   * @returns The final set of documents\n   */\n  private computeResults(\n    docsAndScores: Record<number, { doc: DocumentInterface; score: number }>,\n    now: number\n  ): DocumentInterface[] {\n    const recordedDocs = Object.values(docsAndScores)\n      .map(({ doc, score }) => ({\n        doc,\n        score: this.getCombinedScore(doc, score, now),\n      }))\n      .sort((a, b) => b.score - a.score);\n\n    const results: DocumentInterface[] = [];\n    for (const { doc } of recordedDocs) {\n      const bufferedDoc = this.memoryStream[doc.metadata[BUFFER_IDX]];\n      bufferedDoc.metadata[LAST_ACCESSED_AT_KEY] = now;\n      results.push(bufferedDoc);\n      if (results.length > this.k) {\n        break;\n      }\n    }\n    return results;\n  }\n\n  /**\n   * Prepare documents with necessary metadata before saving\n   * @param docs - The documents to prepare\n   * @param now - The current timestamp\n   * @returns The prepared documents\n   */\n  private prepareDocuments(\n    docs: DocumentInterface[],\n    now: number\n  ): DocumentInterface[] {\n    return docs.map((doc, i) => ({\n      ...doc,\n      metadata: {\n        ...doc.metadata,\n        [LAST_ACCESSED_AT_KEY]: doc.metadata[LAST_ACCESSED_AT_KEY] ?? now,\n        created_at: doc.metadata.created_at ?? now,\n        [BUFFER_IDX]: this.memoryStream.length + i,\n      },\n    }));\n  }\n\n  /**\n   * Calculate the combined score based on vector relevance and other factors\n   * @param doc - The document to calculate the score for\n   * @param vectorRelevance - The relevance score from the vector store\n   * @param nowMsec - The current timestamp in milliseconds\n   * @returns The combined score for the document\n   */\n  private getCombinedScore(\n    doc: DocumentInterface,\n    vectorRelevance: number | null,\n    nowMsec: number\n  ): number {\n    const hoursPassed = this.getHoursPassed(\n      nowMsec,\n      doc.metadata[LAST_ACCESSED_AT_KEY]\n    );\n    let score = (1.0 - this.decayRate) ** hoursPassed;\n    for (const key of this.otherScoreKeys) {\n      score += doc.metadata[key];\n    }\n    if (vectorRelevance !== null) {\n      score += vectorRelevance;\n    }\n    return score;\n  }\n\n  /**\n   * Calculate the hours passed between two time points\n   * @param time - The current time in seconds\n   * @param refTime - The reference time in seconds\n   * @returns The number of hours passed between the two time points\n   */\n  private getHoursPassed(time: number, refTime: number): number {\n    return (time - refTime) / 3600;\n  }\n}\n"],"mappings":"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"}