diff --git a/docs/docs/concepts/low_level.md b/docs/docs/concepts/low_level.md
index 05ceacdea..1d059568b 100644
--- a/docs/docs/concepts/low_level.md
+++ b/docs/docs/concepts/low_level.md
@@ -283,6 +283,9 @@ You can optionally provide a dictionary that maps the `routing_function`'s outpu
graph.add_conditional_edges("node_a", routing_function, {True: "node_b", False: "node_c"})
```
+!!! tip
+ Use [`Command`](#command) instead of conditional edges if you want to combine state updates and routing in a single function.
+
### Entry Point
The entry point is the first node(s) that are run when the graph starts. You can use the [`add_edge`][langgraph.graph.StateGraph.add_edge] method from the virtual [`START`][langgraph.constants.START] node to the first node to execute to specify where to enter the graph.
@@ -322,6 +325,65 @@ def continue_to_jokes(state: OverallState):
graph.add_conditional_edges("node_a", continue_to_jokes)
```
+## `Command`
+
+It can be useful to combine control flow (edges) and state updates (nodes). For example, you might want to BOTH perform state updates AND decide which node to go to next in the SAME node. LangGraph provides a way to do so by returning a [`Command`][langgraph.types.Command] object from node functions:
+
+```python
+def my_node(state: State) -> Command[Literal["my_other_node"]]:
+ return Command(
+ # state update
+ update={"foo": "bar"},
+ # control flow
+ goto="my_other_node"
+ )
+```
+
+`Command` has the following properties:
+
+| Property | Description |
+| --- | --- |
+| `graph` | Graph to send the command to. Supported values:
- `None`: the current graph (default)
- `Command.PARENT`: closest parent graph |
+| `update` | Update to apply to the graph's state. |
+| `resume` | Value to resume execution with. To be used together with [`interrupt()`][langgraph.types.interrupt]. |
+| `goto` | Can be one of the following:
- name of the node to navigate to next (any node that belongs to the specified `graph`)
- sequence of node names to navigate to next
- `Send` object (to execute a node with the input provided)
- sequence of `Send` objects
If `goto` is not specified and there are no other tasks left in the graph, the graph will halt after executing the current superstep. |
+
+```python
+from langgraph.graph import StateGraph, START
+from langgraph.types import Command
+from typing_extensions import Literal, TypedDict
+
+class State(TypedDict):
+ foo: str
+
+def my_node(state: State) -> Command[Literal["my_other_node"]]:
+ return Command(update={"foo": "bar"}, goto="my_other_node")
+
+def my_other_node(state: State):
+ return {"foo": state["foo"] + "baz"}
+
+builder = StateGraph(State)
+builder.add_edge(START, "my_node")
+builder.add_node("my_node", my_node)
+builder.add_node("my_other_node", my_other_node)
+
+graph = builder.compile()
+```
+
+With `Command` you can also achieve dynamic control flow behavior (identical to [conditional edges](#conditional-edges)):
+
+```python
+def my_node(state: State) -> Command[Literal["my_other_node"]]:
+ if state["foo"] == "bar":
+ return Command(update={"foo": "baz"}, goto="my_other_node")
+```
+
+!!! important
+
+ When returning `Command` in your node functions, you must add return type annotations with the list of node names the node is routing to, e.g. `Command[Literal["node_b", "node_c"]]`. This is necessary for the graph compilation and rendering, and tells LangGraph that `node_a` can navigate to `node_b` and `node_c`.
+
+Check out this [how-to guide](../how-tos/command.ipynb) for an end-to-end example of how to use `Command`.
+
## Persistence
LangGraph provides built-in persistence for your agent's state using [checkpointers][langgraph.checkpoint.base.BaseCheckpointSaver]. Checkpointers save snapshots of the graph state at every superstep, allowing resumption at any time. This enables features like human-in-the-loop interactions, memory management, and fault-tolerance. You can even directly manipulate a graph's state after its execution using the
diff --git a/docs/docs/how-tos/command.ipynb b/docs/docs/how-tos/command.ipynb
new file mode 100644
index 000000000..3f3a4c0ef
--- /dev/null
+++ b/docs/docs/how-tos/command.ipynb
@@ -0,0 +1,259 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "d33ecddc-6818-41a3-9d0d-b1b1cbcd286d",
+ "metadata": {},
+ "source": [
+ "# How to combine control flow and state updates with Command"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7c0a8d03-80b4-47fd-9b17-e26aa9b081f3",
+ "metadata": {},
+ "source": [
+ "!!! info \"Prerequisites\"\n",
+ " This guide assumes familiarity with the following:\n",
+ " \n",
+ " - [State](../../concepts/low_level/#state)\n",
+ " - [Nodes](../../concepts/low_level/#nodes)\n",
+ " - [Edges](../../concepts/low_level/#edges)\n",
+ " - [Command](../../concepts/low_level/#command)\n",
+ "\n",
+ "It can be useful to combine control flow (edges) and state updates (nodes). For example, you might want to BOTH perform state updates AND decide which node to go to next in the SAME node. LangGraph provides a way to do so by returning a `Command` object from node functions:\n",
+ "\n",
+ "```python\n",
+ "def my_node(state: State) -> Command[Literal[\"my_other_node\"]]:\n",
+ " return GraphCommand(\n",
+ " # state update\n",
+ " update={\"foo\": \"bar\"},\n",
+ " # control flow\n",
+ " goto=\"my_other_node\"\n",
+ " )\n",
+ "```\n",
+ "\n",
+ "This guide shows how you can do use `Command` to add dynamic control flow in your LangGraph app."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d1c3f866-8c20-40c7-a201-35f6c9f4b680",
+ "metadata": {},
+ "source": [
+ "## Setup\n",
+ "\n",
+ "First, let's install the required packages"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "6999c7fe-31bb-4c19-946a-85c2edc57da7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture --no-stderr\n",
+ "%pip install -U langgraph"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0f131c92-4744-431c-a89c-7c382a15b79f",
+ "metadata": {},
+ "source": [
+ "
\n",
+ "
Set up LangSmith for LangGraph development
\n",
+ "
\n",
+ " Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started here. \n",
+ "
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f22c228f-6882-4757-8e7e-1ca51328af4a",
+ "metadata": {},
+ "source": [
+ "Let's create a simple graph with 3 nodes: A, B and C. We will first execute node A, and then decide whether to go to Node B or Node C next based on the output of node A."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6a08d957-b3d2-4538-bf4a-68ef90a51b98",
+ "metadata": {},
+ "source": [
+ "## Define graph"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "4539b81b-09e9-4660-ac55-1b1775e13892",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import random\n",
+ "from typing_extensions import TypedDict, Literal\n",
+ "\n",
+ "from langgraph.graph import StateGraph, START\n",
+ "from langgraph.types import Command\n",
+ "\n",
+ "\n",
+ "# Define graph state\n",
+ "class State(TypedDict):\n",
+ " foo: str\n",
+ "\n",
+ "\n",
+ "# Define the nodes\n",
+ "\n",
+ "\n",
+ "def node_a(state: State) -> Command[Literal[\"node_b\", \"node_c\"]]:\n",
+ " print(\"Called A\")\n",
+ " value = random.choice([\"a\", \"b\"])\n",
+ " # this is a replacement for a conditional edge function\n",
+ " if value == \"a\":\n",
+ " goto = \"node_b\"\n",
+ " else:\n",
+ " goto = \"node_c\"\n",
+ "\n",
+ " # note how Command allows you to BOTH update the graph state AND route to the next node\n",
+ " return Command(\n",
+ " # this is the state update\n",
+ " update={\"foo\": value},\n",
+ " # this is a replacement for an edge\n",
+ " goto=goto,\n",
+ " )\n",
+ "\n",
+ "\n",
+ "# Nodes B and C are unchanged\n",
+ "\n",
+ "\n",
+ "def node_b(state: State):\n",
+ " print(\"Called B\")\n",
+ " return {\"foo\": state[\"foo\"] + \"b\"}\n",
+ "\n",
+ "\n",
+ "def node_c(state: State):\n",
+ " print(\"Called C\")\n",
+ " return {\"foo\": state[\"foo\"] + \"c\"}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "badc25eb-4876-482e-bb10-d763023cdaad",
+ "metadata": {},
+ "source": [
+ "We can now create the `StateGraph` with the above nodes. Notice that the graph doesn't have [conditional edges](../../concepts/low_level#conditional-edges) for routing! This is because control flow is defined with `GraphCommand` inside `node_a`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "d6711650-4380-4551-a007-2805f49ab2d8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "builder = StateGraph(State)\n",
+ "builder.add_edge(START, \"node_a\")\n",
+ "builder.add_node(node_a)\n",
+ "builder.add_node(node_b)\n",
+ "builder.add_node(node_c)\n",
+ "# NOTE: there are no edges between nodes A, B and C!\n",
+ "\n",
+ "graph = builder.compile()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0ab344c5-d634-4d7d-b3b4-edf4fa875311",
+ "metadata": {},
+ "source": [
+ "!!! important\n",
+ "\n",
+ " You might have noticed that we used `Command` as a return type annotation, e.g. `Command[Literal[\"node_b\", \"node_c\"]]`. This is necessary for the graph compilation and rendering, and tells LangGraph that `node_a` can navigate to `node_b` and `node_c`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "eeb810e5-8822-4c09-8d53-c55cd0f5d42e",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/jpeg": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "from IPython.display import display, Image\n",
+ "\n",
+ "display(Image(graph.get_graph().draw_mermaid_png()))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "58fb6c32-e6fb-4c94-8182-e351ed52a45d",
+ "metadata": {},
+ "source": [
+ "If we run the graph multiple times, we'd see it take different paths (A -> B or A -> C) based on the random choice in node A."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "d88a5d9b-ee08-4ed4-9c65-6e868210bfac",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Called A\n",
+ "Called C\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'foo': 'bc'}"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "graph.invoke({\"foo\": \"\"})"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/docs/docs/how-tos/index.md b/docs/docs/how-tos/index.md
index f0c2f841d..8256a9adb 100644
--- a/docs/docs/how-tos/index.md
+++ b/docs/docs/how-tos/index.md
@@ -20,6 +20,7 @@ These how-to guides show how to achieve that controllability.
- [How to create branches for parallel execution](branching.ipynb)
- [How to create map-reduce branches for parallel execution](map-reduce.ipynb)
- [How to control graph recursion limit](recursion-limit.ipynb)
+- [How to combine control flow and state updates with Command](command.ipynb)
### Persistence
diff --git a/docs/docs/reference/types.md b/docs/docs/reference/types.md
index 347a87d6e..b42b11f35 100644
--- a/docs/docs/reference/types.md
+++ b/docs/docs/reference/types.md
@@ -13,3 +13,5 @@
- PregelExecutableTask
- StateSnapshot
- Send
+ - Command
+ - interrupt
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 58489b12a..bc1ff59ea 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -151,6 +151,7 @@ nav:
- how-tos/branching.ipynb
- how-tos/map-reduce.ipynb
- how-tos/recursion-limit.ipynb
+ - how-tos/command.ipynb
- Persistence:
- Persistence: how-tos#persistence
- how-tos/persistence.ipynb
diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py
index bd6e94d19..3eb4536aa 100644
--- a/libs/langgraph/langgraph/types.py
+++ b/libs/langgraph/langgraph/types.py
@@ -245,7 +245,22 @@ N = TypeVar("N", bound=Hashable)
@dataclasses.dataclass(**_DC_KWARGS)
class Command(Generic[N]):
- """One or more commands to update the graph's state and send messages to nodes."""
+ """One or more commands to update the graph's state and send messages to nodes.
+
+ Args:
+ graph: graph to send the command to. Supported values are:
+
+ - None: the current graph (default)
+ - GraphCommand.PARENT: closest parent graph
+ update: update to apply to the graph's state.
+ resume: value to resume execution with. To be used together with [`interrupt()`][langgraph.types.interrupt].
+ goto: can be one of the following:
+
+ - name of the node to navigate to next (any node that belongs to the specified `graph`)
+ - sequence of node names to navigate to next
+ - `Send` object (to execute a node with the input provided)
+ - sequence of `Send` objects
+ """
graph: Optional[str] = None
update: Optional[dict[str, Any]] = None