import { Serializable } from "@langchain/core/load/serializable";
import { patchConfig, RunnableSequence, } from "@langchain/core/runnables";
/**
 * Error class for parse errors in LangChain. Contains information about
 * the error message and the output that caused the error.
 */
class ParseError extends Error {
    constructor(msg, output) {
        super(msg);
        Object.defineProperty(this, "output", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        this.output = output;
    }
}
/**
 * Abstract base class for agents in LangChain. Provides common
 * functionality for agents, such as handling inputs and outputs.
 */
export class BaseAgent extends Serializable {
    get returnValues() {
        return ["output"];
    }
    get allowedTools() {
        return undefined;
    }
    /**
     * Return the string type key uniquely identifying this class of agent.
     */
    _agentType() {
        throw new Error("Not implemented");
    }
    /**
     * Return response when agent has been stopped due to max iterations
     */
    returnStoppedResponse(earlyStoppingMethod, _steps, _inputs, _callbackManager) {
        if (earlyStoppingMethod === "force") {
            return Promise.resolve({
                returnValues: { output: "Agent stopped due to max iterations." },
                log: "",
            });
        }
        throw new Error(`Invalid stopping method: ${earlyStoppingMethod}`);
    }
    /**
     * Prepare the agent for output, if needed
     */
    async prepareForOutput(_returnValues, _steps) {
        return {};
    }
}
/**
 * Abstract base class for single action agents in LangChain. Extends the
 * BaseAgent class and provides additional functionality specific to
 * single action agents.
 */
export class BaseSingleActionAgent extends BaseAgent {
    _agentActionType() {
        return "single";
    }
}
/**
 * Abstract base class for multi-action agents in LangChain. Extends the
 * BaseAgent class and provides additional functionality specific to
 * multi-action agents.
 */
export class BaseMultiActionAgent extends BaseAgent {
    _agentActionType() {
        return "multi";
    }
}
function isAgentAction(input) {
    return !Array.isArray(input) && input?.tool !== undefined;
}
export function isRunnableAgent(x) {
    return (x.runnable !==
        undefined);
}
// TODO: Remove in the future. Only for backwards compatibility.
// Allows for the creation of runnables with properties that will
// be passed to the agent executor constructor.
export class AgentRunnableSequence extends RunnableSequence {
    constructor() {
        super(...arguments);
        Object.defineProperty(this, "streamRunnable", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "singleAction", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    static fromRunnables([first, ...runnables], config) {
        const sequence = RunnableSequence.from([first, ...runnables], config.name);
        sequence.singleAction = config.singleAction;
        sequence.streamRunnable = config.streamRunnable;
        return sequence;
    }
    static isAgentRunnableSequence(x) {
        return typeof x.singleAction === "boolean";
    }
}
/**
 * Class representing a single-action agent powered by runnables.
 * Extends the BaseSingleActionAgent class and provides methods for
 * planning agent actions with runnables.
 */
export class RunnableSingleActionAgent extends BaseSingleActionAgent {
    get inputKeys() {
        return [];
    }
    constructor(fields) {
        super(fields);
        Object.defineProperty(this, "lc_namespace", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: ["langchain", "agents", "runnable"]
        });
        Object.defineProperty(this, "runnable", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        /**
         * Whether to stream from the runnable or not.
         * If true, the underlying LLM is invoked in a streaming fashion to make it
         * possible to get access to the individual LLM tokens when using
         * `streamLog` with the Agent Executor. If false then LLM is invoked in a
         * non-streaming fashion and individual LLM tokens will not be available
         * in `streamLog`.
         *
         * Note that the runnable should still only stream a single action or
         * finish chunk.
         */
        Object.defineProperty(this, "streamRunnable", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: true
        });
        Object.defineProperty(this, "defaultRunName", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: "RunnableAgent"
        });
        this.runnable = fields.runnable;
        this.defaultRunName =
            fields.defaultRunName ?? this.runnable.name ?? this.defaultRunName;
        this.streamRunnable = fields.streamRunnable ?? this.streamRunnable;
    }
    async plan(steps, inputs, callbackManager, config) {
        const combinedInput = { ...inputs, steps };
        const combinedConfig = patchConfig(config, {
            callbacks: callbackManager,
            runName: this.defaultRunName,
        });
        if (this.streamRunnable) {
            const stream = await this.runnable.stream(combinedInput, combinedConfig);
            let finalOutput;
            for await (const chunk of stream) {
                if (finalOutput === undefined) {
                    finalOutput = chunk;
                }
                else {
                    throw new Error([
                        `Multiple agent actions/finishes received in streamed agent output.`,
                        `Set "streamRunnable: false" when initializing the agent to invoke this agent in non-streaming mode.`,
                    ].join("\n"));
                }
            }
            if (finalOutput === undefined) {
                throw new Error([
                    "No streaming output received from underlying runnable.",
                    `Set "streamRunnable: false" when initializing the agent to invoke this agent in non-streaming mode.`,
                ].join("\n"));
            }
            return finalOutput;
        }
        else {
            return this.runnable.invoke(combinedInput, combinedConfig);
        }
    }
}
/**
 * Class representing a multi-action agent powered by runnables.
 * Extends the BaseMultiActionAgent class and provides methods for
 * planning agent actions with runnables.
 */
export class RunnableMultiActionAgent extends BaseMultiActionAgent {
    get inputKeys() {
        return [];
    }
    constructor(fields) {
        super(fields);
        Object.defineProperty(this, "lc_namespace", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: ["langchain", "agents", "runnable"]
        });
        // TODO: Rename input to "intermediate_steps"
        Object.defineProperty(this, "runnable", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "defaultRunName", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: "RunnableAgent"
        });
        Object.defineProperty(this, "stop", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "streamRunnable", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: true
        });
        this.runnable = fields.runnable;
        this.stop = fields.stop;
        this.defaultRunName =
            fields.defaultRunName ?? this.runnable.name ?? this.defaultRunName;
        this.streamRunnable = fields.streamRunnable ?? this.streamRunnable;
    }
    async plan(steps, inputs, callbackManager, config) {
        const combinedInput = { ...inputs, steps };
        const combinedConfig = patchConfig(config, {
            callbacks: callbackManager,
            runName: this.defaultRunName,
        });
        let output;
        if (this.streamRunnable) {
            const stream = await this.runnable.stream(combinedInput, combinedConfig);
            let finalOutput;
            for await (const chunk of stream) {
                if (finalOutput === undefined) {
                    finalOutput = chunk;
                }
                else {
                    throw new Error([
                        `Multiple agent actions/finishes received in streamed agent output.`,
                        `Set "streamRunnable: false" when initializing the agent to invoke this agent in non-streaming mode.`,
                    ].join("\n"));
                }
            }
            if (finalOutput === undefined) {
                throw new Error([
                    "No streaming output received from underlying runnable.",
                    `Set "streamRunnable: false" when initializing the agent to invoke this agent in non-streaming mode.`,
                ].join("\n"));
            }
            output = finalOutput;
        }
        else {
            output = await this.runnable.invoke(combinedInput, combinedConfig);
        }
        if (isAgentAction(output)) {
            return [output];
        }
        return output;
    }
}
/** @deprecated Renamed to RunnableMultiActionAgent. */
export class RunnableAgent extends RunnableMultiActionAgent {
}
/**
 * Class representing a single action agent using a LLMChain in LangChain.
 * Extends the BaseSingleActionAgent class and provides methods for
 * planning agent actions based on LLMChain outputs.
 * @example
 * ```typescript
 * const customPromptTemplate = new CustomPromptTemplate({
 *   tools: [new Calculator()],
 *   inputVariables: ["input", "agent_scratchpad"],
 * });
 * const customOutputParser = new CustomOutputParser();
 * const agent = new LLMSingleActionAgent({
 *   llmChain: new LLMChain({
 *     prompt: customPromptTemplate,
 *     llm: new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 }),
 *   }),
 *   outputParser: customOutputParser,
 *   stop: ["\nObservation"],
 * });
 * const executor = new AgentExecutor({
 *   agent,
 *   tools: [new Calculator()],
 * });
 * const result = await executor.invoke({
 *   input:
 *     "Who is Olivia Wilde's boyfriend? What is his current age raised to the 0.23 power?",
 * });
 * ```
 */
export class LLMSingleActionAgent extends BaseSingleActionAgent {
    constructor(input) {
        super(input);
        Object.defineProperty(this, "lc_namespace", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: ["langchain", "agents"]
        });
        Object.defineProperty(this, "llmChain", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "outputParser", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "stop", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        this.stop = input.stop;
        this.llmChain = input.llmChain;
        this.outputParser = input.outputParser;
    }
    get inputKeys() {
        return this.llmChain.inputKeys;
    }
    /**
     * Decide what to do given some input.
     *
     * @param steps - Steps the LLM has taken so far, along with observations from each.
     * @param inputs - User inputs.
     * @param callbackManager - Callback manager.
     *
     * @returns Action specifying what tool to use.
     */
    async plan(steps, inputs, callbackManager) {
        const output = await this.llmChain.call({
            intermediate_steps: steps,
            stop: this.stop,
            ...inputs,
        }, callbackManager);
        return this.outputParser.parse(output[this.llmChain.outputKey], callbackManager);
    }
}
/**
 * Class responsible for calling a language model and deciding an action.
 *
 * @remarks This is driven by an LLMChain. The prompt in the LLMChain *must*
 * include a variable called "agent_scratchpad" where the agent can put its
 * intermediary work.
 *
 * @deprecated Use {@link https://js.langchain.com/docs/modules/agents/agent_types/ | new agent creation methods}.
 */
export class Agent extends BaseSingleActionAgent {
    get allowedTools() {
        return this._allowedTools;
    }
    get inputKeys() {
        return this.llmChain.inputKeys.filter((k) => k !== "agent_scratchpad");
    }
    constructor(input) {
        super(input);
        Object.defineProperty(this, "llmChain", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "outputParser", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "_allowedTools", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: undefined
        });
        this.llmChain = input.llmChain;
        this._allowedTools = input.allowedTools;
        this.outputParser = input.outputParser;
    }
    /**
     * Get the default output parser for this agent.
     */
    static getDefaultOutputParser(_fields) {
        throw new Error("Not implemented");
    }
    /**
     * Create a prompt for this class
     *
     * @param _tools - List of tools the agent will have access to, used to format the prompt.
     * @param _fields - Additional fields used to format the prompt.
     *
     * @returns A PromptTemplate assembled from the given tools and fields.
     * */
    static createPrompt(_tools, 
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    _fields) {
        throw new Error("Not implemented");
    }
    /** Construct an agent from an LLM and a list of tools */
    static fromLLMAndTools(_llm, _tools, 
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    _args) {
        throw new Error("Not implemented");
    }
    /**
     * Validate that appropriate tools are passed in
     */
    static validateTools(_tools) { }
    _stop() {
        return [`\n${this.observationPrefix()}`];
    }
    /**
     * Name of tool to use to terminate the chain.
     */
    finishToolName() {
        return "Final Answer";
    }
    /**
     * Construct a scratchpad to let the agent continue its thought process
     */
    async constructScratchPad(steps) {
        return steps.reduce((thoughts, { action, observation }) => thoughts +
            [
                action.log,
                `${this.observationPrefix()}${observation}`,
                this.llmPrefix(),
            ].join("\n"), "");
    }
    async _plan(steps, inputs, suffix, callbackManager) {
        const thoughts = await this.constructScratchPad(steps);
        const newInputs = {
            ...inputs,
            agent_scratchpad: suffix ? `${thoughts}${suffix}` : thoughts,
        };
        if (this._stop().length !== 0) {
            newInputs.stop = this._stop();
        }
        const output = await this.llmChain.predict(newInputs, callbackManager);
        if (!this.outputParser) {
            throw new Error("Output parser not set");
        }
        return this.outputParser.parse(output, callbackManager);
    }
    /**
     * Decide what to do given some input.
     *
     * @param steps - Steps the LLM has taken so far, along with observations from each.
     * @param inputs - User inputs.
     * @param callbackManager - Callback manager to use for this call.
     *
     * @returns Action specifying what tool to use.
     */
    plan(steps, inputs, callbackManager) {
        return this._plan(steps, inputs, undefined, callbackManager);
    }
    /**
     * Return response when agent has been stopped due to max iterations
     */
    async returnStoppedResponse(earlyStoppingMethod, steps, inputs, callbackManager) {
        if (earlyStoppingMethod === "force") {
            return {
                returnValues: { output: "Agent stopped due to max iterations." },
                log: "",
            };
        }
        if (earlyStoppingMethod === "generate") {
            try {
                const action = await this._plan(steps, inputs, "\n\nI now need to return a final answer based on the previous steps:", callbackManager);
                if ("returnValues" in action) {
                    return action;
                }
                return { returnValues: { output: action.log }, log: action.log };
            }
            catch (err) {
                // fine to use instanceof because we're in the same module
                // eslint-disable-next-line no-instanceof/no-instanceof
                if (!(err instanceof ParseError)) {
                    throw err;
                }
                return { returnValues: { output: err.output }, log: err.output };
            }
        }
        throw new Error(`Invalid stopping method: ${earlyStoppingMethod}`);
    }
    /**
     * Load an agent from a json-like object describing it.
     */
    static async deserialize(data) {
        switch (data._type) {
            case "zero-shot-react-description": {
                const { ZeroShotAgent } = await import("./mrkl/index.js");
                return ZeroShotAgent.deserialize(data);
            }
            default:
                throw new Error("Unknown agent type");
        }
    }
}
