diff --git a/libs/langgraph/langgraph/graph/ui.py b/libs/langgraph/langgraph/graph/ui.py index 1682fcdcd..847b2c7fa 100644 --- a/libs/langgraph/langgraph/graph/ui.py +++ b/libs/langgraph/langgraph/graph/ui.py @@ -81,9 +81,8 @@ def push_ui_message( metadata: Optional additional metadata about the UI message. message: Optional message object to associate with the UI message. state_key: Key in the graph state where the UI messages are stored. - Defaults to "ui". merge: Whether to merge props with existing UI message (True) or replace - them (False). Defaults to False. + them (False). Returns: The created UI message. diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py index ae3f70986..18e8854c7 100644 --- a/libs/langgraph/langgraph/types.py +++ b/libs/langgraph/langgraph/types.py @@ -55,6 +55,7 @@ __all__ = ( "Command", "Durability", "interrupt", + "Overwrite", ) Durability = Literal["sync", "async", "exit"] @@ -283,26 +284,32 @@ class Send: node (str): The name of the target node to send the message to. arg (Any): The state or message to send to the target node. - Examples: - >>> from typing import Annotated - >>> import operator - >>> class OverallState(TypedDict): - ... subjects: list[str] - ... jokes: Annotated[list[str], operator.add] - >>> from langgraph.types import Send - >>> from langgraph.graph import END, START - >>> def continue_to_jokes(state: OverallState): - ... return [Send("generate_joke", {"subject": s}) for s in state["subjects"]] - >>> from langgraph.graph import StateGraph - >>> builder = StateGraph(OverallState) - >>> builder.add_node("generate_joke", lambda state: {"jokes": [f"Joke about {state['subject']}"]}) - >>> builder.add_conditional_edges(START, continue_to_jokes) - >>> builder.add_edge("generate_joke", END) - >>> graph = builder.compile() - >>> - >>> # Invoking with two subjects results in a generated joke for each - >>> graph.invoke({"subjects": ["cats", "dogs"]}) - {'subjects': ['cats', 'dogs'], 'jokes': ['Joke about cats', 'Joke about dogs']} + !!! example + + ```python + from typing import Annotated + from langgraph.types import Send + from langgraph.graph import END, START + from langgraph.graph import StateGraph + import operator + + class OverallState(TypedDict): + subjects: list[str] + jokes: Annotated[list[str], operator.add] + + def continue_to_jokes(state: OverallState): + return [Send("generate_joke", {"subject": s}) for s in state["subjects"]] + + builder = StateGraph(OverallState) + builder.add_node("generate_joke", lambda state: {"jokes": [f"Joke about {state['subject']}"]}) + builder.add_conditional_edges(START, continue_to_jokes) + builder.add_edge("generate_joke", END) + graph = builder.compile() + + # Invoking with two subjects results in a generated joke for each + graph.invoke({"subjects": ["cats", "dogs"]}) + # {'subjects': ['cats', 'dogs'], 'jokes': ['Joke about cats', 'Joke about dogs']} + ``` """ __slots__ = ("node", "arg") @@ -342,10 +349,8 @@ N = TypeVar("N", bound=Hashable) class Command(Generic[N], ToolOutputMixin): """One or more commands to update the graph's state and send messages to nodes. - !!! version-added "Added in version 0.2.24" - Args: - graph: graph to send the command to. Supported values are: + graph: Graph to send the command to. Supported values are: - `None`: the current graph - `Command.PARENT`: closest parent graph @@ -415,7 +420,8 @@ def interrupt(value: Any) -> Any: To use an `interrupt`, you must enable a checkpointer, as the feature relies on persisting the graph state. - Example: + !!! example + ```python import uuid from typing import Optional @@ -520,38 +526,42 @@ def interrupt(value: Any) -> Any: @dataclass(slots=True) class Overwrite: - """Bypass a reducer and write the wrapped value directly to a BinaryOperatorAggregate channel. + """Bypass a reducer and write the wrapped value directly to a `BinaryOperatorAggregate` channel. - Receiving multiple Overwrite values for the same channel in a single super-step will raise an InvalidUpdateError. + Receiving multiple `Overwrite` values for the same channel in a single super-step + will raise an `InvalidUpdateError`. - Example: - >>> from typing import Annotated - >>> import operator - >>> from langgraph.graph import StateGraph - >>> from langgraph.types import Overwrite - >>> - >>> class State(TypedDict): - ... messages: Annotated[list, operator.add] - >>> - >>> def node_a(state: TypedDict): - ... # Normal update: uses the reducer (operator.add) - ... return {"messages": ["a"]} - >>> - >>> def node_b(state: State): - ... # Overwrite: bypasses the reducer and replaces the entire value - ... return {"messages": Overwrite(value=["b"])} - >>> - >>> builder = StateGraph(State) - >>> builder.add_node("node_a", node_a) - >>> builder.add_node("node_b", node_b) - >>> builder.set_entry_point("node_a") - >>> builder.add_edge("node_a", "node_b") - >>> graph = builder.compile() - >>> - >>> # Without Overwrite in node_b, messages would be ["START", "a", "b"] - >>> # With Overwrite, messages is just ["b"] - >>> result = graph.invoke({"messages": ["START"]}) - >>> assert result == {"messages": ["b"]} + !!! example + + ```python + from typing import Annotated + import operator + from langgraph.graph import StateGraph + from langgraph.types import Overwrite + + class State(TypedDict): + messages: Annotated[list, operator.add] + + def node_a(state: TypedDict): + # Normal update: uses the reducer (operator.add) + return {"messages": ["a"]} + + def node_b(state: State): + # Overwrite: bypasses the reducer and replaces the entire value + return {"messages": Overwrite(value=["b"])} + + builder = StateGraph(State) + builder.add_node("node_a", node_a) + builder.add_node("node_b", node_b) + builder.set_entry_point("node_a") + builder.add_edge("node_a", "node_b") + graph = builder.compile() + + # Without Overwrite in node_b, messages would be ["START", "a", "b"] + # With Overwrite, messages is just ["b"] + result = graph.invoke({"messages": ["START"]}) + assert result == {"messages": ["b"]} + ``` """ value: Any diff --git a/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py b/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py index c30c64676..6c0df122d 100644 --- a/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py +++ b/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py @@ -309,37 +309,40 @@ def create_react_agent( model: The language model for the agent. Supports static and dynamic model selection. - - **Static model**: A chat model instance (e.g., `ChatOpenAI()`) or - string identifier (e.g., `"openai:gpt-4"`) + - **Static model**: A chat model instance (e.g., + [`ChatOpenAI`][langchain_openai.ChatOpenAI]) or string identifier (e.g., + `"openai:gpt-4"`) - **Dynamic model**: A callable with signature - `(state, runtime) -> BaseChatModel` that returns different models - based on runtime context - If the model has tools bound via `.bind_tools()` or other configurations, - the return type should be a Runnable[LanguageModelInput, BaseMessage] - Coroutines are also supported, allowing for asynchronous model selection. + `(state, runtime) -> BaseChatModel` that returns different models + based on runtime context + + If the model has tools bound via `bind_tools` or other configurations, + the return type should be a `Runnable[LanguageModelInput, BaseMessage]` + Coroutines are also supported, allowing for asynchronous model selection. Dynamic functions receive graph state and runtime, enabling context-dependent model selection. Must return a `BaseChatModel` instance. For tool calling, bind tools using `.bind_tools()`. Bound tools must be a subset of the `tools` parameter. - Dynamic model example: - ```python - from dataclasses import dataclass + !!! example "Dynamic model" - @dataclass - class ModelContext: - model_name: str = "gpt-3.5-turbo" + ```python + from dataclasses import dataclass - # Instantiate models globally - gpt4_model = ChatOpenAI(model="gpt-4") - gpt35_model = ChatOpenAI(model="gpt-3.5-turbo") + @dataclass + class ModelContext: + model_name: str = "gpt-3.5-turbo" - def select_model(state: AgentState, runtime: Runtime[ModelContext]) -> ChatOpenAI: - model_name = runtime.context.model_name - model = gpt4_model if model_name == "gpt-4" else gpt35_model - return model.bind_tools(tools) - ``` + # Instantiate models globally + gpt4_model = ChatOpenAI(model="gpt-4") + gpt35_model = ChatOpenAI(model="gpt-3.5-turbo") + + def select_model(state: AgentState, runtime: Runtime[ModelContext]) -> ChatOpenAI: + model_name = runtime.context.model_name + model = gpt4_model if model_name == "gpt-4" else gpt35_model + return model.bind_tools(tools) + ``` !!! note "Dynamic Model Requirements" @@ -351,23 +354,26 @@ def create_react_agent( If an empty list is provided, the agent will consist of a single LLM node without tool calling. prompt: An optional prompt for the LLM. Can take a few different forms: - - str: This is converted to a SystemMessage and added to the beginning of the list of messages in state["messages"]. - - SystemMessage: this is added to the beginning of the list of messages in state["messages"]. - - Callable: This function should take in full graph state and the output is then passed to the language model. - - Runnable: This runnable should take in full graph state and the output is then passed to the language model. + - `str`: This is converted to a `SystemMessage` and added to the beginning of the list of messages in `state["messages"]`. + - `SystemMessage`: this is added to the beginning of the list of messages in `state["messages"]`. + - `Callable`: This function should take in full graph state and the output is then passed to the language model. + - `Runnable`: This runnable should take in full graph state and the output is then passed to the language model. response_format: An optional schema for the final agent output. If provided, output will be formatted to match the given schema and returned in the 'structured_response' state key. + If not provided, `structured_response` will not be present in the output state. + Can be passed in as: - - an OpenAI function/tool schema, - - a JSON Schema, - - a TypedDict class, - - or a Pydantic class. - - a tuple (prompt, schema), where schema is one of the above. - The prompt will be used together with the model that is being used to generate the structured response. + - An OpenAI function/tool schema, + - A JSON Schema, + - A TypedDict class, + - A Pydantic class. + - A tuple `(prompt, schema)`, where schema is one of the above. + The prompt will be used together with the model that is being used to + generate the structured response. !!! Important `response_format` requires the model to support `.with_structured_output` @@ -428,13 +434,16 @@ def create_react_agent( store: An optional store object. This is used for persisting data across multiple threads (e.g., multiple conversations / users). interrupt_before: An optional list of node names to interrupt before. - Should be one of the following: "agent", "tools". + Should be one of the following: `"agent"`, `"tools"`. + This is useful if you want to add a user confirmation or other interrupt before taking an action. interrupt_after: An optional list of node names to interrupt after. - Should be one of the following: "agent", "tools". + Should be one of the following: `"agent"`, `"tools"`. + This is useful if you want to return directly or run additional processing on an output. debug: A flag indicating whether to enable debug mode. version: Determines the version of the graph to create. + Can be one of: - `"v1"`: The tool node processes a single message. All tool @@ -443,7 +452,7 @@ def create_react_agent( Tool calls are distributed across multiple instances of the tool node using the [Send](https://langchain-ai.github.io/langgraph/concepts/low_level/#send) API. - name: An optional name for the CompiledStateGraph. + name: An optional name for the `CompiledStateGraph`. This name will be automatically used when adding ReAct agent graph to another graph as a subgraph node - particularly useful for building multi-agent systems. @@ -453,14 +462,14 @@ def create_react_agent( Returns: - A compiled LangChain runnable that can be used for chat interactions. + A compiled LangChain `Runnable` that can be used for chat interactions. The "agent" node calls the language model with the messages list (after applying the prompt). If the resulting AIMessage contains `tool_calls`, the graph will then call the ["tools"][langgraph.prebuilt.tool_node.ToolNode]. The "tools" node executes the tools (1 tool per `tool_call`) and adds the responses to the messages list as `ToolMessage` objects. The agent node then calls the language model again. The process repeats until no more `tool_calls` are present in the response. - The agent then returns the full list of messages as a dictionary containing the key "messages". + The agent then returns the full list of messages as a dictionary containing the key `'messages'`. ``` mermaid sequenceDiagram diff --git a/libs/prebuilt/langgraph/prebuilt/interrupt.py b/libs/prebuilt/langgraph/prebuilt/interrupt.py index d2e6058d0..d23c11c85 100644 --- a/libs/prebuilt/langgraph/prebuilt/interrupt.py +++ b/libs/prebuilt/langgraph/prebuilt/interrupt.py @@ -36,7 +36,7 @@ class ActionRequest(TypedDict): Contains the action type and any associated arguments needed for the action. Attributes: - action: The type or name of action being requested (e.g., "Approve XYZ action") + action: The type or name of action being requested (e.g., `"Approve XYZ action"`) args: Key-value pairs of arguments needed for the action """ @@ -89,14 +89,16 @@ class HumanResponse(TypedDict): Attributes: type: The type of response: - - "accept": Approves the current state without changes - - "ignore": Skips/ignores the current step - - "response": Provides text feedback or instructions - - "edit": Modifies the current state/content + + - `'accept'`: Approves the current state without changes + - `'ignore'`: Skips/ignores the current step + - `'response'`: Provides text feedback or instructions + - `'edit'`: Modifies the current state/content args: The response payload: - - None: For ignore/accept actions - - str: For text responses - - ActionRequest: For edit actions with updated content + + - `None`: For ignore/accept actions + - `str`: For text responses + - `ActionRequest`: For edit actions with updated content """ type: Literal["accept", "ignore", "response", "edit"] diff --git a/libs/prebuilt/langgraph/prebuilt/tool_node.py b/libs/prebuilt/langgraph/prebuilt/tool_node.py index 78c6222f1..c287743dd 100644 --- a/libs/prebuilt/langgraph/prebuilt/tool_node.py +++ b/libs/prebuilt/langgraph/prebuilt/tool_node.py @@ -6,6 +6,7 @@ Tools are functions that models can call to interact with external systems, APIs, databases, or perform computations. The module implements design patterns for: + - Parallel execution of multiple tool calls for efficiency - Robust error handling with customizable error messages - State injection for tools that need access to graph state @@ -13,11 +14,13 @@ The module implements design patterns for: - Command-based state updates for advanced control flow Key Components: - `ToolNode`: Main class for executing tools in LangGraph workflows - `InjectedState`: Annotation for injecting graph state into tools - `InjectedStore`: Annotation for injecting persistent store into tools - `ToolRuntime`: Runtime information for tools, bundling together state, context, config, stream_writer, tool_call_id, and store - `tools_condition`: Utility function for conditional routing based on tool calls + +- `ToolNode`: Main class for executing tools in LangGraph workflows +- `InjectedState`: Annotation for injecting graph state into tools +- `InjectedStore`: Annotation for injecting persistent store into tools +- `ToolRuntime`: Runtime information for tools, bundling together `state`, `context`, + `config`, `stream_writer`, `tool_call_id`, and `store` +- `tools_condition`: Utility function for conditional routing based on tool calls Typical Usage: ```python @@ -552,44 +555,52 @@ class ToolNode(RunnableCallable): Output format depends on input type and tool behavior: **For Regular tools**: + - Dict input → `{"messages": [ToolMessage(...)]}` - List input → `[ToolMessage(...)]` **For Command tools**: + - Returns `[Command(...)]` or mixed list with regular tool outputs - - Commands can update state, trigger navigation, or send messages + - `Command` can update state, trigger navigation, or send messages Args: - tools: A sequence of tools that can be invoked by this node. Supports: + tools: A sequence of tools that can be invoked by this node. + + Supports: + - **BaseTool instances**: Tools with schemas and metadata - **Plain functions**: Automatically converted to tools with inferred schemas + name: The name identifier for this node in the graph. Used for debugging - and visualization. Defaults to "tools". + and visualization. tags: Optional metadata tags to associate with the node for filtering - and organization. Defaults to `None`. + and organization. handle_tool_errors: Configuration for error handling during tool execution. Supports multiple strategies: - - **True**: Catch all errors and return a ToolMessage with the default + - `True`: Catch all errors and return a `ToolMessage` with the default error template containing the exception details. - - **str**: Catch all errors and return a ToolMessage with this custom + - `str`: Catch all errors and return a `ToolMessage` with this custom error message string. - - **type[Exception]**: Only catch exceptions with the specified type and + - `type[Exception]`: Only catch exceptions with the specified type and return the default error message for it. - - **tuple[type[Exception], ...]**: Only catch exceptions with the specified + - `tuple[type[Exception], ...]`: Only catch exceptions with the specified types and return default error messages for them. - - **Callable[..., str]**: Catch exceptions matching the callable's signature + - `Callable[..., str]`: Catch exceptions matching the callable's signature and return the string result of calling it with the exception. - - **False**: Disable error handling entirely, allowing exceptions to + - `False`: Disable error handling entirely, allowing exceptions to propagate. Defaults to a callable that: - - catches tool invocation errors (due to invalid arguments provided by the model) and returns a descriptive error message - - ignores tool execution errors (they will be re-raised) + + - Catches tool invocation errors (due to invalid arguments provided by the + model) and returns a descriptive error message + - Ignores tool execution errors (they will be re-raised) messages_key: The key in the state dictionary that contains the message list. This same key will be used for the output `ToolMessage` objects. - Defaults to "messages". + Allows custom state schemas with different message field names. Examples: @@ -1393,7 +1404,7 @@ def tools_condition( """Conditional routing function for tool-calling workflows. This utility function implements the standard conditional logic for ReAct-style - agents: if the last AI message contains tool calls, route to the tool execution + agents: if the last `AIMessage` contains tool calls, route to the tool execution node; otherwise, end the workflow. This pattern is fundamental to most tool-calling agent architectures. @@ -1402,16 +1413,15 @@ def tools_condition( Args: state: The current graph state to examine for tool calls. Supported formats: - - Dictionary containing a messages key (for StateGraph) - - BaseModel instance with a messages attribute + - Dictionary containing a messages key (for `StateGraph`) + - `BaseModel` instance with a messages attribute messages_key: The key or attribute name containing the message list in the state. This allows customization for graphs using different state schemas. - Defaults to "messages". Returns: - Either "tools" if tool calls are present in the last AI message, or "__end__" - to terminate the workflow. These are the standard routing destinations for - tool-calling conditional edges. + Either `'tools'` if tool calls are present in the last `AIMessage`, or `'__end__'` + to terminate the workflow. These are the standard routing destinations for + tool-calling conditional edges. Raises: ValueError: If no messages can be found in the provided state format. @@ -1608,7 +1618,7 @@ class InjectedStore(InjectedToolArg): This annotation enables tools to access LangGraph's persistent storage system without exposing storage details to the language model. Tools annotated with - InjectedStore receive the store instance automatically during execution while + `InjectedStore` receive the store instance automatically during execution while remaining invisible to the model's tool-calling interface. The store provides persistent, cross-session data storage that tools can use diff --git a/libs/prebuilt/langgraph/prebuilt/tool_validator.py b/libs/prebuilt/langgraph/prebuilt/tool_validator.py index fb2b64b87..ec9b3c290 100644 --- a/libs/prebuilt/langgraph/prebuilt/tool_validator.py +++ b/libs/prebuilt/langgraph/prebuilt/tool_validator.py @@ -45,9 +45,9 @@ def _default_format_error( category=LangGraphDeprecatedSinceV10, ) class ValidationNode(RunnableCallable): - """A node that validates all tools requests from the last AIMessage. + """A node that validates all tools requests from the last `AIMessage`. - It can be used either in StateGraph with a "messages" key. + It can be used either in `StateGraph` with a `'messages'` key. !!! note @@ -57,7 +57,8 @@ class ValidationNode(RunnableCallable): messages and tool IDs (for use in multi-turn conversations). Returns: - (Union[Dict[str, List[ToolMessage]], Sequence[ToolMessage]]): A list of ToolMessages with the validated content or error messages. + (Union[Dict[str, List[ToolMessage]], Sequence[ToolMessage]]): A list of + `ToolMessage` objects with the validated content or error messages. Example: ```python title="Example usage for re-prompting the model to generate a valid response:"