"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiQueryRetriever = void 0; const retrievers_1 = require("@langchain/core/retrievers"); const output_parsers_1 = require("@langchain/core/output_parsers"); const prompts_1 = require("@langchain/core/prompts"); const llm_chain_js_1 = require("../chains/llm_chain.cjs"); class LineListOutputParser extends output_parsers_1.BaseOutputParser { constructor() { super(...arguments); Object.defineProperty(this, "lc_namespace", { enumerable: true, configurable: true, writable: true, value: ["langchain", "retrievers", "multiquery"] }); } static lc_name() { return "LineListOutputParser"; } async parse(text) { const startKeyIndex = text.indexOf(""); const endKeyIndex = text.indexOf(""); const questionsStartIndex = startKeyIndex === -1 ? 0 : startKeyIndex + "".length; const questionsEndIndex = endKeyIndex === -1 ? text.length : endKeyIndex; const lines = text .slice(questionsStartIndex, questionsEndIndex) .trim() .split("\n") .filter((line) => line.trim() !== ""); return { lines }; } getFormatInstructions() { throw new Error("Not implemented."); } } // Create template const DEFAULT_QUERY_PROMPT = /* #__PURE__ */ new prompts_1.PromptTemplate({ inputVariables: ["question", "queryCount"], template: `You are an AI language model assistant. Your task is to generate {queryCount} different versions of the given user question to retrieve relevant documents from a vector database. By generating multiple perspectives on the user question, your goal is to help the user overcome some of the limitations of distance-based similarity search. Provide these alternative questions separated by newlines between XML tags. For example: Question 1 Question 2 Question 3 Original question: {question}`, }); /** * @example * ```typescript * const retriever = new MultiQueryRetriever.fromLLM({ * llm: new ChatAnthropic({}), * retriever: new MemoryVectorStore().asRetriever(), * verbose: true, * }); * const retrievedDocs = await retriever.getRelevantDocuments( * "What are mitochondria made of?", * ); * ``` */ class MultiQueryRetriever extends retrievers_1.BaseRetriever { static lc_name() { return "MultiQueryRetriever"; } constructor(fields) { super(fields); Object.defineProperty(this, "lc_namespace", { enumerable: true, configurable: true, writable: true, value: ["langchain", "retrievers", "multiquery"] }); Object.defineProperty(this, "retriever", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "llmChain", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "queryCount", { enumerable: true, configurable: true, writable: true, value: 3 }); Object.defineProperty(this, "parserKey", { enumerable: true, configurable: true, writable: true, value: "lines" }); Object.defineProperty(this, "documentCompressor", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "documentCompressorFilteringFn", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.retriever = fields.retriever; this.llmChain = fields.llmChain; this.queryCount = fields.queryCount ?? this.queryCount; this.parserKey = fields.parserKey ?? this.parserKey; this.documentCompressor = fields.documentCompressor; this.documentCompressorFilteringFn = fields.documentCompressorFilteringFn; } static fromLLM(fields) { const { retriever, llm, prompt = DEFAULT_QUERY_PROMPT, queryCount, parserKey, ...rest } = fields; const outputParser = new LineListOutputParser(); const llmChain = new llm_chain_js_1.LLMChain({ llm, prompt, outputParser }); return new this({ retriever, llmChain, queryCount, parserKey, ...rest }); } // Generate the different queries for each retrieval, using our llmChain async _generateQueries(question, runManager) { const response = await this.llmChain.call({ question, queryCount: this.queryCount }, runManager?.getChild()); const lines = response.text[this.parserKey] || []; if (this.verbose) { console.log(`Generated queries: ${lines}`); } return lines; } // Retrieve documents using the original retriever async _retrieveDocuments(queries, runManager) { const documents = []; await Promise.all(queries.map(async (query) => { const docs = await this.retriever.getRelevantDocuments(query, runManager?.getChild()); documents.push(...docs); })); return documents; } // Deduplicate the documents that were returned in multiple retrievals _uniqueUnion(documents) { const uniqueDocumentsDict = {}; for (const doc of documents) { const key = `${doc.pageContent}:${JSON.stringify(Object.entries(doc.metadata).sort())}`; uniqueDocumentsDict[key] = doc; } const uniqueDocuments = Object.values(uniqueDocumentsDict); return uniqueDocuments; } async _getRelevantDocuments(question, runManager) { const queries = await this._generateQueries(question, runManager); const documents = await this._retrieveDocuments(queries, runManager); const uniqueDocuments = this._uniqueUnion(documents); let outputDocs = uniqueDocuments; if (this.documentCompressor && uniqueDocuments.length) { outputDocs = await this.documentCompressor.compressDocuments(uniqueDocuments, question, runManager?.getChild()); if (this.documentCompressorFilteringFn) { outputDocs = this.documentCompressorFilteringFn(outputDocs); } } return outputDocs; } } exports.MultiQueryRetriever = MultiQueryRetriever;