{"version":3,"file":"multi_query.cjs","names":["BaseOutputParser","PromptTemplate","BaseRetriever","LLMChain"],"sources":["../../src/retrievers/multi_query.ts"],"sourcesContent":["import type { BaseLanguageModelInterface } from \"@langchain/core/language_models/base\";\nimport {\n  BaseRetriever,\n  type BaseRetrieverInput,\n  type BaseRetrieverInterface,\n} from \"@langchain/core/retrievers\";\nimport { Document } from \"@langchain/core/documents\";\nimport { BaseOutputParser } from \"@langchain/core/output_parsers\";\nimport { PromptTemplate, BasePromptTemplate } from \"@langchain/core/prompts\";\nimport { CallbackManagerForRetrieverRun } from \"@langchain/core/callbacks/manager\";\nimport { LLMChain } from \"../chains/llm_chain.js\";\nimport type { BaseDocumentCompressor } from \"./document_compressors/index.js\";\n\ninterface LineList {\n  lines: string[];\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nexport type MultiDocs = Document<Record<string, any>>[];\n\nclass LineListOutputParser extends BaseOutputParser<LineList> {\n  static lc_name() {\n    return \"LineListOutputParser\";\n  }\n\n  lc_namespace = [\"langchain\", \"retrievers\", \"multiquery\"];\n\n  async parse(text: string): Promise<LineList> {\n    const startKeyIndex = text.indexOf(\"<questions>\");\n    const endKeyIndex = text.indexOf(\"</questions>\");\n    const questionsStartIndex =\n      startKeyIndex === -1 ? 0 : startKeyIndex + \"<questions>\".length;\n    const questionsEndIndex = endKeyIndex === -1 ? text.length : endKeyIndex;\n    const lines = text\n      .slice(questionsStartIndex, questionsEndIndex)\n      .trim()\n      .split(\"\\n\")\n      .filter((line) => line.trim() !== \"\");\n    return { lines };\n  }\n\n  getFormatInstructions(): string {\n    throw new Error(\"Not implemented.\");\n  }\n}\n\n// Create template\nconst DEFAULT_QUERY_PROMPT = /* #__PURE__ */ new PromptTemplate({\n  inputVariables: [\"question\", \"queryCount\"],\n  template: `You are an AI language model assistant. Your task is\nto generate {queryCount} different versions of the given user\nquestion to retrieve relevant documents from a vector database.\nBy generating multiple perspectives on the user question,\nyour goal is to help the user overcome some of the limitations\nof distance-based similarity search.\n\nProvide these alternative questions separated by newlines between XML tags. For example:\n\n<questions>\nQuestion 1\nQuestion 2\nQuestion 3\n</questions>\n\nOriginal question: {question}`,\n});\n\nexport interface MultiQueryRetrieverInput extends BaseRetrieverInput {\n  retriever: BaseRetrieverInterface;\n  /** @deprecated Pass a custom prompt into `.fromLLM` instead. */\n  llmChain: LLMChain<LineList>;\n  queryCount?: number;\n  parserKey?: string;\n  documentCompressor?: BaseDocumentCompressor | undefined;\n  documentCompressorFilteringFn?: (docs: MultiDocs) => MultiDocs;\n}\n\n/**\n * @example\n * ```typescript\n * const retriever = new MultiQueryRetriever.fromLLM({\n *   llm: new ChatAnthropic({}),\n *   retriever: new MemoryVectorStore().asRetriever(),\n *   verbose: true,\n * });\n * const retrievedDocs = await retriever.invoke(\n *   \"What are mitochondria made of?\",\n * );\n * ```\n */\nexport class MultiQueryRetriever extends BaseRetriever {\n  static lc_name() {\n    return \"MultiQueryRetriever\";\n  }\n\n  lc_namespace = [\"langchain\", \"retrievers\", \"multiquery\"];\n\n  private retriever: BaseRetrieverInterface;\n\n  private llmChain: LLMChain<LineList>;\n\n  private queryCount = 3;\n\n  private parserKey = \"lines\";\n\n  documentCompressor: BaseDocumentCompressor | undefined;\n\n  documentCompressorFilteringFn?: MultiQueryRetrieverInput[\"documentCompressorFilteringFn\"];\n\n  constructor(fields: MultiQueryRetrieverInput) {\n    super(fields);\n    this.retriever = fields.retriever;\n    this.llmChain = fields.llmChain;\n    this.queryCount = fields.queryCount ?? this.queryCount;\n    this.parserKey = fields.parserKey ?? this.parserKey;\n    this.documentCompressor = fields.documentCompressor;\n    this.documentCompressorFilteringFn = fields.documentCompressorFilteringFn;\n  }\n\n  static fromLLM(\n    fields: Omit<MultiQueryRetrieverInput, \"llmChain\"> & {\n      llm: BaseLanguageModelInterface;\n      prompt?: BasePromptTemplate;\n    }\n  ): MultiQueryRetriever {\n    const {\n      retriever,\n      llm,\n      prompt = DEFAULT_QUERY_PROMPT,\n      queryCount,\n      parserKey,\n      ...rest\n    } = fields;\n    const outputParser = new LineListOutputParser();\n    const llmChain = new LLMChain({ llm, prompt, outputParser });\n    return new this({ retriever, llmChain, queryCount, parserKey, ...rest });\n  }\n\n  // Generate the different queries for each retrieval, using our llmChain\n  private async _generateQueries(\n    question: string,\n    runManager?: CallbackManagerForRetrieverRun\n  ): Promise<string[]> {\n    const response = await this.llmChain.call(\n      { question, queryCount: this.queryCount },\n      runManager?.getChild()\n    );\n    const lines = response.text[this.parserKey] || [];\n    if (this.verbose) {\n      console.log(`Generated queries: ${lines}`);\n    }\n    return lines;\n  }\n\n  // Retrieve documents using the original retriever\n  private async _retrieveDocuments(\n    queries: string[],\n    runManager?: CallbackManagerForRetrieverRun\n  ): Promise<Document[]> {\n    const documents: Document[] = [];\n    await Promise.all(\n      queries.map(async (query) => {\n        const docs = await this.retriever.invoke(query, runManager?.getChild());\n        documents.push(...docs);\n      })\n    );\n    return documents;\n  }\n\n  // Deduplicate the documents that were returned in multiple retrievals\n  private _uniqueUnion(documents: Document[]): Document[] {\n    const uniqueDocumentsDict: { [key: string]: Document } = {};\n\n    for (const doc of documents) {\n      const key = `${doc.pageContent}:${JSON.stringify(\n        Object.entries(doc.metadata).sort()\n      )}`;\n      uniqueDocumentsDict[key] = doc;\n    }\n\n    const uniqueDocuments = Object.values(uniqueDocumentsDict);\n    return uniqueDocuments;\n  }\n\n  async _getRelevantDocuments(\n    question: string,\n    runManager?: CallbackManagerForRetrieverRun\n  ): Promise<Document[]> {\n    const queries = await this._generateQueries(question, runManager);\n    const documents = await this._retrieveDocuments(queries, runManager);\n    const uniqueDocuments = this._uniqueUnion(documents);\n\n    let outputDocs = uniqueDocuments;\n    if (this.documentCompressor && uniqueDocuments.length) {\n      outputDocs = await this.documentCompressor.compressDocuments(\n        uniqueDocuments,\n        question,\n        runManager?.getChild()\n      );\n      if (this.documentCompressorFilteringFn) {\n        outputDocs = this.documentCompressorFilteringFn(outputDocs);\n      }\n    }\n\n    return outputDocs;\n  }\n}\n"],"mappings":";;;;;;;;AAoBA,IAAM,uBAAN,cAAmCA,+BAAAA,iBAA2B;CAC5D,OAAO,UAAU;AACf,SAAO;;CAGT,eAAe;EAAC;EAAa;EAAc;EAAa;CAExD,MAAM,MAAM,MAAiC;EAC3C,MAAM,gBAAgB,KAAK,QAAQ,cAAc;EACjD,MAAM,cAAc,KAAK,QAAQ,eAAe;EAChD,MAAM,sBACJ,kBAAkB,KAAK,IAAI,gBAAgB;EAC7C,MAAM,oBAAoB,gBAAgB,KAAK,KAAK,SAAS;AAM7D,SAAO,EAAE,OALK,KACX,MAAM,qBAAqB,kBAAkB,CAC7C,MAAM,CACN,MAAM,KAAK,CACX,QAAQ,SAAS,KAAK,MAAM,KAAK,GAAG,EACvB;;CAGlB,wBAAgC;AAC9B,QAAM,IAAI,MAAM,mBAAmB;;;AAKvC,MAAM,uCAAuC,IAAIC,wBAAAA,eAAe;CAC9D,gBAAgB,CAAC,YAAY,aAAa;CAC1C,UAAU;;;;;;;;;;;;;;;;CAgBX,CAAC;;;;;;;;;;;;;;AAyBF,IAAa,sBAAb,cAAyCC,2BAAAA,cAAc;CACrD,OAAO,UAAU;AACf,SAAO;;CAGT,eAAe;EAAC;EAAa;EAAc;EAAa;CAExD;CAEA;CAEA,aAAqB;CAErB,YAAoB;CAEpB;CAEA;CAEA,YAAY,QAAkC;AAC5C,QAAM,OAAO;AACb,OAAK,YAAY,OAAO;AACxB,OAAK,WAAW,OAAO;AACvB,OAAK,aAAa,OAAO,cAAc,KAAK;AAC5C,OAAK,YAAY,OAAO,aAAa,KAAK;AAC1C,OAAK,qBAAqB,OAAO;AACjC,OAAK,gCAAgC,OAAO;;CAG9C,OAAO,QACL,QAIqB;EACrB,MAAM,EACJ,WACA,KACA,SAAS,sBACT,YACA,WACA,GAAG,SACD;EAEJ,MAAM,WAAW,IAAIC,kBAAAA,SAAS;GAAE;GAAK;GAAQ,cADxB,IAAI,sBAAsB;GACY,CAAC;AAC5D,SAAO,IAAI,KAAK;GAAE;GAAW;GAAU;GAAY;GAAW,GAAG;GAAM,CAAC;;CAI1E,MAAc,iBACZ,UACA,YACmB;EAKnB,MAAM,SAJW,MAAM,KAAK,SAAS,KACnC;GAAE;GAAU,YAAY,KAAK;GAAY,EACzC,YAAY,UAAU,CACvB,EACsB,KAAK,KAAK,cAAc,EAAE;AACjD,MAAI,KAAK,QACP,SAAQ,IAAI,sBAAsB,QAAQ;AAE5C,SAAO;;CAIT,MAAc,mBACZ,SACA,YACqB;EACrB,MAAM,YAAwB,EAAE;AAChC,QAAM,QAAQ,IACZ,QAAQ,IAAI,OAAO,UAAU;GAC3B,MAAM,OAAO,MAAM,KAAK,UAAU,OAAO,OAAO,YAAY,UAAU,CAAC;AACvE,aAAU,KAAK,GAAG,KAAK;IACvB,CACH;AACD,SAAO;;CAIT,aAAqB,WAAmC;EACtD,MAAM,sBAAmD,EAAE;AAE3D,OAAK,MAAM,OAAO,WAAW;GAC3B,MAAM,MAAM,GAAG,IAAI,YAAY,GAAG,KAAK,UACrC,OAAO,QAAQ,IAAI,SAAS,CAAC,MAAM,CACpC;AACD,uBAAoB,OAAO;;AAI7B,SADwB,OAAO,OAAO,oBAAoB;;CAI5D,MAAM,sBACJ,UACA,YACqB;EACrB,MAAM,UAAU,MAAM,KAAK,iBAAiB,UAAU,WAAW;EACjE,MAAM,YAAY,MAAM,KAAK,mBAAmB,SAAS,WAAW;EACpE,MAAM,kBAAkB,KAAK,aAAa,UAAU;EAEpD,IAAI,aAAa;AACjB,MAAI,KAAK,sBAAsB,gBAAgB,QAAQ;AACrD,gBAAa,MAAM,KAAK,mBAAmB,kBACzC,iBACA,UACA,YAAY,UAAU,CACvB;AACD,OAAI,KAAK,8BACP,cAAa,KAAK,8BAA8B,WAAW;;AAI/D,SAAO"}