Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 124 additions & 51 deletions src/oss/langgraph/graph-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1041,8 +1041,26 @@ graph.addConditionalEdges("nodeA", (state) => {

## `Command`

@[`Command`] is a versatile primitive for controlling graph execution. It accepts four parameters:

- `update`: Apply state updates (similar to returning updates from a node).
- `goto`: Navigate to specific nodes (similar to [conditional edges](#conditional-edges)).
- `graph`: Target a parent graph when navigating from [subgraphs](/oss/langgraph/use-subgraphs).
- `resume`: Provide a value to resume execution after an [interrupt](/oss/langgraph/interrupts).

`Command` is used in three contexts:

- **[Return from nodes](#return-from-nodes)**: Use `update`, `goto`, and `graph` to combine state updates with control flow.
- **[Input to `invoke`/`stream`](#input-to-invokestream)**: Use `resume` to continue execution after an interrupt.
- **[Return from tools](#return-from-tools)**: Similar to return from nodes, combine state updates and control flow from inside a tool.

### Return from nodes

#### `update` and `goto`

Return @[`Command`] from node functions to update state and route to the next node in a single step:

:::python
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:

```python
def my_node(state: State) -> Command[Literal["my_other_node"]]:
Expand All @@ -1062,20 +1080,7 @@ def my_node(state: State) -> Command[Literal["my_other_node"]]:
return Command(update={"foo": "baz"}, goto="my_other_node")
```

Note that @[`Command`] only adds dynamic edges, while static edges will still execute. In other words, @[`Command`] doesn't override static edges.

```python
def node_a(state: State) -> Command[Literal["my_other_node"]]:
if state["foo"] == "bar":
return Command(update={"foo": "baz"}, goto="my_other_node")

# Add a static edge from "node_a" to "node_b"
graph.add_edge("node_a", "node_b")

# Command will NOT prevent "node_a" from going to "node_b"
```

In the example above, **"node_a"** will go to both **"node_b"** and **"my_other_node"**.
Use @[`Command`] when you need to **both** update state **and** route to a different node. If you only need to route without updating state, use [conditional edges](#conditional-edges) instead.

<Note>

Expand All @@ -1086,7 +1091,6 @@ When returning @[`Command`] in your node functions, you must add return type ann
:::

:::js
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:

```typescript
import { Command } from "@langchain/langgraph";
Expand Down Expand Up @@ -1114,25 +1118,7 @@ graph.addNode("myNode", (state) => {
});
```

Note that @[Command] only adds dynamic edges, while static edges will still execute. In other words, @[Command] doesn't override static edges.

```typescript
graph.addNode("nodeA", (state) => {
if (state.foo === "bar") {
return new Command({
update: { foo: "baz" },
goto: "myOtherNode",
});
}
});

// Add a static edge from "nodeA" to "nodeB"
graph.addEdge("nodeA", "nodeB")

// Command will NOT prevent "nodeA" from going to "nodeB"
```

In the example above, **"nodeA"** will go to both **"nodeB"** and **"myOtherNode"**.
Use @[`Command`] when you need to **both** update state **and** route to a different node. If you only need to route without updating state, use [conditional edges](#conditional-edges) instead.

When using @[`Command`] in your node functions, you must add the `ends` parameter when adding the node to specify which nodes it can route to:

Expand All @@ -1144,17 +1130,18 @@ builder.addNode("myNode", myNode, {

:::

Check out this [how-to guide](/oss/langgraph/use-graph-api#combine-control-flow-and-state-updates-with-command) for an end-to-end example of how to use @[`Command`].
<Warning>

### When should I use command instead of conditional edges?
@[`Command`] only adds dynamic edges — static edges defined with `add_edge` / `addEdge` still execute. For example, if `node_a` returns `Command(goto="my_other_node")` and you also have `graph.add_edge("node_a", "node_b")`, both `node_b` and `my_other_node` will run.

</Warning>

- Use @[`Command`] when you need to **both** update the graph state **and** route to a different node. For example, when implementing [multi-agent handoffs](/oss/langchain/multi-agent/handoffs) where it's important to route to a different agent and pass some information to that agent.
- Use [conditional edges](#conditional-edges) to route between nodes conditionally without updating the state.
Check out this [how-to guide](/oss/langgraph/use-graph-api#combine-control-flow-and-state-updates-with-command) for an end-to-end example of how to use @[`Command`].

### Navigating to a node in a parent graph
#### `graph`

:::python
If you are using [subgraphs](/oss/langgraph/use-subgraphs), you might want to navigate from a node within a subgraph to a different subgraph (i.e. a different node in the parent graph). To do so, you can specify `graph=Command.PARENT` in @[`Command`]:
If you are using [subgraphs](/oss/langgraph/use-subgraphs), you can navigate from a node within a subgraph to a different node in the parent graph by specifying `graph=Command.PARENT` in @[`Command`]:

```python
def my_node(state: State) -> Command[Literal["other_subgraph"]]:
Expand All @@ -1169,15 +1156,14 @@ def my_node(state: State) -> Command[Literal["other_subgraph"]]:

Setting `graph` to `Command.PARENT` will navigate to the closest parent graph.


When you send updates from a subgraph node to a parent graph node for a key that's shared by both parent and subgraph [state schemas](#schema), you **must** define a [reducer](#reducers) for the key you're updating in the parent graph state. See this [example](/oss/langgraph/use-graph-api#navigate-to-a-node-in-a-parent-graph).

</Note>

:::

:::js
If you are using [subgraphs](/oss/langgraph/use-subgraphs), you might want to navigate from a node within a subgraph to a different subgraph (i.e. a different node in the parent graph). To do so, you can specify `graph: Command.PARENT` in `Command`:
If you are using [subgraphs](/oss/langgraph/use-subgraphs), you can navigate from a node within a subgraph to a different node in the parent graph by specifying `graph: Command.PARENT` in `Command`:

```typescript
import { Command } from "@langchain/langgraph";
Expand All @@ -1201,26 +1187,113 @@ When you send updates from a subgraph node to a parent graph node for a key that

:::

This is particularly useful when implementing [multi-agent handoffs](/oss/langchain/multi-agent/handoffs).
This is particularly useful when implementing [multi-agent handoffs](/oss/langchain/multi-agent/handoffs). Check out [this guide](/oss/langgraph/use-graph-api#navigate-to-a-node-in-a-parent-graph) for detail.

Check out [this guide](/oss/langgraph/use-graph-api#navigate-to-a-node-in-a-parent-graph) for detail.
### Input to `invoke`/`stream`

### Using inside tools
:::python

A common use case is updating graph state from inside a tool. For example, in a customer support application you might want to look up customer information based on their account number or ID in the beginning of the conversation.
<Warning>

Refer to [this guide](/oss/langgraph/use-graph-api#use-inside-tools) for detail.
`Command(resume=...)` is the **only** `Command` pattern intended as input to `invoke()`/`stream()`. Do not use `Command(update=...)` as input to continue multi-turn conversations — because passing any `Command` as input resumes from the latest checkpoint (i.e. the last step that ran, not `__start__`), the graph will appear stuck if it already finished. To continue a conversation on an existing thread, pass a plain input dict:

### Human-in-the-loop
```python
# WRONG — graph resumes from the latest checkpoint
# (last step that ran), appears stuck
graph.invoke(Command(update={ # [!code --]
"messages": [{"role": "user", "content": "follow up"}] # [!code --]
}), config) # [!code --]

# CORRECT — plain dict restarts from __start__
graph.invoke( { # [!code ++]
"messages": [{"role": "user", "content": "follow up"}] # [!code ++]
}, config) # [!code ++]
```

</Warning>

:::

:::js

<Warning>

`new Command({ resume: ... })` is the **only** `Command` pattern intended as input to `invoke()`/`stream()`. Do not use `new Command({ update: ... })` as input to continue multi-turn conversations — because passing any `Command` as input resumes from the latest checkpoint (i.e. the last step that ran, not `__start__`), the graph will appear stuck if it already finished. To continue a conversation on an existing thread, pass a plain input object:

```typescript
// WRONG — graph resumes from the latest checkpoint
// (last step that ran), appears stuck
await graph.invoke(new Command({ update: { messages: [{ role: "user", content: "follow up" }] } }), config); // [!code --]

// CORRECT — plain object restarts from __start__
await graph.invoke({ messages: [{ role: "user", content: "follow up" }] }, config); // [!code ++]
```

</Warning>

:::

#### `resume`

:::python
@[`Command`] is an important part of human-in-the-loop workflows: when using `interrupt()` to collect user input, @[`Command`] is then used to supply the input and resume execution via `Command(resume="User input")`. Check out [this conceptual guide](/oss/langgraph/interrupts) for more information.

Use `Command(resume=...)` to provide a value and resume graph execution after an [interrupt](/oss/langgraph/interrupts). The value passed to `resume` becomes the return value of the `interrupt()` call inside the paused node:

```python
from langgraph.types import Command, interrupt

def human_review(state: State):
# Pauses the graph and waits for a value
answer = interrupt("Do you approve?")
return {"messages": [{"role": "user", "content": answer}]}

# First invocation — hits the interrupt and pauses
result = graph.invoke({"messages": [...]}, config)

# Resume with a value — the interrupt() call returns "yes"
result = graph.invoke(Command(resume="yes"), config)
```

Check out the [interrupts conceptual guide](/oss/langgraph/interrupts) for full details on interrupt patterns, including multiple interrupts and validation loops.

:::

:::js
@[`Command`] is an important part of human-in-the-loop workflows: when using `interrupt()` to collect user input, @[`Command`] is then used to supply the input and resume execution via `new Command({ resume: "User input" })`. Check out the [human-in-the-loop conceptual guide](/oss/langgraph/interrupts) for more information.

Use `new Command({ resume: ... })` to provide a value and resume graph execution after an [interrupt](/oss/langgraph/interrupts). The value passed to `resume` becomes the return value of the `interrupt()` call inside the paused node:

```typescript
import { Command, interrupt } from "@langchain/langgraph";

const humanReview = async (state: typeof StateAnnotation.State) => {
// Pauses the graph and waits for a value
const answer = interrupt("Do you approve?");
return { messages: [{ role: "user", content: answer }] };
};

// First invocation — hits the interrupt and pauses
const result = await graph.invoke({ messages: [...] }, config);

// Resume with a value — the interrupt() call returns "yes"
const resumed = await graph.invoke(new Command({ resume: "yes" }), config);
```

Check out the [interrupts conceptual guide](/oss/langgraph/interrupts) for full details on interrupt patterns, including multiple interrupts and validation loops.

:::

### Return from tools

You can return @[`Command`] from tools to update graph state and control flow. Use `update` to modify state (e.g., saving customer information looked up during a conversation) and `goto` to route to a specific node after the tool completes.

<Warning>

When used inside tools, `goto` adds a dynamic edge — any static edges already defined on the node that called the tool will still execute.

</Warning>

Refer to [this guide](/oss/langgraph/use-graph-api#use-inside-tools) for detail.

## Graph migrations

LangGraph can easily handle migrations of graph definitions (nodes, edges, and state) even when using a checkpointer to track state.
Expand Down
14 changes: 14 additions & 0 deletions src/oss/langgraph/interrupts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ graph.invoke(Command(resume=True), config=config)
- The value passed to `Command(resume=...)` becomes the return value of the @[`interrupt`] call
- The node restarts from the beginning of the node where the @[`interrupt`] was called when resumed, so any code before the @[`interrupt`] runs again
- You can pass any JSON-serializable value as the resume value

<Warning>

`Command(resume=...)` is the **only** `Command` pattern intended as input to `invoke()`/`stream()`. The other `Command` parameters (`update`, `goto`, `graph`) are designed for [returning from node functions](/oss/langgraph/graph-api#command). Do not pass `Command(update=...)` as input to continue multi-turn conversations — pass a plain input dict instead.

</Warning>

:::

:::js
Expand Down Expand Up @@ -121,6 +128,13 @@ await graph.invoke(new Command({ resume: true }), config);
- The value passed to `new Command({ resume: ... })` becomes the return value of the @[`interrupt`] call
- The node restarts from the beginning of the node where the @[`interrupt`] was called when resumed, so any code before the @[`interrupt`] runs again
- You can pass any JSON-serializable value as the resume value

<Warning>

`new Command({ resume: ... })` is the **only** `Command` pattern intended as input to `invoke()`/`stream()`. The other `Command` parameters (`update`, `goto`, `graph`) are designed for [returning from node functions](/oss/langgraph/graph-api#command). Do not pass `new Command({ update: ... })` as input to continue multi-turn conversations — pass a plain input object instead.

</Warning>

:::

## Common patterns
Expand Down