From 291379dfb907ee071b097f428c586765e64b3eaa Mon Sep 17 00:00:00 2001 From: Eugene Yurtsev Date: Wed, 4 Dec 2024 22:32:11 -0500 Subject: [PATCH] x --- .../how-tos/human_in_the_loop/interrupt.ipynb | 2 +- libs/langgraph/langgraph/types.py | 59 ++++++++++++------- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/docs/docs/how-tos/human_in_the_loop/interrupt.ipynb b/docs/docs/how-tos/human_in_the_loop/interrupt.ipynb index 043e60c6b..892be2c65 100644 --- a/docs/docs/how-tos/human_in_the_loop/interrupt.ipynb +++ b/docs/docs/how-tos/human_in_the_loop/interrupt.ipynb @@ -37,7 +37,7 @@ "\n", "Remember that graph execution always restarts at the beginning of the node. Be cautious of side effects, such as API calls that mutate data, as these may inadvertently be triggered multiple times.\n", "\n", - "When a node contains multiple interrupt calls, LangGraph maintains a list of resume values provided during graph execution. When resuming, execution always starts at the beginning of the node, and for each interrupt encountered, LangGraph checks whether a corresponding value exists in the list. Matching is strictly index-based, making the order of interrupt calls within the node critical. Users should avoid logic that dynamically removes, adds, or reorders interrupt calls between executions, as this can lead to mismatched indices. Such patterns often involve unconventional state mutations, such as altering state via `Command(resume=..., update=SOME_STATE_MUTATION)` or relying on global variables to modify the node's structure." + "When a node contains multiple interrupt calls, LangGraph maintains a list of resume values scoped to the specific task executing the node. When resuming, execution always starts at the beginning of the node, and for each interrupt encountered, LangGraph checks whether a corresponding value exists in the task's list. Matching is strictly index-based, making the order of interrupt calls within the node critical. Users should avoid logic that dynamically removes, adds, or reorders interrupt calls between executions, as this can lead to mismatched indices. Such patterns often involve unconventional state mutations, such as altering state via `Command(resume=..., update=SOME_STATE_MUTATION)` or relying on global variables to modify the node's structure." ] }, { diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py index 456196e44..3e3c729ba 100644 --- a/libs/langgraph/langgraph/types.py +++ b/libs/langgraph/langgraph/types.py @@ -330,38 +330,55 @@ class PregelScratchpad(TypedDict, total=False): def interrupt(value: Any) -> Any: """Interrupt the graph with a resumable exception from within a node. - The interrupt function is used for supporting human-in-the-loop workflows. - - It can be thought of as an equivalent to the python's built-in `input` function. + The `interrupt` function enables human-in-the-loop workflows by pausing graph + execution and surfacing a value to the client. This value can communicate context + or request input required to resume execution. In a given node, the first invocation of this function raises a `GraphInterrupt` - exception. The `value` argument is passed to the exception and can be used to - communicate information to the client executing the graph. - - The client can choose to resume the graph from the same node provide a value to - resume with. - + exception, halting execution. The provided `value` is included with the exception + and sent to the client executing the graph. - The client will use the `Command` primitive to - resume graph execution. + A client resuming the graph must use the `Command` primitive to specify a value + for the interrupt and continue execution. The graph resumes from the start of + the node, **re-executing** all logic. - graph.astream(Command(resume="answer 1", update={"my_key": "foofoo"}), config, stream_mode="updates") + If a node contains multiple `interrupt` calls, LangGraph matches resume values + to interrupts based on their order in the node. This list of resume values + is scoped to the specific task executing the node and is not shared across tasks. + To use an `interrupt`, you must enable a checkpointer, as the feature relies + on persisting the graph state. - The first invocation of this function raises a `GraphInterrupt` exception - - The first occurrence of this function in a node raises a `GraphInterrupt` - exception with the given value. + Examples: - A client executing the graph will receive the value and can choose to - resume the graph from the same node with a value. + ```python + async def some_node(state: State): + # Surface a value as part of the interrupt + question = {"question": "how old are you?"} + answer = interrupt(question) + # Continue execution with the provided answer + ``` + + A client resuming the graph: + ```python + for chunk in graph.astream( + Command(resume="25"), + config, + stream_mode="updates" + ): + print(chunk) + ``` Args: - value: The value to interrupt the graph with. + value: The value to surface to the client when the graph is interrupted. Returns: - On a first call, raises a `GraphInterrupt` exception with the given value. - On subsequent calls from the same node, returns the value to resume with. + On subsequent invocations within the same node (same task to be precise), + returns the value provided during the first invocation + + Raises: + GraphInterrupt: On the first invocation within the node, halts execution + and surfaces the provided value to the client. """ from langgraph.constants import ( CONFIG_KEY_CHECKPOINT_NS,