import { Command, END, _isSend, isCommand } from "../constants.js";
import { isGraphInterrupt } from "../errors.js";
import { RunnableCallable } from "../utils.js";
import { ToolMessage, isAIMessage, isBaseMessage } from "@langchain/core/messages";
//#region src/prebuilt/tool_node.ts
const isBaseMessageArray = (input) => Array.isArray(input) && input.every(isBaseMessage);
const isMessagesState = (input) => typeof input === "object" && input != null && "messages" in input && isBaseMessageArray(input.messages);
const isSendInput = (input) => typeof input === "object" && input != null && "lg_tool_call" in input;
/**
* A node that runs the tools requested in the last AIMessage. It can be used
* either in StateGraph with a "messages" key or in MessageGraph. If multiple
* tool calls are requested, they will be run in parallel. The output will be
* a list of ToolMessages, one for each tool call.
*
* @example
* ```ts
* import { ToolNode } from "@langchain/langgraph/prebuilt";
* import { tool } from "@langchain/core/tools";
* import { z } from "zod";
* import { AIMessage } from "@langchain/core/messages";
*
* const getWeather = tool((input) => {
*   if (["sf", "san francisco"].includes(input.location.toLowerCase())) {
*     return "It's 60 degrees and foggy.";
*   } else {
*     return "It's 90 degrees and sunny.";
*   }
* }, {
*   name: "get_weather",
*   description: "Call to get the current weather.",
*   schema: z.object({
*     location: z.string().describe("Location to get the weather for."),
*   }),
* });
*
* const tools = [getWeather];
* const toolNode = new ToolNode(tools);
*
* const messageWithSingleToolCall = new AIMessage({
*   content: "",
*   tool_calls: [
*     {
*       name: "get_weather",
*       args: { location: "sf" },
*       id: "tool_call_id",
*       type: "tool_call",
*     }
*   ]
* })
*
* await toolNode.invoke({ messages: [messageWithSingleToolCall] });
* // Returns tool invocation responses as:
* // { messages: ToolMessage[] }
* ```
*
* @example
* ```ts
* import {
*   StateGraph,
*   MessagesAnnotation,
* } from "@langchain/langgraph";
* import { ToolNode } from "@langchain/langgraph/prebuilt";
* import { tool } from "@langchain/core/tools";
* import { z } from "zod";
* import { ChatAnthropic } from "@langchain/anthropic";
*
* const getWeather = tool((input) => {
*   if (["sf", "san francisco"].includes(input.location.toLowerCase())) {
*     return "It's 60 degrees and foggy.";
*   } else {
*     return "It's 90 degrees and sunny.";
*   }
* }, {
*   name: "get_weather",
*   description: "Call to get the current weather.",
*   schema: z.object({
*     location: z.string().describe("Location to get the weather for."),
*   }),
* });
*
* const tools = [getWeather];
* const modelWithTools = new ChatAnthropic({
*   model: "claude-3-haiku-20240307",
*   temperature: 0
* }).bindTools(tools);
*
* const toolNodeForGraph = new ToolNode(tools)
*
* const shouldContinue = (state: typeof MessagesAnnotation.State) => {
*   const { messages } = state;
*   const lastMessage = messages[messages.length - 1];
*   if ("tool_calls" in lastMessage && Array.isArray(lastMessage.tool_calls) && lastMessage.tool_calls?.length) {
*     return "tools";
*   }
*   return "__end__";
* }
*
* const callModel = async (state: typeof MessagesAnnotation.State) => {
*   const { messages } = state;
*   const response = await modelWithTools.invoke(messages);
*   return { messages: response };
* }
*
* const graph = new StateGraph(MessagesAnnotation)
*   .addNode("agent", callModel)
*   .addNode("tools", toolNodeForGraph)
*   .addEdge("__start__", "agent")
*   .addConditionalEdges("agent", shouldContinue)
*   .addEdge("tools", "agent")
*   .compile();
*
* const inputs = {
*   messages: [{ role: "user", content: "what is the weather in SF?" }],
* };
*
* const stream = await graph.stream(inputs, {
*   streamMode: "values",
* });
*
* for await (const { messages } of stream) {
*   console.log(messages);
* }
* // Returns the messages in the state at each step of execution
* ```
*
* ### Accessing graph state and runtime context from tools
*
* Tools executed by a `ToolNode` only receive the arguments produced by the
* model. To give a tool access to the surrounding graph state or other runtime
* context, read them from the {@link ToolRuntime} that is passed as the
* second argument to every tool:
*
* - `runtime.state` — the input the `ToolNode` was invoked with. When the
*   `ToolNode` runs as a graph node (e.g. inside `createReactAgent`), this is
*   the current graph state. This works in any runtime, including web browsers,
*   because it does not rely on `node:async_hooks`/`AsyncLocalStorage`.
* - `runtime.config`, `runtime.context`, `runtime.store`, etc. — other
*   run-scoped values.
*
* @example
* ```ts
* import { ToolNode } from "@langchain/langgraph/prebuilt";
* import { StateGraph, MessagesZodState } from "@langchain/langgraph";
* import { tool, type ToolRuntime } from "@langchain/core/tools";
* import { z } from "zod";
*
* // Define the graph state with a Zod schema. The extra `userId` key becomes
* // part of the state that the ToolNode forwards to its tools via `runtime.state`.
* const AgentState = z.object({
*   ...MessagesZodState.shape,
*   userId: z.string(),
* });
*
* const getUserInfo = tool(
*   async (_input, runtime: ToolRuntime<typeof AgentState>) => {
*     // Read the current graph state directly from the second argument.
*     const userId = runtime.state.userId;
*     return userId === "user_123" ? "User is John Smith" : "Unknown user";
*   },
*   {
*     name: "get_user_info",
*     description: "Look up user info.",
*     schema: z.object({}),
*   }
* );
*
* // Wire the ToolNode into a StateGraph that uses `AgentState`. Because the
* // node runs with the graph state as its input, the tool can read `userId`.
* const graph = new StateGraph(AgentState)
*   .addNode("tools", new ToolNode([getUserInfo]))
*   .addEdge("__start__", "tools")
*   .compile();
*
* await graph.invoke({ messages: [...], userId: "user_123" });
* ```
*/
var ToolNode = class extends RunnableCallable {
	tools;
	handleToolErrors = true;
	trace = false;
	constructor(tools, options) {
		const { name, tags, handleToolErrors } = options ?? {};
		super({
			name,
			tags,
			func: (input, config) => this.run(input, config)
		});
		this.tools = tools;
		this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;
	}
	async runTool(call, config, state) {
		const tool = this.tools.find((tool) => tool.name === call.name);
		try {
			if (tool === void 0) throw new Error(`Tool "${call.name}" not found.`);
			const toolCall = {
				...call,
				type: "tool_call"
			};
			const runtime = {
				...config,
				state,
				toolCallId: call.id ?? "",
				config,
				context: config.context,
				store: config.store ?? null,
				writer: config.writer ?? config.configurable?.writer ?? null
			};
			const output = await tool.invoke(toolCall, runtime);
			if (isBaseMessage(output) && output.getType() === "tool" || isCommand(output)) return output;
			return new ToolMessage({
				status: "success",
				name: tool.name,
				content: typeof output === "string" ? output : JSON.stringify(output),
				tool_call_id: call.id
			});
		} catch (e) {
			if (!this.handleToolErrors) throw e;
			if (isGraphInterrupt(e)) throw e;
			return new ToolMessage({
				status: "error",
				content: `Error: ${e.message}\n Please fix your mistakes.`,
				name: call.name,
				tool_call_id: call.id ?? ""
			});
		}
	}
	async run(input, config) {
		let outputs;
		if (isSendInput(input)) {
			const { lg_tool_call: toolCall, ...state } = input;
			outputs = [await this.runTool(toolCall, config, state)];
		} else {
			let messages;
			if (isBaseMessageArray(input)) messages = input;
			else if (isMessagesState(input)) messages = input.messages;
			else throw new Error("ToolNode only accepts BaseMessage[] or { messages: BaseMessage[] } as input.");
			const toolMessageIds = new Set(messages.filter((msg) => msg.getType() === "tool").map((msg) => msg.tool_call_id));
			let aiMessage;
			for (let i = messages.length - 1; i >= 0; i -= 1) {
				const message = messages[i];
				if (isAIMessage(message)) {
					aiMessage = message;
					break;
				}
			}
			if (aiMessage == null || !isAIMessage(aiMessage)) throw new Error("ToolNode only accepts AIMessages as input.");
			outputs = await Promise.all(aiMessage.tool_calls?.filter((call) => call.id == null || !toolMessageIds.has(call.id)).map((call) => this.runTool(call, config, input)) ?? []);
		}
		if (!outputs.some(isCommand)) return Array.isArray(input) ? outputs : { messages: outputs };
		const combinedOutputs = [];
		let parentCommand = null;
		for (const output of outputs) if (isCommand(output)) if (output.graph === Command.PARENT && Array.isArray(output.goto) && output.goto.every((send) => _isSend(send))) if (parentCommand) parentCommand.goto.push(...output.goto);
		else parentCommand = new Command({
			graph: Command.PARENT,
			goto: output.goto
		});
		else combinedOutputs.push(output);
		else combinedOutputs.push(Array.isArray(input) ? [output] : { messages: [output] });
		if (parentCommand) combinedOutputs.push(parentCommand);
		return combinedOutputs;
	}
};
/**
* A conditional edge function that determines whether to route to a tools node or end the graph.
*
* This function is designed to be used as a conditional edge in a LangGraph state graph to implement
* the common pattern of checking if an AI message contains tool calls that need to be executed.
*
* @param state - The current state of the graph, which can be either:
*   - An array of `BaseMessage` objects, where the last message is checked for tool calls
*   - A state object conforming to `MessagesAnnotation.State`, which contains a `messages` array
*
* @returns A string indicating the next node to route to:
*   - `"tools"` - If the last message contains tool calls that need to be executed
*   - `END` - If there are no tool calls, indicating the graph should terminate
*
* @example
* ```typescript
* import { StateGraph, MessagesAnnotation, END, START } from "@langchain/langgraph";
* import { ToolNode, toolsCondition } from "@langchain/langgraph/prebuilt";
*
* const graph = new StateGraph(MessagesAnnotation)
*   .addNode("agent", agentNode)
*   .addNode("tools", new ToolNode([searchTool, calculatorTool]))
*   .addEdge(START, "agent")
*   .addConditionalEdges("agent", toolsCondition, ["tools", END])
*   .addEdge("tools", "agent")
*   .compile();
* ```
*
* @remarks
* The function checks the last message in the state for the presence of `tool_calls`.
* If the message is an `AIMessage` with one or more tool calls, it returns `"tools"`,
* indicating that the graph should route to a tools node (typically a `ToolNode`) to
* execute those tool calls. Otherwise, it returns `END` to terminate the graph execution.
*
* This is a common pattern in agentic workflows where an AI model decides whether to
* use tools or provide a final response.
*/
function toolsCondition(state) {
	const message = Array.isArray(state) ? state[state.length - 1] : state.messages[state.messages.length - 1];
	if (message !== void 0 && "tool_calls" in message && (message.tool_calls?.length ?? 0) > 0) return "tools";
	else return END;
}
//#endregion
export { ToolNode, toolsCondition };

//# sourceMappingURL=tool_node.js.map