@langchain/langgraph
Version:
232 lines (231 loc) • 8.74 kB
text/typescript
import { Command, END } from "../constants.cjs";
import { LangGraphRunnableConfig } from "../pregel/runnable_types.cjs";
import { RunnableCallable } from "../utils.cjs";
import { MessagesAnnotation } from "../graph/messages_annotation.cjs";
import { RunnableToolLike } from "@langchain/core/runnables";
import { BaseMessage, ToolMessage } from "@langchain/core/messages";
import { DynamicTool, StructuredToolInterface } from "@langchain/core/tools";
import { ToolCall } from "@langchain/core/messages/tool";
//#region src/prebuilt/tool_node.d.ts
type ToolNodeOptions = {
name?: string;
tags?: string[];
handleToolErrors?: boolean;
};
/**
* 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" });
* ```
*/
declare class ToolNode<T = any> extends RunnableCallable<T, T> {
tools: (StructuredToolInterface | DynamicTool | RunnableToolLike)[];
handleToolErrors: boolean;
trace: boolean;
constructor(tools: (StructuredToolInterface | DynamicTool | RunnableToolLike)[], options?: ToolNodeOptions);
protected runTool(call: ToolCall, config: LangGraphRunnableConfig, state: unknown): Promise<ToolMessage | Command>;
protected run(input: unknown, config: LangGraphRunnableConfig): Promise<T>;
}
/**
* 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.
*/
declare function toolsCondition(state: BaseMessage[] | typeof MessagesAnnotation.State): "tools" | typeof END;
//#endregion
export { ToolNode, ToolNodeOptions, toolsCondition };
//# sourceMappingURL=tool_node.d.cts.map