* Adding support for mapping interrupt ids -> resume values with the
`Command.resume_map` argument, like:
```py
resume_map = {
i.interrupt_id: f"human input for prompt {i.value}"
for i in parent_graph.get_state(thread_config).interrupts
}
parent_graph.invoke(Command(resume=resume_map), config=thread_config)
```
* Adds an `interrupts` attribute on `StateSnapshot` so that we can
access that directly rather than having to do
`get_state(thread_config).tasks` and then iterate over tasks to find
interrupts
* Deprecates undocumented feature where (if interrupting a graph from
the level of an interrupt), you could pass a dict mapping task ids ->
resume values. Now we recommend and endorse the `interrupt_id` approach
above.
I'll note, from an internal perspective, I would love if we didn't have
to pass around this map, but it seems like the best way right now to
make the necessary resume information necessary at different levels in a
graph with subgraphs.
Fix https://github.com/langchain-ai/langgraph/issues/4028
Slotted to be included in our v0.4.0 release early next week!
A few notes:
* We shouldn't be using `tool.poetry.dependencies`, that's deprecated -
waiting for a future PR to address this big change though.
* We should remove upper bounds for all deps unless strictly necessary.
We want to deprecate `TavilySearchResults` in langchain-community in
favor of `TavilySearch` in langchain-tavily.
Also update quickstart to use `init_chat_model`.
This PR does a few things:
1. Surfaces interrupts when `stream_mode='values'` (particularly
relevant for `invoke`, where this is the default behavior)
2. Adds an `interrupt_id` property to the `Interrupt` dataclass so that
interrupts can effectively be mapped to resumes
3. Minor docs updates to reflect the new pattern (no need for a special
section on interrupts with `invoke` and `ainvoke`)
* In a different PR (the one with the multiple resume values), as it's
more relevant there: add an `interrupts` property to `StateSnapshot` so
that `interrupts` can easily be iterated over if users are attempting to
map interrupts to resumes.
I **don't** recommend we release this until we have multi-resumes
working.
## Example
We have the following setup where we're sending multiple prompts to the
child graph, which uses `interrupt`:
```py
def child_graph(state):
human_input = interrupt(state["prompt"])
return {
"human_inputs": [human_input],
}
```
<img width="142" alt="Screenshot 2025-04-23 at 10 01 12 AM"
src="https://github.com/user-attachments/assets/c6238bf1-54ad-4e48-ab0b-60a0bfc18485"
/>
Old behavior:
```py
initial_input = {"prompts": ["a", "b"]}
print(parent_graph.invoke(input=initial_input,config=thread_config,stream_mode="values"))
#> {'prompts': ['a', 'b'], 'human_inputs': []}
print(parent_graph.invoke(Command(resume="hello 1"),config=thread_config,stream_mode="values"))
#> {'prompts': ['a', 'b'], 'human_inputs': ['hello 1']}
print(parent_graph.invoke(Command(resume="hello 2"),config=thread_config,stream_mode="values"))
#> {'prompts': ['a', 'b'], 'human_inputs': ['hello 1', 'hello 2']}
```
New behavior:
```py
initial_input = {"prompts": ["a", "b"]}
print(parent_graph.invoke(input=initial_input,config=thread_config,stream_mode="values"))
"""
{
"prompts": ["a", "b"],
"human_inputs": [],
"__interrupt__": [
Interrupt(
value="a",
resumable=True,
ns=["child_graph:38d43a18-a5e7-8ab2-ca83-9d80f6e9ca83"]
),
Interrupt(
value="b",
resumable=True,
ns=["child_graph:dad810e8-738e-9f90-41cd-30c0091eb79b"]
)
]
}
"""
print(parent_graph.invoke(Command(resume="hello 1"),config=thread_config,stream_mode="values"))
"""
{
"prompts": ["a", "b"],
"human_inputs": ["hello 1"],
"__interrupt__": [
Interrupt(
value="b",
resumable=True,
ns=["child_graph:dad810e8-738e-9f90-41cd-30c0091eb79b"]
)
]
}
"""
print(parent_graph.invoke(Command(resume="hello 2"),config=thread_config,stream_mode="values"))
#> {'prompts': ['a', 'b'], 'human_inputs': ['hello 1', 'hello 2']}
```