import type { ToolInterface } from "@langchain/core/tools";
import { BasePromptTemplate } from "@langchain/core/prompts";
import type {
  BaseLanguageModel,
  BaseLanguageModelInterface,
} from "@langchain/core/language_models/base";
import { RunnablePassthrough } from "@langchain/core/runnables";
import { AgentStep } from "@langchain/core/agents";
import { renderTextDescription } from "../../tools/render.js";
import { formatLogToString } from "../format_scratchpad/log.js";
import { ReActSingleInputOutputParser } from "./output_parser.js";
import { AgentRunnableSequence } from "../agent.js";

/**
 * Params used by the createXmlAgent function.
 */
export type CreateReactAgentParams = {
  /** LLM to use for the agent. */
  llm: BaseLanguageModelInterface;
  /** Tools this agent has access to. */
  tools: ToolInterface[];
  /**
   * The prompt to use. Must have input keys for
   * `tools`, `tool_names`, and `agent_scratchpad`.
   */
  prompt: BasePromptTemplate;
  /**
   * Whether to invoke the underlying model in streaming mode,
   * allowing streaming of intermediate steps. Defaults to true.
   */
  streamRunnable?: boolean;
};

/**
 * Create an agent that uses ReAct prompting.
 * @param params Params required to create the agent. Includes an LLM, tools, and prompt.
 * @returns A runnable sequence representing an agent. It takes as input all the same input
 *     variables as the prompt passed in does. It returns as output either an
 *     AgentAction or AgentFinish.
 *
 * @example
 * ```typescript
 * import { AgentExecutor, createReactAgent } from "langchain/agents";
 * import { pull } from "langchain/hub";
 * import type { PromptTemplate } from "@langchain/core/prompts";
 *
 * import { OpenAI } from "@langchain/openai";
 *
 * // Define the tools the agent will have access to.
 * const tools = [...];
 *
 * // Get the prompt to use - you can modify this!
 * // If you want to see the prompt in full, you can at:
 * // https://smith.langchain.com/hub/hwchase17/react
 * const prompt = await pull<PromptTemplate>("hwchase17/react");
 *
 * const llm = new OpenAI({
 *   temperature: 0,
 * });
 *
 * const agent = await createReactAgent({
 *   llm,
 *   tools,
 *   prompt,
 * });
 *
 * const agentExecutor = new AgentExecutor({
 *   agent,
 *   tools,
 * });
 *
 * const result = await agentExecutor.invoke({
 *   input: "what is LangChain?",
 * });
 * ```
 */
export async function createReactAgent({
  llm,
  tools,
  prompt,
  streamRunnable,
}: CreateReactAgentParams) {
  const missingVariables = ["tools", "tool_names", "agent_scratchpad"].filter(
    (v) => !prompt.inputVariables.includes(v)
  );
  if (missingVariables.length > 0) {
    throw new Error(
      `Provided prompt is missing required input variables: ${JSON.stringify(
        missingVariables
      )}`
    );
  }
  const toolNames = tools.map((tool) => tool.name);
  const partialedPrompt = await prompt.partial({
    tools: renderTextDescription(tools),
    tool_names: toolNames.join(", "),
  });
  // TODO: Add .bind to core runnable interface.
  const llmWithStop = (llm as BaseLanguageModel).bind({
    stop: ["\nObservation:"],
  });
  const agent = AgentRunnableSequence.fromRunnables(
    [
      RunnablePassthrough.assign({
        agent_scratchpad: (input: { steps: AgentStep[] }) =>
          formatLogToString(input.steps),
      }),
      partialedPrompt,
      llmWithStop,
      new ReActSingleInputOutputParser({
        toolNames,
      }),
    ],
    {
      name: "ReactAgent",
      streamRunnable,
      singleAction: true,
    }
  );
  return agent;
}


export {
  BaseMessagePromptTemplate,
  type MessagesPlaceholderFields as MessagePlaceholderFields,
  MessagesPlaceholder,
  type MessageStringPromptTemplateFields,
  BaseMessageStringPromptTemplate,
  BaseChatPromptTemplate,
  type ChatMessagePromptTemplateFields,
  ChatMessagePromptTemplate,
  HumanMessagePromptTemplate,
  AIMessagePromptTemplate,
  SystemMessagePromptTemplate,
  type ChatPromptTemplateInput,
  type BaseMessagePromptTemplateLike,
  ChatPromptTemplate,
} from "@langchain/core/prompts";

export {
  type ChatPromptValueFields,
  ChatPromptValue,
} from "@langchain/core/prompt_values";


import * as url from "node:url";
import * as path from "node:path";
import { test, expect } from "@jest/globals";
import { Document } from "@langchain/core/documents";
import { JSONLoader } from "../fs/json.js";

test("Test JSON loader", async () => {
  const filePath = path.resolve(
    path.dirname(url.fileURLToPath(import.meta.url)),
    "./example_data/Star_Wars_The_Clone_Wars_S06E07_Crisis_at_the_Heart.json"
  );
  const loader = new JSONLoader(filePath);
  const docs = await loader.load();
  expect(docs.length).toBe(32);
  expect(docs[0]).toEqual(
    new Document({
      metadata: { source: filePath, line: 1 },
      pageContent:
        "<i>Corruption discovered at the core of the Banking Clan!</i>",
    })
  );
});

test("Test JSON  loader for complex json without keys", async () => {
  const filePath = path.resolve(
    path.dirname(url.fileURLToPath(import.meta.url)),
    "./example_data/complex.json"
  );
  const loader = new JSONLoader(filePath);
  const docs = await loader.load();

  expect(docs.length).toBe(10);
  expect(docs[0]).toEqual(
    new Document({
      metadata: { source: filePath, line: 1 },
      pageContent: "BD 2023 SUMMER",
    })
  );
  expect(docs[1]).toEqual(
    new Document({
      metadata: { source: filePath, line: 2 },
      pageContent: "LinkedIn Job",
    })
  );
  expect(docs[2]).toEqual(
    new Document({
      metadata: { source: filePath, line: 3 },
      pageContent: "IMPORTANT",
    })
  );
});

test("Test JSON loader for complex json with one key that points nothing", async () => {
  const filePath = path.resolve(
    path.dirname(url.fileURLToPath(import.meta.url)),
    "./example_data/complex.json"
  );
  const loader = new JSONLoader(filePath, ["/plop"]);
  const docs = await loader.load();

  expect(docs.length).toBe(0);
});

test("Test JSON loader for complex json with one key that exists", async () => {
  const filePath = path.resolve(
    path.dirname(url.fileURLToPath(import.meta.url)),
    "./example_data/complex.json"
  );
  const loader = new JSONLoader(filePath, ["/from"]);
  const docs = await loader.load();

  expect(docs.length).toBe(2);
  expect(docs[1]).toEqual(
    new Document({
      metadata: { source: filePath, line: 2 },
      pageContent: "LinkedIn Job2",
    })
  );
});

test("Test JSON loader for complex json with two keys that exists", async () => {
  const filePath = path.resolve(
    path.dirname(url.fileURLToPath(import.meta.url)),
    "./example_data/complex.json"
  );
  const loader = new JSONLoader(filePath, ["/from", "/labels"]);
  const docs = await loader.load();

  expect(docs.length).toBe(6);
  expect(docs[3]).toEqual(
    new Document({
      metadata: { source: filePath, line: 4 },
      pageContent: "INBOX",
    })
  );
});

test("Test JSON loader for complex json with two existing keys on different level", async () => {
  const filePath = path.resolve(
    path.dirname(url.fileURLToPath(import.meta.url)),
    "./example_data/complex.json"
  );
  const loader = new JSONLoader(filePath, ["/from", "/surname"]);
  const docs = await loader.load();

  expect(docs.length).toBe(3);
  expect(docs[2]).toEqual(
    new Document({
      metadata: { source: filePath, line: 3 },
      pageContent: "bob",
    })
  );
});


import * as url from "node:url";
import * as path from "node:path";
import * as fs from "node:fs/promises";
import { test, expect } from "@jest/globals";
import { Document } from "@langchain/core/documents";
import { JSONLoader } from "../fs/json.js";

test("Test JSON loader from blob", async () => {
  const filePath = path.resolve(
    path.dirname(url.fileURLToPath(import.meta.url)),
    "./example_data/Star_Wars_The_Clone_Wars_S06E07_Crisis_at_the_Heart.json"
  );
  const loader = new JSONLoader(
    new Blob([await fs.readFile(filePath)], { type: "application/json" })
  );
  const docs = await loader.load();
  expect(docs.length).toBe(32);
  expect(docs[0]).toEqual(
    new Document({
      metadata: { source: "blob", blobType: "application/json", line: 1 },
      pageContent:
        "<i>Corruption discovered at the core of the Banking Clan!</i>",
    })
  );
});

test("Test JSON loader from blob", async () => {
  const loader = new JSONLoader(
    new Blob(
      [
        `{
  "texts": ["This is a sentence.", "This is another sentence."]
}`,
      ],
      { type: "application/json" }
    )
  );
  const docs = await loader.load();
  expect(docs.length).toBe(2);
  expect(docs[0]).toMatchInlineSnapshot(`
    Document {
      "id": undefined,
      "metadata": {
        "blobType": "application/json",
        "line": 1,
        "source": "blob",
      },
      "pageContent": "This is a sentence.",
    }
  `);
  expect(docs[1]).toMatchInlineSnapshot(`
    Document {
      "id": undefined,
      "metadata": {
        "blobType": "application/json",
        "line": 2,
        "source": "blob",
      },
      "pageContent": "This is another sentence.",
    }
  `);
});

test("Test JSON loader from blob", async () => {
  const loader = new JSONLoader(
    new Blob(
      [
        `{
  "1": {
    "body": "BD 2023 SUMMER",
    "from": "LinkedIn Job",
    "labels": ["IMPORTANT", "CATEGORY_UPDATES", "INBOX"]
  },
  "2": {
    "body": "Intern, Treasury and other roles are available",
    "from": "LinkedIn Job2",
    "labels": ["IMPORTANT"],
    "other": {
      "name": "plop",
      "surname": "bob"
    }
  }
}`,
      ],
      { type: "application/json" }
    )
  );
  const docs = await loader.load();
  expect(docs.length).toBe(10);
  expect(docs[0]).toMatchInlineSnapshot(`
    Document {
      "id": undefined,
      "metadata": {
        "blobType": "application/json",
        "line": 1,
        "source": "blob",
      },
      "pageContent": "BD 2023 SUMMER",
    }
  `);
  expect(docs[1]).toMatchInlineSnapshot(`
    Document {
      "id": undefined,
      "metadata": {
        "blobType": "application/json",
        "line": 2,
        "source": "blob",
      },
      "pageContent": "LinkedIn Job",
    }
  `);
});


import type { readFile as ReadFileT } from "node:fs/promises";
import { Document } from "@langchain/core/documents";
import { getEnv } from "@langchain/core/utils/env";
import { BaseDocumentLoader } from "../base.js";

/**
 * A class that extends the `BaseDocumentLoader` class. It represents a
 * document loader that loads documents from a text file. The `load()`
 * method is implemented to read the text from the file or blob, parse it
 * using the `parse()` method, and create a `Document` instance for each
 * parsed page. The metadata includes the source of the text (file path or
 * blob) and, if there are multiple pages, the line number of each page.
 * @example
 * ```typescript
 * const loader = new TextLoader("src/document_loaders/example_data/example.txt");
 * const docs = await loader.load();
 * ```
 */
export class TextLoader extends BaseDocumentLoader {
  constructor(public filePathOrBlob: string | Blob) {
    super();
  }

  /**
   * A protected method that takes a `raw` string as a parameter and returns
   * a promise that resolves to an array containing the raw text as a single
   * element.
   * @param raw The raw text to be parsed.
   * @returns A promise that resolves to an array containing the raw text as a single element.
   */
  protected async parse(raw: string): Promise<string[]> {
    return [raw];
  }

  /**
   * A method that loads the text file or blob and returns a promise that
   * resolves to an array of `Document` instances. It reads the text from
   * the file or blob using the `readFile` function from the
   * `node:fs/promises` module or the `text()` method of the blob. It then
   * parses the text using the `parse()` method and creates a `Document`
   * instance for each parsed page. The metadata includes the source of the
   * text (file path or blob) and, if there are multiple pages, the line
   * number of each page.
   * @returns A promise that resolves to an array of `Document` instances.
   */
  public async load(): Promise<Document[]> {
    let text: string;
    let metadata: Record<string, string>;
    if (typeof this.filePathOrBlob === "string") {
      const { readFile } = await TextLoader.imports();
      text = await readFile(this.filePathOrBlob, "utf8");
      metadata = { source: this.filePathOrBlob };
    } else {
      text = await this.filePathOrBlob.text();
      metadata = { source: "blob", blobType: this.filePathOrBlob.type };
    }
    const parsed = await this.parse(text);
    parsed.forEach((pageContent, i) => {
      if (typeof pageContent !== "string") {
        throw new Error(
          `Expected string, at position ${i} got ${typeof pageContent}`
        );
      }
    });
    return parsed.map(
      (pageContent, i) =>
        new Document({
          pageContent,
          metadata:
            parsed.length === 1
              ? metadata
              : {
                  ...metadata,
                  line: i + 1,
                },
        })
    );
  }

  /**
   * A static method that imports the `readFile` function from the
   * `node:fs/promises` module. It is used to dynamically import the
   * function when needed. If the import fails, it throws an error
   * indicating that the `fs/promises` module is not available in the
   * current environment.
   * @returns A promise that resolves to an object containing the `readFile` function from the `node:fs/promises` module.
   */
  static async imports(): Promise<{
    readFile: typeof ReadFileT;
  }> {
    try {
      const { readFile } = await import("node:fs/promises");
      return { readFile };
    } catch (e) {
      console.error(e);
      throw new Error(
        `Failed to load fs/promises. TextLoader available only on environment 'node'. It appears you are running environment '${getEnv()}'. See https://<link to docs> for alternatives.`
      );
    }
  }
}


import type { BaseLanguageModelInterface } from "@langchain/core/language_models/base";
import type { VectorStoreInterface } from "@langchain/core/vectorstores";
import { ChainValues } from "@langchain/core/utils/types";
import { CallbackManagerForChainRun } from "@langchain/core/callbacks/manager";
import { PromptTemplate } from "@langchain/core/prompts";
import { SerializedChatVectorDBQAChain } from "./serde.js";
import { BaseChain, ChainInputs } from "./base.js";
import { LLMChain } from "./llm_chain.js";
import { loadQAStuffChain } from "./question_answering/load.js";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type LoadValues = Record<string, any>;

const question_generator_template = `Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question.

Chat History:
{chat_history}
Follow Up Input: {question}
Standalone question:`;

const qa_template = `Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.

{context}

Question: {question}
Helpful Answer:`;

/**
 * Interface for the input parameters of the ChatVectorDBQAChain class.
 */
export interface ChatVectorDBQAChainInput extends ChainInputs {
  vectorstore: VectorStoreInterface;
  combineDocumentsChain: BaseChain;
  questionGeneratorChain: LLMChain;
  returnSourceDocuments?: boolean;
  outputKey?: string;
  inputKey?: string;
  k?: number;
}

/** @deprecated use `ConversationalRetrievalQAChain` instead. */
export class ChatVectorDBQAChain
  extends BaseChain
  implements ChatVectorDBQAChainInput
{
  k = 4;

  inputKey = "question";

  chatHistoryKey = "chat_history";

  get inputKeys() {
    return [this.inputKey, this.chatHistoryKey];
  }

  outputKey = "result";

  get outputKeys() {
    return [this.outputKey];
  }

  vectorstore: VectorStoreInterface;

  combineDocumentsChain: BaseChain;

  questionGeneratorChain: LLMChain;

  returnSourceDocuments = false;

  constructor(fields: ChatVectorDBQAChainInput) {
    super(fields);
    this.vectorstore = fields.vectorstore;
    this.combineDocumentsChain = fields.combineDocumentsChain;
    this.questionGeneratorChain = fields.questionGeneratorChain;
    this.inputKey = fields.inputKey ?? this.inputKey;
    this.outputKey = fields.outputKey ?? this.outputKey;
    this.k = fields.k ?? this.k;
    this.returnSourceDocuments =
      fields.returnSourceDocuments ?? this.returnSourceDocuments;
  }

  /** @ignore */
  async _call(
    values: ChainValues,
    runManager?: CallbackManagerForChainRun
  ): Promise<ChainValues> {
    if (!(this.inputKey in values)) {
      throw new Error(`Question key ${this.inputKey} not found.`);
    }
    if (!(this.chatHistoryKey in values)) {
      throw new Error(`chat history key ${this.inputKey} not found.`);
    }
    const question: string = values[this.inputKey];
    const chatHistory: string = values[this.chatHistoryKey];
    let newQuestion = question;
    if (chatHistory.length > 0) {
      const result = await this.questionGeneratorChain.call(
        {
          question,
          chat_history: chatHistory,
        },
        runManager?.getChild("question_generator")
      );
      const keys = Object.keys(result);
      console.log("_call", values, keys);
      if (keys.length === 1) {
        newQuestion = result[keys[0]];
      } else {
        throw new Error(
          "Return from llm chain has multiple values, only single values supported."
        );
      }
    }
    const docs = await this.vectorstore.similaritySearch(
      newQuestion,
      this.k,
      undefined,
      runManager?.getChild("vectorstore")
    );
    const inputs = {
      question: newQuestion,
      input_documents: docs,
      chat_history: chatHistory,
    };
    const result = await this.combineDocumentsChain.call(
      inputs,
      runManager?.getChild("combine_documents")
    );
    if (this.returnSourceDocuments) {
      return {
        ...result,
        sourceDocuments: docs,
      };
    }
    return result;
  }

  _chainType() {
    return "chat-vector-db" as const;
  }

  static async deserialize(
    data: SerializedChatVectorDBQAChain,
    values: LoadValues
  ) {
    if (!("vectorstore" in values)) {
      throw new Error(
        `Need to pass in a vectorstore to deserialize VectorDBQAChain`
      );
    }
    const { vectorstore } = values;

    return new ChatVectorDBQAChain({
      combineDocumentsChain: await BaseChain.deserialize(
        data.combine_documents_chain
      ),
      questionGeneratorChain: await LLMChain.deserialize(
        data.question_generator
      ),
      k: data.k,
      vectorstore,
    });
  }

  serialize(): SerializedChatVectorDBQAChain {
    return {
      _type: this._chainType(),
      combine_documents_chain: this.combineDocumentsChain.serialize(),
      question_generator: this.questionGeneratorChain.serialize(),
      k: this.k,
    };
  }

  /**
   * Creates an instance of ChatVectorDBQAChain using a BaseLanguageModel
   * and other options.
   * @param llm Instance of BaseLanguageModel used to generate a new question.
   * @param vectorstore Instance of VectorStore used for vector operations.
   * @param options (Optional) Additional options for creating the ChatVectorDBQAChain instance.
   * @returns New instance of ChatVectorDBQAChain.
   */
  static fromLLM(
    llm: BaseLanguageModelInterface,
    vectorstore: VectorStoreInterface,
    options: {
      inputKey?: string;
      outputKey?: string;
      k?: number;
      returnSourceDocuments?: boolean;
      questionGeneratorTemplate?: string;
      qaTemplate?: string;
      verbose?: boolean;
    } = {}
  ): ChatVectorDBQAChain {
    const { questionGeneratorTemplate, qaTemplate, verbose, ...rest } = options;
    const question_generator_prompt = PromptTemplate.fromTemplate(
      questionGeneratorTemplate || question_generator_template
    );
    const qa_prompt = PromptTemplate.fromTemplate(qaTemplate || qa_template);

    const qaChain = loadQAStuffChain(llm, { prompt: qa_prompt, verbose });
    const questionGeneratorChain = new LLMChain({
      prompt: question_generator_prompt,
      llm,
      verbose,
    });
    const instance = new this({
      vectorstore,
      combineDocumentsChain: qaChain,
      questionGeneratorChain,
      ...rest,
    });
    return instance;
  }
}


import type { BaseLanguageModelInterface } from "@langchain/core/language_models/base";
import type { BaseRetrieverInterface } from "@langchain/core/retrievers";
import { ChainValues } from "@langchain/core/utils/types";
import { CallbackManagerForChainRun } from "@langchain/core/callbacks/manager";
import { BaseChain, ChainInputs } from "./base.js";
import { SerializedVectorDBQAChain } from "./serde.js";
import {
  StuffQAChainParams,
  loadQAStuffChain,
} from "./question_answering/load.js";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type LoadValues = Record<string, any>;

/**
 * Interface for the input parameters of the RetrievalQAChain class.
 */
export interface RetrievalQAChainInput extends Omit<ChainInputs, "memory"> {
  retriever: BaseRetrieverInterface;
  combineDocumentsChain: BaseChain;
  inputKey?: string;
  returnSourceDocuments?: boolean;
}

/**
 * @deprecated This class will be removed in 1.0.0. See below for an example implementation using
 * `createRetrievalChain`:
 * Class representing a chain for performing question-answering tasks with
 * a retrieval component.
 * @example
 * ```typescript
 * import { createStuffDocumentsChain } from "langchain/chains/combine_documents";
 * import { ChatPromptTemplate } from "@langchain/core/prompts";
 * import { createRetrievalChain } from "langchain/chains/retrieval";
 * import { MemoryVectorStore } from "langchain/vectorstores/memory";
 *
 * const documents = [...your documents here];
 * const embeddings = ...your embeddings model;
 * const llm = ...your LLM model;
 *
 * const vectorstore = await MemoryVectorStore.fromDocuments(
 *   documents,
 *   embeddings
 * );
 * const prompt = ChatPromptTemplate.fromTemplate(`Answer the user's question: {input} based on the following context {context}`);
 *
 * const combineDocsChain = await createStuffDocumentsChain({
 *   llm,
 *   prompt,
 * });
 * const retriever = vectorstore.asRetriever();
 *
 * const retrievalChain = await createRetrievalChain({
 *   combineDocsChain,
 *   retriever,
 * });
 * ```
 */
export class RetrievalQAChain
  extends BaseChain
  implements RetrievalQAChainInput
{
  static lc_name() {
    return "RetrievalQAChain";
  }

  inputKey = "query";

  get inputKeys() {
    return [this.inputKey];
  }

  get outputKeys() {
    return this.combineDocumentsChain.outputKeys.concat(
      this.returnSourceDocuments ? ["sourceDocuments"] : []
    );
  }

  retriever: BaseRetrieverInterface;

  combineDocumentsChain: BaseChain;

  returnSourceDocuments = false;

  constructor(fields: RetrievalQAChainInput) {
    super(fields);
    this.retriever = fields.retriever;
    this.combineDocumentsChain = fields.combineDocumentsChain;
    this.inputKey = fields.inputKey ?? this.inputKey;
    this.returnSourceDocuments =
      fields.returnSourceDocuments ?? this.returnSourceDocuments;
  }

  /** @ignore */
  async _call(
    values: ChainValues,
    runManager?: CallbackManagerForChainRun
  ): Promise<ChainValues> {
    if (!(this.inputKey in values)) {
      throw new Error(`Question key "${this.inputKey}" not found.`);
    }
    const question: string = values[this.inputKey];
    const docs = await this.retriever.getRelevantDocuments(
      question,
      runManager?.getChild("retriever")
    );
    const inputs = { question, input_documents: docs, ...values };
    const result = await this.combineDocumentsChain.call(
      inputs,
      runManager?.getChild("combine_documents")
    );
    if (this.returnSourceDocuments) {
      return {
        ...result,
        sourceDocuments: docs,
      };
    }
    return result;
  }

  _chainType() {
    return "retrieval_qa" as const;
  }

  static async deserialize(
    _data: SerializedVectorDBQAChain,
    _values: LoadValues
  ): Promise<RetrievalQAChain> {
    throw new Error("Not implemented");
  }

  serialize(): SerializedVectorDBQAChain {
    throw new Error("Not implemented");
  }

  /**
   * Creates a new instance of RetrievalQAChain using a BaseLanguageModel
   * and a BaseRetriever.
   * @param llm The BaseLanguageModel used to generate a new question.
   * @param retriever The BaseRetriever used to retrieve relevant documents.
   * @param options Optional parameters for the RetrievalQAChain.
   * @returns A new instance of RetrievalQAChain.
   */
  static fromLLM(
    llm: BaseLanguageModelInterface,
    retriever: BaseRetrieverInterface,
    options?: Partial<
      Omit<
        RetrievalQAChainInput,
        "retriever" | "combineDocumentsChain" | "index"
      >
    > &
      StuffQAChainParams
  ): RetrievalQAChain {
    const qaChain = loadQAStuffChain(llm, {
      prompt: options?.prompt,
    });
    return new this({
      ...options,
      retriever,
      combineDocumentsChain: qaChain,
    });
  }
}


import { PromptTemplate } from "@langchain/core/prompts";
import { LLMChain, LLMChainInput } from "./llm_chain.js";
import { BufferMemory } from "../memory/buffer_memory.js";
import { Optional } from "../types/type-utils.js";

export const DEFAULT_TEMPLATE = `The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:
{history}
Human: {input}
AI:`;

/**
 * A class for conducting conversations between a human and an AI. It
 * extends the {@link LLMChain} class.
 * @example
 * ```typescript
 * const model = new ChatOpenAI({});
 * const chain = new ConversationChain({ llm: model });
 *
 * // Sending a greeting to the conversation chain
 * const res1 = await chain.call({ input: "Hi! I'm Jim." });
 * console.log({ res1 });
 *
 * // Following up with a question in the conversation
 * const res2 = await chain.call({ input: "What's my name?" });
 * console.log({ res2 });
 * ```
 */
export class ConversationChain extends LLMChain {
  static lc_name() {
    return "ConversationChain";
  }

  constructor({
    prompt,
    outputKey,
    memory,
    ...rest
  }: Optional<LLMChainInput, "prompt">) {
    super({
      prompt:
        prompt ??
        new PromptTemplate({
          template: DEFAULT_TEMPLATE,
          inputVariables: ["history", "input"],
        }),
      outputKey: outputKey ?? "response",
      memory: memory ?? new BufferMemory(),
      ...rest,
    });
  }
}


import {
  BaseLanguageModel,
  BaseLanguageModelInterface,
  BaseLanguageModelInput,
} from "@langchain/core/language_models/base";
import type { ChainValues } from "@langchain/core/utils/types";
import type { Generation } from "@langchain/core/outputs";
import type { BaseMessage } from "@langchain/core/messages";
import type { BasePromptValueInterface } from "@langchain/core/prompt_values";
import { BasePromptTemplate } from "@langchain/core/prompts";
import {
  BaseLLMOutputParser,
  BaseOutputParser,
} from "@langchain/core/output_parsers";
import {
  CallbackManager,
  BaseCallbackConfig,
  CallbackManagerForChainRun,
  Callbacks,
} from "@langchain/core/callbacks/manager";
import { Runnable, type RunnableInterface } from "@langchain/core/runnables";
import { BaseChain, ChainInputs } from "./base.js";
import { SerializedLLMChain } from "./serde.js";
import { NoOpOutputParser } from "../output_parsers/noop.js";

type LLMType =
  | BaseLanguageModelInterface
  | Runnable<BaseLanguageModelInput, string>
  | Runnable<BaseLanguageModelInput, BaseMessage>;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type CallOptionsIfAvailable<T> = T extends { CallOptions: infer CO } ? CO : any;
/**
 * Interface for the input parameters of the LLMChain class.
 */
export interface LLMChainInput<
  T extends string | object = string,
  Model extends LLMType = LLMType
> extends ChainInputs {
  /** Prompt object to use */
  prompt: BasePromptTemplate;
  /** LLM Wrapper to use */
  llm: Model;
  /** Kwargs to pass to LLM */
  llmKwargs?: CallOptionsIfAvailable<Model>;
  /** OutputParser to use */
  outputParser?: BaseLLMOutputParser<T>;
  /** Key to use for output, defaults to `text` */
  outputKey?: string;
}

function isBaseLanguageModel(llmLike: unknown): llmLike is BaseLanguageModel {
  return typeof (llmLike as BaseLanguageModelInterface)._llmType === "function";
}

function _getLanguageModel(llmLike: RunnableInterface): BaseLanguageModel {
  if (isBaseLanguageModel(llmLike)) {
    return llmLike;
  } else if ("bound" in llmLike && Runnable.isRunnable(llmLike.bound)) {
    return _getLanguageModel(llmLike.bound);
  } else if (
    "runnable" in llmLike &&
    "fallbacks" in llmLike &&
    Runnable.isRunnable(llmLike.runnable)
  ) {
    return _getLanguageModel(llmLike.runnable);
  } else if ("default" in llmLike && Runnable.isRunnable(llmLike.default)) {
    return _getLanguageModel(llmLike.default);
  } else {
    throw new Error("Unable to extract BaseLanguageModel from llmLike object.");
  }
}

/**
 * @deprecated This class will be removed in 1.0.0. Use the LangChain Expression Language (LCEL) instead.
 * See the example below for how to use LCEL with the LLMChain class:
 *
 * Chain to run queries against LLMs.
 *
 * @example
 * ```ts
 * import { ChatPromptTemplate } from "@langchain/core/prompts";
 * import { ChatOpenAI } from "@langchain/openai";
 *
 * const prompt = ChatPromptTemplate.fromTemplate("Tell me a {adjective} joke");
 * const llm = new ChatOpenAI();
 * const chain = prompt.pipe(llm);
 *
 * const response = await chain.invoke({ adjective: "funny" });
 * ```
 */
export class LLMChain<
    T extends string | object = string,
    Model extends LLMType = LLMType
  >
  extends BaseChain
  implements LLMChainInput<T>
{
  static lc_name() {
    return "LLMChain";
  }

  lc_serializable = true;

  prompt: BasePromptTemplate;

  llm: Model;

  llmKwargs?: CallOptionsIfAvailable<Model>;

  outputKey = "text";

  outputParser?: BaseLLMOutputParser<T>;

  get inputKeys() {
    return this.prompt.inputVariables;
  }

  get outputKeys() {
    return [this.outputKey];
  }

  constructor(fields: LLMChainInput<T, Model>) {
    super(fields);
    this.prompt = fields.prompt;
    this.llm = fields.llm;
    this.llmKwargs = fields.llmKwargs;
    this.outputKey = fields.outputKey ?? this.outputKey;
    this.outputParser =
      fields.outputParser ?? (new NoOpOutputParser() as BaseOutputParser<T>);
    if (this.prompt.outputParser) {
      if (fields.outputParser) {
        throw new Error("Cannot set both outputParser and prompt.outputParser");
      }
      this.outputParser = this.prompt.outputParser as BaseOutputParser<T>;
    }
  }

  private getCallKeys(): string[] {
    const callKeys = "callKeys" in this.llm ? this.llm.callKeys : [];
    return callKeys;
  }

  /** @ignore */
  _selectMemoryInputs(values: ChainValues): ChainValues {
    const valuesForMemory = super._selectMemoryInputs(values);
    const callKeys = this.getCallKeys();
    for (const key of callKeys) {
      if (key in values) {
        delete valuesForMemory[key];
      }
    }
    return valuesForMemory;
  }

  /** @ignore */
  async _getFinalOutput(
    generations: Generation[],
    promptValue: BasePromptValueInterface,
    runManager?: CallbackManagerForChainRun
  ): Promise<unknown> {
    let finalCompletion: unknown;
    if (this.outputParser) {
      finalCompletion = await this.outputParser.parseResultWithPrompt(
        generations,
        promptValue,
        runManager?.getChild()
      );
    } else {
      finalCompletion = generations[0].text;
    }
    return finalCompletion;
  }

  /**
   * Run the core logic of this chain and add to output if desired.
   *
   * Wraps _call and handles memory.
   */
  call(
    values: ChainValues & CallOptionsIfAvailable<Model>,
    config?: Callbacks | BaseCallbackConfig
  ): Promise<ChainValues> {
    return super.call(values, config);
  }

  /** @ignore */
  async _call(
    values: ChainValues & CallOptionsIfAvailable<Model>,
    runManager?: CallbackManagerForChainRun
  ): Promise<ChainValues> {
    const valuesForPrompt = { ...values };
    const valuesForLLM = {
      ...this.llmKwargs,
    } as CallOptionsIfAvailable<Model>;
    const callKeys = this.getCallKeys();
    for (const key of callKeys) {
      if (key in values) {
        if (valuesForLLM) {
          valuesForLLM[key as keyof CallOptionsIfAvailable<Model>] =
            values[key];
          delete valuesForPrompt[key];
        }
      }
    }
    const promptValue = await this.prompt.formatPromptValue(valuesForPrompt);
    if ("generatePrompt" in this.llm) {
      const { generations } = await this.llm.generatePrompt(
        [promptValue],
        valuesForLLM,
        runManager?.getChild()
      );
      return {
        [this.outputKey]: await this._getFinalOutput(
          generations[0],
          promptValue,
          runManager
        ),
      };
    }

    const modelWithParser = this.outputParser
      ? this.llm.pipe(this.outputParser)
      : this.llm;
    const response = await modelWithParser.invoke(
      promptValue,
      runManager?.getChild()
    );
    return {
      [this.outputKey]: response,
    };
  }

  /**
   * Format prompt with values and pass to LLM
   *
   * @param values - keys to pass to prompt template
   * @param callbackManager - CallbackManager to use
   * @returns Completion from LLM.
   *
   * @example
   * ```ts
   * llm.predict({ adjective: "funny" })
   * ```
   */
  async predict(
    values: ChainValues & CallOptionsIfAvailable<Model>,
    callbackManager?: CallbackManager
  ): Promise<T> {
    const output = await this.call(values, callbackManager);
    return output[this.outputKey];
  }

  _chainType() {
    return "llm" as const;
  }

  static async deserialize(data: SerializedLLMChain) {
    const { llm, prompt } = data;
    if (!llm) {
      throw new Error("LLMChain must have llm");
    }
    if (!prompt) {
      throw new Error("LLMChain must have prompt");
    }

    return new LLMChain({
      llm: await BaseLanguageModel.deserialize(llm),
      prompt: await BasePromptTemplate.deserialize(prompt),
    });
  }

  /** @deprecated */
  serialize(): SerializedLLMChain {
    const serialize =
      "serialize" in this.llm ? this.llm.serialize() : undefined;
    return {
      _type: `${this._chainType()}_chain`,
      llm: serialize,
      prompt: this.prompt.serialize(),
    };
  }

  _getNumTokens(text: string): Promise<number> {
    return _getLanguageModel(this.llm).getNumTokens(text);
  }
}


export class ConversationalRetrievalQAChain
  extends BaseChain
  implements ConversationalRetrievalQAChainInput
{
  static lc_name() {
    return "ConversationalRetrievalQAChain";
  }

  inputKey = "question";

  chatHistoryKey = "chat_history";

  get inputKeys() {
    return [this.inputKey, this.chatHistoryKey];
  }

  get outputKeys() {
    return this.combineDocumentsChain.outputKeys.concat(
      this.returnSourceDocuments ? ["sourceDocuments"] : []
    );
  }

  retriever: BaseRetrieverInterface;

  combineDocumentsChain: BaseChain;

  questionGeneratorChain: LLMChain;

  returnSourceDocuments = false;

  returnGeneratedQuestion = false;

  constructor(fields: ConversationalRetrievalQAChainInput) {
    super(fields);
    this.retriever = fields.retriever;
    this.combineDocumentsChain = fields.combineDocumentsChain;
    this.questionGeneratorChain = fields.questionGeneratorChain;
    this.inputKey = fields.inputKey ?? this.inputKey;
    this.returnSourceDocuments =
      fields.returnSourceDocuments ?? this.returnSourceDocuments;
    this.returnGeneratedQuestion =
      fields.returnGeneratedQuestion ?? this.returnGeneratedQuestion;
  }

  /**
   * Static method to convert the chat history input into a formatted
   * string.
   * @param chatHistory Chat history input which can be a string, an array of BaseMessage instances, or an array of string arrays.
   * @returns A formatted string representing the chat history.
   */
  static getChatHistoryString(
    chatHistory: string | BaseMessage[] | string[][]
  ) {
    let historyMessages: BaseMessage[];
    if (Array.isArray(chatHistory)) {
      // TODO: Deprecate on a breaking release
      if (
        Array.isArray(chatHistory[0]) &&
        typeof chatHistory[0][0] === "string"
      ) {
        console.warn(
          "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."
        );
        historyMessages = chatHistory.flat().map((stringMessage, i) => {
          if (i % 2 === 0) {
            return new HumanMessage(stringMessage);
          } else {
            return new AIMessage(stringMessage);
          }
        });
      } else {
        historyMessages = chatHistory as BaseMessage[];
      }
      return historyMessages
        .map((chatMessage) => {
          if (chatMessage._getType() === "human") {
            return `Human: ${chatMessage.content}`;
          } else if (chatMessage._getType() === "ai") {
            return `Assistant: ${chatMessage.content}`;
          } else {
            return `${chatMessage.content}`;
          }
        })
        .join("\n");
    }
    return chatHistory;
  }

  /** @ignore */
  async _call(
    values: ChainValues,
    runManager?: CallbackManagerForChainRun
  ): Promise<ChainValues> {
    if (!(this.inputKey in values)) {
      throw new Error(`Question key ${this.inputKey} not found.`);
    }
    if (!(this.chatHistoryKey in values)) {
      throw new Error(`Chat history key ${this.chatHistoryKey} not found.`);
    }
    const question: string = values[this.inputKey];
    const chatHistory: string =
      ConversationalRetrievalQAChain.getChatHistoryString(
        values[this.chatHistoryKey]
      );
    let newQuestion = question;
    if (chatHistory.length > 0) {
      const result = await this.questionGeneratorChain.call(
        {
          question,
          chat_history: chatHistory,
        },
        runManager?.getChild("question_generator")
      );
      const keys = Object.keys(result);
      if (keys.length === 1) {
        newQuestion = result[keys[0]];
      } else {
        throw new Error(
          "Return from llm chain has multiple values, only single values supported."
        );
      }
    }
    const docs = await this.retriever.getRelevantDocuments(
      newQuestion,
      runManager?.getChild("retriever")
    );
    const inputs = {
      question: newQuestion,
      input_documents: docs,
      chat_history: chatHistory,
    };
    let result = await this.combineDocumentsChain.call(
      inputs,
      runManager?.getChild("combine_documents")
    );
    if (this.returnSourceDocuments) {
      result = {
        ...result,
        sourceDocuments: docs,
      };
    }
    if (this.returnGeneratedQuestion) {
      result = {
        ...result,
        generatedQuestion: newQuestion,
      };
    }
    return result;
  }

  _chainType(): string {
    return "conversational_retrieval_chain";
  }

  static async deserialize(
    _data: SerializedChatVectorDBQAChain,
    _values: LoadValues
  ): Promise<ConversationalRetrievalQAChain> {
    throw new Error("Not implemented.");
  }

  serialize(): SerializedChatVectorDBQAChain {
    throw new Error("Not implemented.");
  }

  /**
   * Static method to create a new ConversationalRetrievalQAChain from a
   * BaseLanguageModel and a BaseRetriever.
   * @param llm {@link BaseLanguageModelInterface} instance used to generate a new question.
   * @param retriever {@link BaseRetrieverInterface} instance used to retrieve relevant documents.
   * @param options.returnSourceDocuments Whether to return source documents in the final output
   * @param options.questionGeneratorChainOptions Options to initialize the standalone question generation chain used as the first internal step
   * @param options.qaChainOptions {@link QAChainParams} used to initialize the QA chain used as the second internal step
   * @returns A new instance of ConversationalRetrievalQAChain.
   */
  static fromLLM(
    llm: BaseLanguageModelInterface,
    retriever: BaseRetrieverInterface,
    options: {
      outputKey?: string; // not used
      returnSourceDocuments?: boolean;
      /** @deprecated Pass in questionGeneratorChainOptions.template instead */
      questionGeneratorTemplate?: string;
      /** @deprecated Pass in qaChainOptions.prompt instead */
      qaTemplate?: string;
      questionGeneratorChainOptions?: {
        llm?: BaseLanguageModelInterface;
        template?: string;
      };
      qaChainOptions?: QAChainParams;
    } & Omit<
      ConversationalRetrievalQAChainInput,
      "retriever" | "combineDocumentsChain" | "questionGeneratorChain"
    > = {}
  ): ConversationalRetrievalQAChain {
    const {
      questionGeneratorTemplate,
      qaTemplate,
      qaChainOptions = {
        type: "stuff",
        prompt: qaTemplate
          ? PromptTemplate.fromTemplate(qaTemplate)
          : undefined,
      },
      questionGeneratorChainOptions,
      verbose,
      ...rest
    } = options;

    const qaChain = loadQAChain(llm, qaChainOptions);

    const questionGeneratorChainPrompt = PromptTemplate.fromTemplate(
      questionGeneratorChainOptions?.template ??
        questionGeneratorTemplate ??
        question_generator_template
    );
    const questionGeneratorChain = new LLMChain({
      prompt: questionGeneratorChainPrompt,
      llm: questionGeneratorChainOptions?.llm ?? llm,
      verbose,
    });
    const instance = new this({
      retriever,
      combineDocumentsChain: qaChain,
      questionGeneratorChain,
      verbose,
      ...rest,
    });
    return instance;
  }
}


/* eslint-disable spaced-comment */
import {
  ChatPromptTemplate,
  SystemMessagePromptTemplate,
  HumanMessagePromptTemplate,
  PromptTemplate,
} from "@langchain/core/prompts";
import {
  ConditionalPromptSelector,
  isChatModel,
} from "@langchain/core/example_selectors";

export const DEFAULT_QA_PROMPT = /*#__PURE__*/ new PromptTemplate({
  template:
    "Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.\n\n{context}\n\nQuestion: {question}\nHelpful Answer:",
  inputVariables: ["context", "question"],
});

const system_template = `Use the following pieces of context to answer the users question.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
----------------
{context}`;
const messages = [
  /*#__PURE__*/ SystemMessagePromptTemplate.fromTemplate(system_template),
  /*#__PURE__*/ HumanMessagePromptTemplate.fromTemplate("{question}"),
];
const CHAT_PROMPT = /*#__PURE__*/ ChatPromptTemplate.fromMessages(messages);

export const QA_PROMPT_SELECTOR = /*#__PURE__*/ new ConditionalPromptSelector(
  DEFAULT_QA_PROMPT,
  [[isChatModel, CHAT_PROMPT]]
);


import { test } from "@jest/globals";
import { OpenAI } from "@langchain/openai";
import { Document } from "@langchain/core/documents";
import {
  loadQAMapReduceChain,
  loadQARefineChain,
  loadQAStuffChain,
} from "../load.js";

test("Test loadQAStuffChain", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const chain = loadQAStuffChain(model);
  const docs = [
    new Document({ pageContent: "foo" }),
    new Document({ pageContent: "bar" }),
    new Document({ pageContent: "baz" }),
  ];
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.call({ input_documents: docs, question: "Whats up" });
  // console.log({ res });
});

test("Test loadQAMapReduceChain", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const chain = loadQAMapReduceChain(model);
  const docs = [
    new Document({ pageContent: "foo" }),
    new Document({ pageContent: "bar" }),
    new Document({ pageContent: "baz" }),
  ];
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.call({ input_documents: docs, question: "Whats up" });
  // console.log({ res });
});

test("Test loadQARefineChain", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const chain = loadQARefineChain(model);
  const docs = [
    new Document({ pageContent: "Harrison went to Harvard." }),
    new Document({ pageContent: "Ankush went to Princeton." }),
  ];
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.call({
    input_documents: docs,
    question: "Where did Harrison go to college?",
  });
  // console.log({ res });
});


import { test } from "@jest/globals";
import { OpenAI, ChatOpenAI } from "@langchain/openai";
import {
  ChatPromptTemplate,
  HumanMessagePromptTemplate,
  PromptTemplate,
} from "@langchain/core/prompts";
import { LLMChain } from "../llm_chain.js";
import { BufferMemory } from "../../memory/buffer_memory.js";

test("Test OpenAI", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const prompt = new PromptTemplate({
    template: "Print {foo}",
    inputVariables: ["foo"],
  });
  const chain = new LLMChain({ prompt, llm: model });
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.call({ foo: "my favorite color" });
  // console.log({ res });
});

test("Test OpenAI with timeout", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const prompt = new PromptTemplate({
    template: "Print {foo}",
    inputVariables: ["foo"],
  });
  const chain = new LLMChain({ prompt, llm: model });
  await expect(() =>
    chain.call({
      foo: "my favorite color",
      timeout: 10,
    })
  ).rejects.toThrow();
});

test("Test run method", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const prompt = new PromptTemplate({
    template: "Print {foo}",
    inputVariables: ["foo"],
  });
  const chain = new LLMChain({ prompt, llm: model });
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.run("my favorite color");
  // console.log({ res });
});

test("Test run method", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const prompt = new PromptTemplate({
    template: "{history} Print {foo}",
    inputVariables: ["foo", "history"],
  });
  const chain = new LLMChain({
    prompt,
    llm: model,
    memory: new BufferMemory(),
  });
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.run("my favorite color");
  // console.log({ res });
});

test("Test memory + cancellation", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const prompt = new PromptTemplate({
    template: "{history} Print {foo}",
    inputVariables: ["foo", "history"],
  });
  const chain = new LLMChain({
    prompt,
    llm: model,
    memory: new BufferMemory(),
  });
  await expect(() =>
    chain.call({
      foo: "my favorite color",
      signal: AbortSignal.timeout(20),
    })
  ).rejects.toThrow();
});

test("Test memory + timeout", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const prompt = new PromptTemplate({
    template: "{history} Print {foo}",
    inputVariables: ["foo", "history"],
  });
  const chain = new LLMChain({
    prompt,
    llm: model,
    memory: new BufferMemory(),
  });
  await expect(() =>
    chain.call({
      foo: "my favorite color",
      timeout: 20,
    })
  ).rejects.toThrow();
});

test("Test apply", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const prompt = new PromptTemplate({
    template: "Print {foo}",
    inputVariables: ["foo"],
  });
  const chain = new LLMChain({ prompt, llm: model });
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.apply([{ foo: "my favorite color" }]);
  // console.log({ res });
});

test("Test LLMChain with ChatOpenAI", async () => {
  const model = new ChatOpenAI({ temperature: 0.9 });
  const template = "What is a good name for a company that makes {product}?";
  const prompt = new PromptTemplate({ template, inputVariables: ["product"] });
  const humanMessagePrompt = new HumanMessagePromptTemplate(prompt);
  const chatPromptTemplate = ChatPromptTemplate.fromMessages([
    humanMessagePrompt,
  ]);
  const chatChain = new LLMChain({ llm: model, prompt: chatPromptTemplate });
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chatChain.call({ product: "colorful socks" });
  // console.log({ res });
});

test("Test passing a runnable to an LLMChain", async () => {
  const model = new ChatOpenAI({ modelName: "gpt-3.5-turbo-1106" });
  const runnableModel = model.bind({
    response_format: {
      type: "json_object",
    },
  });
  const prompt = PromptTemplate.fromTemplate(
    "You are a bee --I mean a spelling bee. Respond with a JSON key of 'spelling':\nQuestion:{input}"
  );
  const chain = new LLMChain({ llm: runnableModel, prompt });
  const response = await chain.invoke({ input: "How do you spell today?" });
  expect(JSON.parse(response.text)).toMatchObject({
    spelling: expect.any(String),
  });
});


import { test, expect } from "@jest/globals";
import { Document } from "@langchain/core/documents";
import { BaseLLM } from "@langchain/core/language_models/llms";
import { LLMResult } from "@langchain/core/outputs";
import { loadQAMapReduceChain } from "../question_answering/load.js";
import { loadSummarizationChain } from "../index.js";

class FakeLLM extends BaseLLM {
  nrMapCalls = 0;

  nrReduceCalls = 0;

  _llmType(): string {
    return "fake";
  }

  async _generate(prompts: string[]): Promise<LLMResult> {
    return {
      generations: prompts.map((prompt) => {
        let completion = "";
        if (prompt.startsWith("Use the following portion")) {
          this.nrMapCalls += 1;
          completion = "a portion of context";
        } else if (prompt.startsWith("Given the following extracted")) {
          this.nrReduceCalls += 1;
          completion = "a final answer";
        }
        return [
          {
            text: completion,
            score: 0,
          },
        ];
      }),
    };
  }
}

test("Test MapReduceDocumentsChain", async () => {
  const model = new FakeLLM({});
  const chain = loadQAMapReduceChain(model);
  const docs = [
    new Document({ pageContent: "harrison went to harvard" }),
    new Document({ pageContent: "ankush went to princeton" }),
  ];

  const res = await chain.invoke({
    input_documents: docs,
    question: "Where did harrison go to college",
  });
  // console.log({ res });

  expect(res).toEqual({
    text: "a final answer",
  });
  expect(model.nrMapCalls).toBe(0); // below maxTokens
  expect(model.nrReduceCalls).toBe(1);
});

test("Test MapReduceDocumentsChain with content above maxTokens and intermediate steps", async () => {
  const model = new FakeLLM({});
  const chain = loadQAMapReduceChain(model, {
    returnIntermediateSteps: true,
  });
  const aString = "a".repeat(4000);
  const bString = "b".repeat(4000);
  const docs = [
    new Document({ pageContent: aString }),
    new Document({ pageContent: bString }),
  ];

  const res = await chain.invoke({
    input_documents: docs,
    question: "Is the letter c present in the document",
  });
  // console.log({ res });

  expect(res).toEqual({
    text: "a final answer",
    intermediateSteps: ["a portion of context", "a portion of context"],
  });
  expect(model.nrMapCalls).toBe(2); // above maxTokens
  expect(model.nrReduceCalls).toBe(1);
});

test("Test RefineDocumentsChain", async () => {
  const model = new FakeLLM({});
  const chain = loadSummarizationChain(model, { type: "refine" });
  const docs = [
    new Document({ pageContent: "harrison went to harvard" }),
    new Document({ pageContent: "ankush went to princeton" }),
  ];

  expect(chain.inputKeys).toEqual(["input_documents"]);

  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.run(docs);
  // console.log({ res });
});


import { test } from "@jest/globals";
import { OpenAI } from "@langchain/openai";
import { ConversationChain } from "../conversation.js";

test("Test ConversationChain", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const chain = new ConversationChain({ llm: model });
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.call({ input: "my favorite color" });
  // console.log({ res });
});


import { expect, test } from "@jest/globals";
import { OpenAI, OpenAIEmbeddings, ChatOpenAI } from "@langchain/openai";
import { PromptTemplate } from "@langchain/core/prompts";
import { ConversationalRetrievalQAChain } from "../conversational_retrieval_chain.js";
import { MemoryVectorStore } from "../../vectorstores/memory.js";
import { BufferMemory } from "../../memory/buffer_memory.js";

test("Test ConversationalRetrievalQAChain from LLM", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const vectorStore = await MemoryVectorStore.fromTexts(
    ["Hello world", "Bye bye", "hello nice world", "bye", "hi"],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );
  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever()
  );
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.call({ question: "foo", chat_history: "bar" });
  // console.log({ res });
});

test("Test ConversationalRetrievalQAChain from LLM with flag option to return source", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const vectorStore = await MemoryVectorStore.fromTexts(
    ["Hello world", "Bye bye", "hello nice world", "bye", "hi"],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );
  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever(),
    {
      returnSourceDocuments: true,
    }
  );
  const res = await chain.call({ question: "foo", chat_history: "bar" });

  expect(res).toEqual(
    expect.objectContaining({
      text: expect.any(String),
      sourceDocuments: expect.arrayContaining([
        expect.objectContaining({
          metadata: expect.objectContaining({
            id: expect.any(Number),
          }),
          pageContent: expect.any(String),
        }),
      ]),
    })
  );
});

test("Test ConversationalRetrievalQAChain from LLM with flag option to return source and memory set", async () => {
  const model = new OpenAI({ modelName: "gpt-3.5-turbo-instruct" });
  const vectorStore = await MemoryVectorStore.fromTexts(
    ["Hello world", "Bye bye", "hello nice world", "bye", "hi"],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );
  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever(),
    {
      returnSourceDocuments: true,
      memory: new BufferMemory({
        memoryKey: "chat_history",
        inputKey: "question",
        outputKey: "text",
      }),
    }
  );
  const res = await chain.call({ question: "foo", chat_history: "bar" });

  expect(res).toEqual(
    expect.objectContaining({
      text: expect.any(String),
      sourceDocuments: expect.arrayContaining([
        expect.objectContaining({
          metadata: expect.objectContaining({
            id: expect.any(Number),
          }),
          pageContent: expect.any(String),
        }),
      ]),
    })
  );
});

test("Test ConversationalRetrievalQAChain from LLM with override default prompts", async () => {
  const model = new OpenAI({
    modelName: "gpt-3.5-turbo-instruct",
    temperature: 0,
  });
  const vectorStore = await MemoryVectorStore.fromTexts(
    ["Hello world", "Bye bye", "hello nice world", "bye", "hi"],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );

  const qa_template = `Use the following pieces of context to answer the question at the end. If you don't know the answer, just say "Sorry I dont know, I am learning from Aliens", don't try to make up an answer.
  {context}

  Question: {question}
  Helpful Answer:`;

  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever(),
    {
      qaTemplate: qa_template,
    }
  );
  const res = await chain.call({
    question: "What is better programming Language Python or Javascript ",
    chat_history: "bar",
  });
  expect(res.text).toContain("I am learning from Aliens");
  // console.log({ res });
});

test("Test ConversationalRetrievalQAChain from LLM with a chat model", async () => {
  const model = new ChatOpenAI({
    modelName: "gpt-3.5-turbo",
    temperature: 0,
  });
  const vectorStore = await MemoryVectorStore.fromTexts(
    ["Hello world", "Bye bye", "hello nice world", "bye", "hi"],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );
  const qa_template = `Use the following pieces of context to answer the question at the end. If you don't know the answer, just say "Sorry I dont know, I am learning from Aliens", don't try to make up an answer.
  {context}

  Question: {question}
  Helpful Answer:`;

  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever(),
    {
      qaChainOptions: {
        type: "stuff",
        prompt: PromptTemplate.fromTemplate(qa_template),
      },
    }
  );
  const res = await chain.call({
    question: "What is better programming Language Python or Javascript ",
    chat_history: "bar",
  });
  expect(res.text).toContain("I am learning from Aliens");
  // console.log({ res });
});

test("Test ConversationalRetrievalQAChain from LLM with a map reduce chain", async () => {
  const model = new ChatOpenAI({
    modelName: "gpt-3.5-turbo",
    temperature: 0,
  });
  const vectorStore = await MemoryVectorStore.fromTexts(
    ["Hello world", "Bye bye", "hello nice world", "bye", "hi"],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );

  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever(),
    {
      qaChainOptions: {
        type: "map_reduce",
      },
    }
  );
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.call({
    question: "What is better programming Language Python or Javascript ",
    chat_history: "bar",
  });

  // console.log({ res });
});

test("Test ConversationalRetrievalQAChain from LLM without memory", async () => {
  const model = new OpenAI({
    temperature: 0,
  });
  const vectorStore = await MemoryVectorStore.fromTexts(
    [
      "Mitochondria are the powerhouse of the cell",
      "Foo is red",
      "Bar is red",
      "Buildings are made out of brick",
      "Mitochondria are made of lipids",
    ],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );

  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever()
  );
  const question = "What is the powerhouse of the cell?";
  const res = await chain.call({
    question,
    chat_history: "",
  });

  // console.log({ res });

  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res2 = await chain.call({
    question: "What are they made out of?",
    chat_history: question + res.text,
  });

  // console.log({ res2 });
});


test("Test ConversationalRetrievalQAChain from LLM with a chat model without memory", async () => {
  const model = new ChatOpenAI({
    modelName: "gpt-3.5-turbo",
    temperature: 0,
  });
  const vectorStore = await MemoryVectorStore.fromTexts(
    [
      "Mitochondria are the powerhouse of the cell",
      "Foo is red",
      "Bar is red",
      "Buildings are made out of brick",
      "Mitochondria are made of lipids",
    ],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );

  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever()
  );
  const question = "What is the powerhouse of the cell?";
  const res = await chain.call({
    question,
    chat_history: "",
  });

  // console.log({ res });

  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res2 = await chain.call({
    question: "What are they made out of?",
    chat_history: question + res.text,
  });

  // console.log({ res2 });
});

test("Test ConversationalRetrievalQAChain from LLM with memory", async () => {
  const model = new OpenAI({
    temperature: 0,
  });
  const vectorStore = await MemoryVectorStore.fromTexts(
    [
      "Mitochondria are the powerhouse of the cell",
      "Foo is red",
      "Bar is red",
      "Buildings are made out of brick",
      "Mitochondria are made of lipids",
    ],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );

  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever(),
    {
      memory: new BufferMemory({
        memoryKey: "chat_history",
      }),
    }
  );
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.call({
    question: "What is the powerhouse of the cell?",
  });

  // console.log({ res });

  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res2 = await chain.call({
    question: "What are they made out of?",
  });

  // console.log({ res2 });
});

test("Test ConversationalRetrievalQAChain from LLM with a chat model and memory", async () => {
  const model = new ChatOpenAI({
    modelName: "gpt-3.5-turbo",
    temperature: 0,
  });
  const vectorStore = await MemoryVectorStore.fromTexts(
    [
      "Mitochondria are the powerhouse of the cell",
      "Foo is red",
      "Bar is red",
      "Buildings are made out of brick",
      "Mitochondria are made of lipids",
    ],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );

  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever(),
    {
      memory: new BufferMemory({
        memoryKey: "chat_history",
        returnMessages: true,
      }),
    }
  );
  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res = await chain.call({
    question: "What is the powerhouse of the cell?",
  });

  // console.log({ res });

  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res2 = await chain.call({
    question: "What are they made out of?",
  });

  // console.log({ res2 });
});

test("Test ConversationalRetrievalQAChain from LLM with deprecated history syntax", async () => {
  const model = new OpenAI({
    temperature: 0,
  });
  const vectorStore = await MemoryVectorStore.fromTexts(
    [
      "Mitochondria are the powerhouse of the cell",
      "Foo is red",
      "Bar is red",
      "Buildings are made out of brick",
      "Mitochondria are made of lipids",
    ],
    [{ id: 2 }, { id: 1 }, { id: 3 }, { id: 4 }, { id: 5 }],
    new OpenAIEmbeddings()
  );

  const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever()
  );
  const question = "What is the powerhouse of the cell?";
  const res = await chain.call({
    question,
    chat_history: [],
  });

  // console.log({ res });

  // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
  // @ts-expect-error unused var
  const res2 = await chain.call({
    question: "What are they made out of?",
    chat_history: [[question, res.text]],
  });

  // console.log({ res2 });
});


/* eslint-disable spaced-comment */
import { PromptTemplate } from "@langchain/core/prompts";

export const API_URL_RAW_PROMPT_TEMPLATE = `You are given the below API Documentation:
{api_docs}
Using this documentation, generate the full API url to call for answering the user question.
You should build the API url in order to get a response that is as short as possible, while still getting the necessary information to answer the question. Pay attention to deliberately exclude any unnecessary pieces of data in the API call.

Question:{question}
API url:`;

export const API_URL_PROMPT_TEMPLATE = /* #__PURE__ */ new PromptTemplate({
  inputVariables: ["api_docs", "question"],
  template: API_URL_RAW_PROMPT_TEMPLATE,
});

export const API_RESPONSE_RAW_PROMPT_TEMPLATE = `${API_URL_RAW_PROMPT_TEMPLATE} {api_url}

Here is the response from the API:

{api_response}

Summarize this response to answer the original question.

Summary:`;
export const API_RESPONSE_PROMPT_TEMPLATE = /* #__PURE__ */ new PromptTemplate({
  inputVariables: ["api_docs", "question", "api_url", "api_response"],
  template: API_RESPONSE_RAW_PROMPT_TEMPLATE,
});


/* eslint-disable spaced-comment */
import { BasePromptTemplate, PromptTemplate } from "@langchain/core/prompts";

export const DEFAULT_SQL_DATABASE_PROMPT = /*#__PURE__*/ new PromptTemplate({
  template: `Given an input question, first create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer. Unless the user specifies in his question a specific number of examples he wishes to obtain, always limit your query to at most {top_k} results. You can order the results by a relevant column to return the most interesting examples in the database.

Never query for all the columns from a specific table, only ask for a the few relevant columns given the question.

Pay attention to use only the column names that you can see in the schema description. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.

Use the following format:

Question: "Question here"
SQLQuery: "SQL Query to run"
SQLResult: "Result of the SQLQuery"
Answer: "Final answer here"

Only use the tables listed below.

{table_info}

Question: {input}`,
  inputVariables: ["dialect", "table_info", "input", "top_k"],
});

export const SQL_POSTGRES_PROMPT = /*#__PURE__*/ new PromptTemplate({
  template: `You are a PostgreSQL expert. Given an input question, first create a syntactically correct PostgreSQL query to run, then look at the results of the query and return the answer to the input question.
Unless the user specifies in the question a specific number of examples to obtain, query for at most {top_k} results using the LIMIT clause as per PostgreSQL. You can order the results to return the most informative data in the database.
Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in double quotes (") to denote them as delimited identifiers.
Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.

Use the following format:

Question: "Question here"
SQLQuery: "SQL Query to run"
SQLResult: "Result of the SQLQuery"
Answer: "Final answer here"

Only use the following tables:
{table_info}

Question: {input}`,
  inputVariables: ["dialect", "table_info", "input", "top_k"],
});

export const SQL_SQLITE_PROMPT = /*#__PURE__*/ new PromptTemplate({
  template: `You are a SQLite expert. Given an input question, first create a syntactically correct SQLite query to run, then look at the results of the query and return the answer to the input question.
Unless the user specifies in the question a specific number of examples to obtain, query for at most {top_k} results using the LIMIT clause as per SQLite. You can order the results to return the most informative data in the database.
Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in double quotes (") to denote them as delimited identifiers.
Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.

Use the following format:

Question: "Question here"
SQLQuery: "SQL Query to run"
SQLResult: "Result of the SQLQuery"
Answer: "Final answer here"

Only use the following tables:
{table_info}

Question: {input}`,
  inputVariables: ["dialect", "table_info", "input", "top_k"],
});

export const SQL_MYSQL_PROMPT = /*#__PURE__*/ new PromptTemplate({
  template: `You are a MySQL expert. Given an input question, first create a syntactically correct MySQL query to run, then look at the results of the query and return the answer to the input question.
Unless the user specifies in the question a specific number of examples to obtain, query for at most {top_k} results using the LIMIT clause as per MySQL. You can order the results to return the most informative data in the database.
Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in backticks (\`) to denote them as delimited identifiers.
Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.

Use the following format:

Question: "Question here"
SQLQuery: "SQL Query to run"
SQLResult: "Result of the SQLQuery"
Answer: "Final answer here"

Only use the following tables:
{table_info}

Question: {input}`,
  inputVariables: ["dialect", "table_info", "input", "top_k"],
});

export const SQL_MSSQL_PROMPT = /*#__PURE__*/ new PromptTemplate({
  template: `You are an MS SQL expert. Given an input question, first create a syntactically correct MS SQL query to run, then look at the results of the query and return the answer to the input question.
Unless the user specifies in the question a specific number of examples to obtain, query for at most {top_k} results using the TOP clause as per MS SQL. You can order the results to return the most informative data in the database.
Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in square brackets ([]) to denote them as delimited identifiers.
Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.

Use the following format:

Question: "Question here"
SQLQuery: "SQL Query to run"
SQLResult: "Result of the SQLQuery"
Answer: "Final answer here"

Only use the following tables:
{table_info}

Question: {input}`,
  inputVariables: ["dialect", "table_info", "input", "top_k"],
});

export const SQL_SAP_HANA_PROMPT = /*#__PURE__*/ new PromptTemplate({
  template: `You are a SAP HANA expert. Given an input question, first create a syntactically correct SAP HANA query to run, then look at the results of the query and return the answer to the input question.
Unless the user specifies in the question a specific number of examples to obtain, query for at most {top_k} results using the LIMIT clause as per SAP HANA. You can order the results to return the most informative data in the database.
Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in double quotes (") to denote them as delimited identifiers.
Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.
Always use a schema name when executing a query.

Use the following format:

Question: "Question here"
SQLQuery: "SQL Query to run"
SQLResult: "Result of the SQLQuery"
Answer: "Final answer here"

Only use the following tables:
{table_info}

Question: {input}`,
  inputVariables: ["dialect", "table_info", "input", "top_k"],
});

export const SQL_ORACLE_PROMPT = /*#__PURE__*/ new PromptTemplate({
  template: `You are a ORACLE expert. Given an input question, first create a syntactically correct ORACLE query to run, then look at the results of the query and return the answer to the input question.
Unless the user specifies in the question a specific number of examples to obtain, query for at most {top_k} results using the ROWNUM clause as per ORACLE. You can order the results to return the most informative data in the database.
Never query for all columns from a table. You must query only the columns that are needed to answer the question.
Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.

Use the following format:

Question: "Question here"
SQLQuery: "SQL Query to run"
SQLResult: "Result of the SQLQuery"
Answer: "Final answer here"

Only use the following tables:
{table_info}

Question: {input}`,
  inputVariables: ["dialect", "table_info", "input", "top_k"],
});

export type SqlDialect =
  | "oracle"
  | "postgres"
  | "sqlite"
  | "mysql"
  | "mssql"
  | "sap hana";

export const SQL_PROMPTS_MAP: Record<SqlDialect, BasePromptTemplate> = {
  oracle: SQL_ORACLE_PROMPT,
  postgres: SQL_POSTGRES_PROMPT,
  sqlite: SQL_SQLITE_PROMPT,
  mysql: SQL_MYSQL_PROMPT,
  mssql: SQL_MSSQL_PROMPT,
  "sap hana": SQL_SAP_HANA_PROMPT,
};


// Next.js API route support: https://nextjs.org/docs/api-routes/introduction

// import all entrypoints to test, do not do this in your own app
import "../../entrypoints.js";

// Import a few things we'll use to test the exports
import { LLMChain } from "langchain/chains";
import { ChatOpenAI } from "@langchain/openai";
import {
  ChatPromptTemplate,
  HumanMessagePromptTemplate,
} from "@langchain/core/prompts";
import { OpenAI } from "@langchain/openai";
import { OpenAIEmbeddings } from "@langchain/openai";
import { CallbackManager } from "@langchain/core/callbacks/manager";
import { ChatAgent } from "langchain/agents";

import { NextRequest, NextResponse } from "next/server";

export const config = {
  runtime: "edge",
};

export default async function handler(req: NextRequest) {
  // Intantiate a few things to test the exports
  new OpenAI({ openAIApiKey: process.env.OPENAI_API_KEY });
  const emb = new OpenAIEmbeddings({
    openAIApiKey: process.env.OPENAI_API_KEY,
  });
  const agent = ChatAgent.fromLLMAndTools(new ChatOpenAI(), []);

  // Set up a streaming LLM
  const encoder = new TextEncoder();
  const stream = new TransformStream();
  const writer = stream.writable.getWriter();
  const llm = new ChatOpenAI({
    streaming: true,
    callbackManager: CallbackManager.fromHandlers({
      handleLLMNewToken: async (token) => {
        await writer.ready;
        await writer.write(encoder.encode(`data: ${token}\n\n`));
      },
      handleLLMEnd: async () => {
        await writer.ready;
        await writer.close();
      },
      handleLLMError: async (e) => {
        await writer.ready;
        await writer.abort(e);
      },
    }),
  });

  // Test a chain + prompt + model
  const chain = new LLMChain({
    llm,
    prompt: ChatPromptTemplate.fromMessages([
      HumanMessagePromptTemplate.fromTemplate("{input}"),
    ]),
  });

  // Run the chain but don't await it, otherwise the response will start
  // only after the chain is done
  chain.run("hello").catch(console.error);

  return new NextResponse(stream.readable, {
    headers: {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache",
    },
  });
}


import { ChatOpenAI } from "@langchain/openai";
import { createOpenAIToolsAgent, AgentExecutor } from "langchain/agents";
import { pull } from "langchain/hub";
import type { ChatPromptTemplate } from "@langchain/core/prompts";

const model = new ChatOpenAI({
  openAIApiKey: "sk-XXXX",
});

const prompt = await pull<ChatPromptTemplate>(
  "hwchase17/openai-functions-agent"
);

const agent = await createOpenAIToolsAgent({
  llm: model,
  prompt,
  tools: []
});

const agentExecutor = new AgentExecutor({
  agent,
  tools: [],
});

console.log(agentExecutor);


# Conceptual guide

This section contains introductions to key parts of LangChain.

## Architecture

LangChain as a framework consists of several pieces. The below diagram shows how they relate.

import ThemedImage from "@theme/ThemedImage";
import useBaseUrl from "@docusaurus/useBaseUrl";

<ThemedImage
  alt="Diagram outlining the hierarchical organization of the LangChain framework, displaying the interconnected parts across multiple layers."
  sources={{
    light: useBaseUrl("/svg/langchain_stack_062024.svg"),
    dark: useBaseUrl("/svg/langchain_stack_062024_dark.svg"),
  }}
  title="LangChain Framework Overview"
  style={{ width: "100%" }}
/>

### `@langchain/core`

This package contains base abstractions of different components and ways to compose them together.
The interfaces for core components like LLMs, vector stores, retrievers and more are defined here.
No third party integrations are defined here.
The dependencies are kept purposefully very lightweight.

This package is a requirement of most others in the LangChain ecosystem, and must be installed separately.

### `@langchain/community`

This package contains third party integrations that are maintained by the LangChain community.
Key partner packages are separated out (see below).
This contains all integrations for various components (LLMs, vector stores, retrievers).
All dependencies in this package are optional to keep the package as lightweight as possible.

### Partner packages

While the long tail of integrations are in `@langchain/community`, we split popular integrations into their own packages (e.g. `langchain-openai`, `langchain-anthropic`, etc).
This was done in order to improve support for these important integrations.

### `langchain`

The main `langchain` package contains chains, agents, and retrieval strategies that make up an application's cognitive architecture.
These are NOT third party integrations.
All chains, agents, and retrieval strategies here are NOT specific to any one integration, but rather generic across all integrations.

### [LangGraph.js](https://langchain-ai.github.io/langgraphjs/)

LangGraph.js is an extension of `langchain` aimed at
building robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph.

LangGraph exposes high level interfaces for creating common types of agents, as well as a low-level API for composing custom flows.

### [LangSmith](https://docs.smith.langchain.com)

A developer platform that lets you debug, test, evaluate, and monitor LLM applications.

## Installation

If you want to work with high level abstractions, you should install the `langchain` package.

```bash npm2yarn
npm i langchain @langchain/core
```

If you want to work with specific integrations, you will need to install them separately.
See [here](/docs/integrations/platforms/) for a list of integrations and how to install them.

For working with LangSmith, you will need to set up a LangSmith developer account [here](https://smith.langchain.com) and get an API key.
After that, you can enable it by setting environment variables:

```shell
export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY=ls__...

# Reduce tracing latency if you are not in a serverless environment
# export LANGCHAIN_CALLBACKS_BACKGROUND=true
```

## LangChain Expression Language

LangChain Expression Language, or LCEL, is a declarative way to easily compose chains together.
LCEL was designed from day 1 to **support putting prototypes in production, with no code changes**, from the simplest “prompt + LLM” chain to the most complex chains
(we’ve seen folks successfully run LCEL chains with 100s of steps in production). To highlight a few of the reasons you might want to use LCEL:

**First-class streaming support**
When you build your chains with LCEL you get the best possible time-to-first-token (time elapsed until the first chunk of output comes out). For some chains this means
eg. we stream tokens straight from an LLM to a streaming output parser, and you get back parsed, incremental chunks of output at the same rate as the LLM provider outputs the raw tokens.

**Optimized parallel execution**
Whenever your LCEL chains have steps that can be executed in parallel (eg if you fetch documents from multiple retrievers) we automatically do it for the smallest possible latency.

**Retries and fallbacks**
Configure retries and fallbacks for any part of your LCEL chain. This is a great way to make your chains more reliable at scale. We’re currently working on adding streaming
support for retries/fallbacks, so you can get the added reliability without any latency cost.

**Access intermediate results**
For more complex chains it’s often very useful to access the results of intermediate steps even before the final output is produced. This can be used to let end-users know
something is happening, or even just to debug your chain.

[**Seamless LangSmith tracing**](https://docs.smith.langchain.com)
As your chains get more and more complex, it becomes increasingly important to understand what exactly is happening at every step.
With LCEL, **all** steps are automatically logged to [LangSmith](https://docs.smith.langchain.com) for maximum observability and debuggability.

### Runnable interface {#interface}

To make it as easy as possible to create custom chains, we've implemented a ["Runnable"](https://api.js.langchain.com/classes/langchain_core.runnables.Runnable.html) protocol.
Many LangChain components implement the `Runnable` protocol, including chat models, LLMs, output parsers, retrievers, prompt templates, and more. There are also several useful primitives for working with runnables, which you can read about below.

This is a standard interface, which makes it easy to define custom chains as well as invoke them in a standard way.
The standard interface includes:

- [`stream`](#stream): stream back chunks of the response
- [`invoke`](#invoke): call the chain on an input
- [`batch`](#batch): call the chain on an array of inputs

The **input type** and **output type** varies by component:

| Component    | Input Type                                            | Output Type           |
| ------------ | ----------------------------------------------------- | --------------------- |
| Prompt       | Object                                                | PromptValue           |
| ChatModel    | Single string, list of chat messages or a PromptValue | ChatMessage           |
| LLM          | Single string, list of chat messages or a PromptValue | String                |
| OutputParser | The output of an LLM or ChatModel                     | Depends on the parser |
| Retriever    | Single string                                         | List of Documents     |
| Tool         | Single string or object, depending on the tool        | Depends on the tool   |

## Components

LangChain provides standard, extendable interfaces and external integrations for various components useful for building with LLMs.
Some components LangChain implements, some components we rely on third-party integrations for, and others are a mix.

### Chat models

Language models that use a sequence of messages as inputs and return chat messages as outputs (as opposed to using plain text).
These are generally newer models (older models are generally `LLMs`, see below).
Chat models support the assignment of distinct roles to conversation messages, helping to distinguish messages from the AI, users, and instructions such as system messages.

Although the underlying models are messages in, message out, the LangChain wrappers also allow these models to take a string as input.
This gives them the same interface as LLMs (and simpler to use).
When a string is passed in as input, it will be converted to a `HumanMessage` under the hood before being passed to the underlying model.

LangChain does not host any Chat Models, rather we rely on third party integrations.

We have some standardized parameters when constructing ChatModels:

- `model`: the name of the model

Chat Models also accept other parameters that are specific to that integration.

:::important
Some chat models have been fine-tuned for **tool calling** and provide a dedicated API for it.
Generally, such models are better at tool calling than non-fine-tuned models, and are recommended for use cases that require tool calling.
Please see the [tool calling section](/docs/concepts/#functiontool-calling) for more information.
:::

For specifics on how to use chat models, see the [relevant how-to guides here](/docs/how_to/#chat-models).

#### Multimodality

Some chat models are multimodal, accepting images, audio and even video as inputs.
These are still less common, meaning model providers haven't standardized on the "best" way to define the API.
Multimodal outputs are even less common. As such, we've kept our multimodal abstractions fairly light weight
and plan to further solidify the multimodal APIs and interaction patterns as the field matures.

In LangChain, most chat models that support multimodal inputs also accept those values in OpenAI's content blocks format.
So far this is restricted to image inputs. For models like Gemini which support video and other bytes input, the APIs also support the native, model-specific representations.

For specifics on how to use multimodal models, see the [relevant how-to guides here](/docs/how_to/#multimodal).

### LLMs


:::caution
Pure text-in/text-out LLMs tend to be older or lower-level. Many popular models are best used as [chat completion models](/docs/concepts/#chat-models),
even for non-chat use cases.

You are probably looking for [the section above instead](/docs/concepts/#chat-models).
:::

Language models that takes a string as input and returns a string.
These are traditionally older models (newer models generally are [Chat Models](/docs/concepts/#chat-models), see above).

Although the underlying models are string in, string out, the LangChain wrappers also allow these models to take messages as input.
This gives them the same interface as [Chat Models](/docs/concepts/#chat-models).
When messages are passed in as input, they will be formatted into a string under the hood before being passed to the underlying model.

LangChain does not host any LLMs, rather we rely on third party integrations.

For specifics on how to use LLMs, see the [relevant how-to guides here](/docs/how_to/#llms).

### Message types

Some language models take an array of messages as input and return a message.
There are a few different types of messages.
All messages have a `role`, `content`, and `response_metadata` property.

The `role` describes WHO is saying the message. The standard roles are "user", "assistant", "system", and "tool".
LangChain has different message classes for different roles.

The `content` property describes the content of the message.
This can be a few different things:

- A string (most models deal this type of content)
- A List of objects (this is used for multi-modal input, where the object contains information about that input type and that input location)

Optionally, messages can have a `name` property which allows for differentiating between multiple speakers with the same role.
For example, if there are two users in the chat history it can be useful to differentiate between them. Not all models support this.

#### HumanMessage

This represents a message with role "user".

#### AIMessage

This represents a message with role "assistant". In addition to the `content` property, these messages also have:

**`response_metadata`**

The `response_metadata` property contains additional metadata about the response. The data here is often specific to each model provider.
This is where information like log-probs and token usage may be stored.

**`tool_calls`**

These represent a decision from an language model to call a tool. They are included as part of an `AIMessage` output.
They can be accessed from there with the `.tool_calls` property.

This property returns a list of `ToolCall`s. A `ToolCall` is an object with the following arguments:

- `name`: The name of the tool that should be called.
- `args`: The arguments to that tool.
- `id`: The id of that tool call.

#### SystemMessage

This represents a message with role "system", which tells the model how to behave. Not every model provider supports this.

#### ToolMessage

This represents a message with role "tool", which contains the result of calling a tool. In addition to `role` and `content`, this message has:

- a `tool_call_id` field which conveys the id of the call to the tool that was called to produce this result.
- an `artifact` field which can be used to pass along arbitrary artifacts of the tool execution which are useful to track but which should not be sent to the model.

#### (Legacy) FunctionMessage

This is a legacy message type, corresponding to OpenAI's legacy function-calling API. `ToolMessage` should be used instead to correspond to the updated tool-calling API.

This represents the result of a function call. In addition to `role` and `content`, this message has a `name` parameter which conveys the name of the function that was called to produce this result.

### Prompt templates

Prompt templates help to translate user input and parameters into instructions for a language model.
This can be used to guide a model's response, helping it understand the context and generate relevant and coherent language-based output.

Prompt Templates take as input an object, where each key represents a variable in the prompt template to fill in.

Prompt Templates output a PromptValue. This PromptValue can be passed to an LLM or a ChatModel, and can also be cast to a string or an array of messages.
The reason this PromptValue exists is to make it easy to switch between strings and messages.

There are a few different types of prompt templates:

#### String PromptTemplates

These prompt templates are used to format a single string, and generally are used for simpler inputs.
For example, a common way to construct and use a PromptTemplate is as follows:

```typescript
import { PromptTemplate } from "@langchain/core/prompts";

const promptTemplate = PromptTemplate.fromTemplate(
  "Tell me a joke about {topic}"
);

await promptTemplate.invoke({ topic: "cats" });
```

#### ChatPromptTemplates

These prompt templates are used to format an array of messages. These "templates" consist of an array of templates themselves.
For example, a common way to construct and use a ChatPromptTemplate is as follows:

```typescript
import { ChatPromptTemplate } from "@langchain/core/prompts";

const promptTemplate = ChatPromptTemplate.fromMessages([
  ["system", "You are a helpful assistant"],
  ["user", "Tell me a joke about {topic}"],
]);

await promptTemplate.invoke({ topic: "cats" });
```

In the above example, this ChatPromptTemplate will construct two messages when called.
The first is a system message, that has no variables to format.
The second is a HumanMessage, and will be formatted by the `topic` variable the user passes in.

#### MessagesPlaceholder

This prompt template is responsible for adding an array of messages in a particular place.
In the above ChatPromptTemplate, we saw how we could format two messages, each one a string.
But what if we wanted the user to pass in an array of messages that we would slot into a particular spot?
This is how you use MessagesPlaceholder.

```typescript
import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from "@langchain/core/prompts";
import { HumanMessage } from "@langchain/core/messages";

const promptTemplate = ChatPromptTemplate.fromMessages([
  ["system", "You are a helpful assistant"],
  new MessagesPlaceholder("msgs"),
]);

promptTemplate.invoke({ msgs: [new HumanMessage({ content: "hi!" })] });
```

This will produce an array of two messages, the first one being a system message, and the second one being the HumanMessage we passed in.
If we had passed in 5 messages, then it would have produced 6 messages in total (the system message plus the 5 passed in).
This is useful for letting an array of messages be slotted into a particular spot.

An alternative way to accomplish the same thing without using the `MessagesPlaceholder` class explicitly is:

```typescript
const promptTemplate = ChatPromptTemplate.fromMessages([
  ["system", "You are a helpful assistant"],
  ["placeholder", "{msgs}"], // <-- This is the changed part
]);
```

For specifics on how to use prompt templates, see the [relevant how-to guides here](/docs/how_to/#prompt-templates).

### Example Selectors

One common prompting technique for achieving better performance is to include examples as part of the prompt.
This is known as [few-shot prompting](/docs/concepts/#few-shot-prompting).
This gives the language model concrete examples of how it should behave.
Sometimes these examples are hardcoded into the prompt, but for more advanced situations it may be nice to dynamically select them.
Example Selectors are classes responsible for selecting and then formatting examples into prompts.

For specifics on how to use example selectors, see the [relevant how-to guides here](/docs/how_to/#example-selectors).

### Output parsers

:::note

The information here refers to parsers that take a text output from a model try to parse it into a more structured representation.
More and more models are supporting function (or tool) calling, which handles this automatically.
It is recommended to use function/tool calling rather than output parsing.
See documentation for that [here](/docs/concepts/#function-tool-calling).

:::

Responsible for taking the output of a model and transforming it to a more suitable format for downstream tasks.
Useful when you are using LLMs to generate structured data, or to normalize output from chat models and LLMs.

There are two main methods an output parser must implement:

- "Get format instructions": A method which returns a string containing instructions for how the output of a language model should be formatted.
- "Parse": A method which takes in a string (assumed to be the response from a language model) and parses it into some structure.

And then one optional one:

- "Parse with prompt": A method which takes in a string (assumed to be the response from a language model) and a prompt (assumed to be the prompt that generated such a response) and parses it into some structure. The prompt is largely provided in the event the OutputParser wants to retry or fix the output in some way, and needs information from the prompt to do so.

Output parsers accept a string or `BaseMessage` as input and can return an arbitrary type.

LangChain has many different types of output parsers. This is a list of output parsers LangChain supports. The table below has various pieces of information:

**Name**: The name of the output parser