{"version":3,"file":"conversational_retrieval_chain.cjs","names":["BaseChain","HumanMessage","AIMessage","PromptTemplate","loadQAChain","LLMChain"],"sources":["../../src/chains/conversational_retrieval_chain.ts"],"sourcesContent":["import type { BaseLanguageModelInterface } from \"@langchain/core/language_models/base\";\nimport type { BaseRetrieverInterface } from \"@langchain/core/retrievers\";\nimport { PromptTemplate } from \"@langchain/core/prompts\";\nimport { BaseMessage, HumanMessage, AIMessage } from \"@langchain/core/messages\";\nimport { ChainValues } from \"@langchain/core/utils/types\";\nimport { CallbackManagerForChainRun } from \"@langchain/core/callbacks/manager\";\nimport { SerializedChatVectorDBQAChain } from \"./serde.js\";\nimport { BaseChain, ChainInputs } from \"./base.js\";\nimport { LLMChain } from \"./llm_chain.js\";\nimport { QAChainParams, loadQAChain } from \"./question_answering/load.js\";\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nexport type LoadValues = Record<string, any>;\n\nconst question_generator_template = `Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question.\n\nChat History:\n{chat_history}\nFollow Up Input: {question}\nStandalone question:`;\n\n/**\n * Interface for the input parameters of the\n * ConversationalRetrievalQAChain class.\n */\nexport interface ConversationalRetrievalQAChainInput extends ChainInputs {\n  retriever: BaseRetrieverInterface;\n  combineDocumentsChain: BaseChain;\n  questionGeneratorChain: LLMChain;\n  returnSourceDocuments?: boolean;\n  returnGeneratedQuestion?: boolean;\n  inputKey?: string;\n}\n\n/**\n * Class for conducting conversational question-answering tasks with a\n * retrieval component. Extends the BaseChain class and implements the\n * ConversationalRetrievalQAChainInput interface.\n * @example\n * ```typescript\n * import { ChatAnthropic } from \"@langchain/anthropic\";\n * import {\n *   ChatPromptTemplate,\n *   MessagesPlaceholder,\n * } from \"@langchain/core/prompts\";\n * import { BaseMessage } from \"@langchain/core/messages\";\n * import { createStuffDocumentsChain } from \"@langchain/classic/chains/combine_documents\";\n * import { createHistoryAwareRetriever } from \"@langchain/classic/chains/history_aware_retriever\";\n * import { createRetrievalChain } from \"@langchain/classic/chains/retrieval\";\n *\n * const retriever = ...your retriever;\n * const llm = new ChatAnthropic();\n *\n * // Contextualize question\n * const contextualizeQSystemPrompt = `\n * Given a chat history and the latest user question\n * which might reference context in the chat history,\n * formulate a standalone question which can be understood\n * without the chat history. Do NOT answer the question, just\n * reformulate it if needed and otherwise return it as is.`;\n * const contextualizeQPrompt = ChatPromptTemplate.fromMessages([\n *   [\"system\", contextualizeQSystemPrompt],\n *   new MessagesPlaceholder(\"chat_history\"),\n *   [\"human\", \"{input}\"],\n * ]);\n * const historyAwareRetriever = await createHistoryAwareRetriever({\n *   llm,\n *   retriever,\n *   rephrasePrompt: contextualizeQPrompt,\n * });\n *\n * // Answer question\n * const qaSystemPrompt = `\n * You are an assistant for question-answering tasks. Use\n * the following pieces of retrieved context to answer the\n * question. If you don't know the answer, just say that you\n * don't know. Use three sentences maximum and keep the answer\n * concise.\n * \\n\\n\n * {context}`;\n * const qaPrompt = ChatPromptTemplate.fromMessages([\n *   [\"system\", qaSystemPrompt],\n *   new MessagesPlaceholder(\"chat_history\"),\n *   [\"human\", \"{input}\"],\n * ]);\n *\n * // Below we use createStuffDocuments_chain to feed all retrieved context\n * // into the LLM. Note that we can also use StuffDocumentsChain and other\n * // instances of BaseCombineDocumentsChain.\n * const questionAnswerChain = await createStuffDocumentsChain({\n *   llm,\n *   prompt: qaPrompt,\n * });\n *\n * const ragChain = await createRetrievalChain({\n *   retriever: historyAwareRetriever,\n *   combineDocsChain: questionAnswerChain,\n * });\n *\n * // Usage:\n * const chat_history: BaseMessage[] = [];\n * const response = await ragChain.invoke({\n *   chat_history,\n *   input: \"...\",\n * });\n * ```\n */\nexport class ConversationalRetrievalQAChain\n  extends BaseChain\n  implements ConversationalRetrievalQAChainInput\n{\n  static lc_name() {\n    return \"ConversationalRetrievalQAChain\";\n  }\n\n  inputKey = \"question\";\n\n  chatHistoryKey = \"chat_history\";\n\n  get inputKeys() {\n    return [this.inputKey, this.chatHistoryKey];\n  }\n\n  get outputKeys() {\n    return this.combineDocumentsChain.outputKeys.concat(\n      this.returnSourceDocuments ? [\"sourceDocuments\"] : []\n    );\n  }\n\n  retriever: BaseRetrieverInterface;\n\n  combineDocumentsChain: BaseChain;\n\n  questionGeneratorChain: LLMChain;\n\n  returnSourceDocuments = false;\n\n  returnGeneratedQuestion = false;\n\n  constructor(fields: ConversationalRetrievalQAChainInput) {\n    super(fields);\n    this.retriever = fields.retriever;\n    this.combineDocumentsChain = fields.combineDocumentsChain;\n    this.questionGeneratorChain = fields.questionGeneratorChain;\n    this.inputKey = fields.inputKey ?? this.inputKey;\n    this.returnSourceDocuments =\n      fields.returnSourceDocuments ?? this.returnSourceDocuments;\n    this.returnGeneratedQuestion =\n      fields.returnGeneratedQuestion ?? this.returnGeneratedQuestion;\n  }\n\n  /**\n   * Static method to convert the chat history input into a formatted\n   * string.\n   * @param chatHistory Chat history input which can be a string, an array of BaseMessage instances, or an array of string arrays.\n   * @returns A formatted string representing the chat history.\n   */\n  static getChatHistoryString(\n    chatHistory: string | BaseMessage[] | string[][]\n  ) {\n    let historyMessages: BaseMessage[];\n    if (Array.isArray(chatHistory)) {\n      // TODO: Deprecate on a breaking release\n      if (\n        Array.isArray(chatHistory[0]) &&\n        typeof chatHistory[0][0] === \"string\"\n      ) {\n        console.warn(\n          \"Passing chat history as an array of strings is deprecated.\\nPlease see https://js.langchain.com/docs/modules/chains/popular/chat_vector_db#externally-managed-memory for more information.\"\n        );\n        historyMessages = chatHistory.flat().map((stringMessage, i) => {\n          if (i % 2 === 0) {\n            return new HumanMessage(stringMessage);\n          } else {\n            return new AIMessage(stringMessage);\n          }\n        });\n      } else {\n        historyMessages = chatHistory as BaseMessage[];\n      }\n      return historyMessages\n        .map((chatMessage) => {\n          if (chatMessage._getType() === \"human\") {\n            return `Human: ${chatMessage.content}`;\n          } else if (chatMessage._getType() === \"ai\") {\n            return `Assistant: ${chatMessage.content}`;\n          } else {\n            return `${chatMessage.content}`;\n          }\n        })\n        .join(\"\\n\");\n    }\n    return chatHistory;\n  }\n\n  /** @ignore */\n  async _call(\n    values: ChainValues,\n    runManager?: CallbackManagerForChainRun\n  ): Promise<ChainValues> {\n    if (!(this.inputKey in values)) {\n      throw new Error(`Question key ${this.inputKey} not found.`);\n    }\n    if (!(this.chatHistoryKey in values)) {\n      throw new Error(`Chat history key ${this.chatHistoryKey} not found.`);\n    }\n    const question: string = values[this.inputKey];\n    const chatHistory: string =\n      ConversationalRetrievalQAChain.getChatHistoryString(\n        values[this.chatHistoryKey]\n      );\n    let newQuestion = question;\n    if (chatHistory.length > 0) {\n      const result = await this.questionGeneratorChain.call(\n        {\n          question,\n          chat_history: chatHistory,\n        },\n        runManager?.getChild(\"question_generator\")\n      );\n      const keys = Object.keys(result);\n      if (keys.length === 1) {\n        newQuestion = result[keys[0]];\n      } else {\n        throw new Error(\n          \"Return from llm chain has multiple values, only single values supported.\"\n        );\n      }\n    }\n    const docs = await this.retriever.invoke(\n      newQuestion,\n      runManager?.getChild(\"retriever\")\n    );\n    const inputs = {\n      question: newQuestion,\n      input_documents: docs,\n      chat_history: chatHistory,\n    };\n    let result = await this.combineDocumentsChain.call(\n      inputs,\n      runManager?.getChild(\"combine_documents\")\n    );\n    if (this.returnSourceDocuments) {\n      result = {\n        ...result,\n        sourceDocuments: docs,\n      };\n    }\n    if (this.returnGeneratedQuestion) {\n      result = {\n        ...result,\n        generatedQuestion: newQuestion,\n      };\n    }\n    return result;\n  }\n\n  _chainType(): string {\n    return \"conversational_retrieval_chain\";\n  }\n\n  static async deserialize(\n    _data: SerializedChatVectorDBQAChain,\n    _values: LoadValues\n  ): Promise<ConversationalRetrievalQAChain> {\n    throw new Error(\"Not implemented.\");\n  }\n\n  serialize(): SerializedChatVectorDBQAChain {\n    throw new Error(\"Not implemented.\");\n  }\n\n  /**\n   * Static method to create a new ConversationalRetrievalQAChain from a\n   * BaseLanguageModel and a BaseRetriever.\n   * @param llm {@link BaseLanguageModelInterface} instance used to generate a new question.\n   * @param retriever {@link BaseRetrieverInterface} instance used to retrieve relevant documents.\n   * @param options.returnSourceDocuments Whether to return source documents in the final output\n   * @param options.questionGeneratorChainOptions Options to initialize the standalone question generation chain used as the first internal step\n   * @param options.qaChainOptions {@link QAChainParams} used to initialize the QA chain used as the second internal step\n   * @returns A new instance of ConversationalRetrievalQAChain.\n   */\n  static fromLLM(\n    llm: BaseLanguageModelInterface,\n    retriever: BaseRetrieverInterface,\n    options: {\n      outputKey?: string; // not used\n      returnSourceDocuments?: boolean;\n      /** @deprecated Pass in questionGeneratorChainOptions.template instead */\n      questionGeneratorTemplate?: string;\n      /** @deprecated Pass in qaChainOptions.prompt instead */\n      qaTemplate?: string;\n      questionGeneratorChainOptions?: {\n        llm?: BaseLanguageModelInterface;\n        template?: string;\n      };\n      qaChainOptions?: QAChainParams;\n    } & Omit<\n      ConversationalRetrievalQAChainInput,\n      \"retriever\" | \"combineDocumentsChain\" | \"questionGeneratorChain\"\n    > = {}\n  ): ConversationalRetrievalQAChain {\n    const {\n      questionGeneratorTemplate,\n      qaTemplate,\n      qaChainOptions = {\n        type: \"stuff\",\n        prompt: qaTemplate\n          ? PromptTemplate.fromTemplate(qaTemplate)\n          : undefined,\n      },\n      questionGeneratorChainOptions,\n      verbose,\n      ...rest\n    } = options;\n\n    const qaChain = loadQAChain(llm, qaChainOptions);\n\n    const questionGeneratorChainPrompt = PromptTemplate.fromTemplate(\n      questionGeneratorChainOptions?.template ??\n        questionGeneratorTemplate ??\n        question_generator_template\n    );\n    const questionGeneratorChain = new LLMChain({\n      prompt: questionGeneratorChainPrompt,\n      llm: questionGeneratorChainOptions?.llm ?? llm,\n      verbose,\n    });\n    const instance = new this({\n      retriever,\n      combineDocumentsChain: qaChain,\n      questionGeneratorChain,\n      verbose,\n      ...rest,\n    });\n    return instance;\n  }\n}\n"],"mappings":"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"}