Checked other resources
Example Code
from typing import TypedDict, Optional
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
class State(TypedDict):
"""The graph state from the example using interrupt()"""
state_counter: int
human_response: Optional[str]
counter_node_in_subgraph = 0
counter_human_node = 0
def node_in_subgraph(state: State):
"""A node in the sub-graph."""
global counter_node_in_subgraph
counter_node_in_subgraph += 1
print(f"SUBGRAPH: Entered `node_in_subgraph` a total of {counter_node_in_subgraph} times")
answer = state.get("human_response","None")
print(f"SUBGRAPH: `node_in_subgraph` has human reponse: '{answer}'")
return {}
def human_node(state: State):
"""This node would process human input if it ran."""
global counter_human_node
counter_human_node += 1
print(f"SUBGRAPH: Entered `human_node` in sub-graph a total of {counter_human_node} times")
answer = state.get("human_response")
print(f"SUBGRAPH: `human_node` got an answer of '{answer}' from state.")
return {}
checkpointer = MemorySaver()
subgraph_builder = StateGraph(State)
subgraph_builder.add_node("some_node", node_in_subgraph)
subgraph_builder.add_node("human_node", human_node)
subgraph_builder.add_edge(START, "some_node")
subgraph_builder.add_edge("some_node", "human_node")
subgraph_builder.add_edge("human_node", END)
subgraph = subgraph_builder.compile(
checkpointer=checkpointer,
interrupt_before=["human_node"]
)
counter_parent_node = 0
def parent_node(state: State, config: dict): # Added config
"""This parent node will invoke the subgraph."""
global counter_parent_node
counter_parent_node += 1
print(f"PARENT: Entered `parent_node` a total of {counter_parent_node} times")
print(f" PARENT: state before subgraph.invoke: {state}")
subgraph_output_state = subgraph.invoke(state, config=config)
print(f" PARENT: subgraph_output_state: {subgraph_output_state}")
# The parent node returns the full state output from the subgraph.
return subgraph_output_state
builder = StateGraph(State)
builder.add_node("parent_node", parent_node)
builder.add_edge(START, "parent_node")
builder.add_edge("parent_node", END)
graph = builder.compile(checkpointer=checkpointer)
print("Main graph compiled.")
config = {
"configurable": {
"thread_id": "1",
}
}
# Initial run - should interrupt before subgraph's 'human_node'
print("\n--- Initial Run (expecting interrupt) ---")
initial_input = State(state_counter=1, human_response=None) # type: ignore
for chunk in graph.stream(initial_input, config, subgraphs=True):
print(f"CHUNK Initial: {chunk}")
print(f"\n--- Graph interrupted. Current state (Main Graph): {graph.get_state(config).values} ---")
print(f"--- Next node in Main Graph: {graph.get_state(config).next} ---")
print(f"--- Next node in Sub Graph: {graph.get_state(config, subgraphs=True).tasks[0].state.next} ---")
print('\n--- Resuming ---')
hil_response_value = "35" # The input the human provides
# Update state with the HIL response
graph.update_state(
config,
State(state_counter=graph.get_state(config).values["state_counter"],
human_response=hil_response_value) # type: ignore
)
print(f"State updated with human_response: {hil_response_value}")
print(f" Current state (Main Graph) before resume stream: {graph.get_state(config).values}")
# Resume by streaming with None as input
for chunk in graph.stream(None, config, subgraphs=True):
print(f"CHUNK Resumed: {chunk}")
print(f"\n--- Final State (Main Graph): {graph.get_state(config).values} ---")
print(f"--- Final counter_node_in_subgraph: {counter_node_in_subgraph} ---")
print(f"--- Final counter_human_node: {counter_human_node} ---")
print(f"--- Final counter_parent_node: {counter_parent_node} ---")
Error Message and Stack Trace (if applicable)
Main graph compiled.
--- Initial Run (expecting interrupt) ---
PARENT: Entered `parent_node` a total of 1 times
PARENT: state before subgraph.invoke: {'state_counter': 1, 'human_response': None}
SUBGRAPH: Entered `node_in_subgraph` a total of 1 times
SUBGRAPH: `node_in_subgraph` has human reponse: 'None'
CHUNK Initial: (('parent_node:34b0ebce-0f4f-349e-2e37-3278639278b7',), {'some_node': None})
CHUNK Initial: ((), {'__interrupt__': ()})
--- Graph interrupted. Current state (Main Graph): {'state_counter': 1, 'human_response': None} ---
--- Next node in Main Graph: ('parent_node',) ---
--- Next node in Sub Graph: ('human_node',) ---
--- Resuming ---
State updated with human_response: 35
Current state (Main Graph) before resume stream: {'state_counter': 1, 'human_response': '35'}
PARENT: Entered `parent_node` a total of 2 times
PARENT: state before subgraph.invoke: {'state_counter': 1, 'human_response': '35'}
SUBGRAPH: Entered `node_in_subgraph` a total of 2 times
SUBGRAPH: `node_in_subgraph` has human reponse: '35'
CHUNK Resumed: (('parent_node:e104621c-052e-bf14-5fa0-e956f82d4312',), {'some_node': None})
CHUNK Resumed: ((), {'__interrupt__': ()})
--- Final State (Main Graph): {'state_counter': 1, 'human_response': '35'} ---
--- Final counter_node_in_subgraph: 2 ---
--- Final counter_human_node: 0 ---
--- Final counter_parent_node: 2 ---
Description
I have recreated the example from the docs (https://langchain-ai.github.io/langgraph/how-tos/human_in_the_loop/add-human-in-the-loop/#subgraphs-called-as-functions).
But instead of using interrupt and Command to resume, i am using interrupt_before that i define at the subgraph compilation.
The expected behavior, based on documentation for nested checkpoints and interruptible subgraphs, is that if the subgraph interrupts (e.g., before its human_node), the parent graph's calling node (parent_node) will re-execute upon resuming the main graph. When this re-executing parent_node calls the subgraph again, the subgraph should use its own checkpoint to resume from its internal breakpoint (i.e., start executing its human_node).
However, as you can see in the output, the subgraph is restarting from the entry point node, despite its checkpoint correctly indicating next=('human_node',).
This leads to some_node executing twice and human_node (the HIL target) never executing, effectively breaking the HIL flow within the subgraph.
System Info
System Information
OS: Darwin
OS Version: Darwin Kernel Version 23.6.0: Fri Jul 5 17:55:37 PDT 2024; root:xnu-10063.141.1~2/RELEASE_ARM64_T6030
Python Version: 3.12.2 (main, Mar 7 2024, 22:00:34) [Clang 15.0.0 (clang-1500.1.0.2.5)]
Package Information
langchain_core: 0.3.47
langchain: 0.3.20
langchain_community: 0.3.19
langsmith: 0.3.13
langchain_experimental: 0.3.4
langchain_openai: 0.3.9
langchain_text_splitters: 0.3.7
langgraph_api: 0.2.27
langgraph_cli: 0.2.10
langgraph_license: Installed. No version info available.
langgraph_runtime: Installed. No version info available.
langgraph_runtime_inmem: 0.0.11
langgraph_sdk: 0.1.69
langgraph_supervisor: 0.0.20
Optional packages not installed
langserve
Other Dependencies
aiohttp<4.0.0,>=3.8.3: Installed. No version info available.
async-timeout<5.0.0,>=4.0.0;: Installed. No version info available.
blockbuster: 1.5.24
click: 8.1.8
cloudpickle: 3.1.1
cryptography: 43.0.3
dataclasses-json<0.7,>=0.5.7: Installed. No version info available.
httpx: 0.28.1
httpx-sse<1.0.0,>=0.4.0: Installed. No version info available.
jsonpatch<2.0,>=1.33: Installed. No version info available.
jsonschema-rs: 0.29.1
langchain-anthropic;: Installed. No version info available.
langchain-aws;: Installed. No version info available.
langchain-cohere;: Installed. No version info available.
langchain-community;: Installed. No version info available.
langchain-core<0.4.0,>=0.3.40: Installed. No version info available.
langchain-core<1.0.0,>=0.3.41: Installed. No version info available.
langchain-core<1.0.0,>=0.3.45: Installed. No version info available.
langchain-deepseek;: Installed. No version info available.
langchain-fireworks;: Installed. No version info available.
langchain-google-genai;: Installed. No version info available.
langchain-google-vertexai;: Installed. No version info available.
langchain-groq;: Installed. No version info available.
langchain-huggingface;: Installed. No version info available.
langchain-mistralai;: Installed. No version info available.
langchain-ollama;: Installed. No version info available.
langchain-openai;: Installed. No version info available.
langchain-text-splitters<1.0.0,>=0.3.6: Installed. No version info available.
langchain-together;: Installed. No version info available.
langchain-xai;: Installed. No version info available.
langchain<1.0.0,>=0.3.20: Installed. No version info available.
langgraph: 0.4.5
langgraph-checkpoint: 2.0.26
langgraph-prebuilt<0.2.0,>=0.1.7: Installed. No version info available.
langgraph>=0.3.5: Installed. No version info available.
langsmith-pyo3: Installed. No version info available.
langsmith<0.4,>=0.1.125: Installed. No version info available.
langsmith<0.4,>=0.1.17: Installed. No version info available.
numpy<3,>=1.26.2: Installed. No version info available.
openai<2.0.0,>=1.66.3: Installed. No version info available.
orjson: 3.10.15
packaging: 24.2
packaging<25,>=23.2: Installed. No version info available.
pydantic: 2.10.6
pydantic-settings<3.0.0,>=2.4.0: Installed. No version info available.
pydantic<3.0.0,>=2.5.2;: Installed. No version info available.
pydantic<3.0.0,>=2.7.4: Installed. No version info available.
pydantic<3.0.0,>=2.7.4;: Installed. No version info available.
pyjwt: 2.10.1
pytest: Installed. No version info available.
python-dotenv: 1.0.1
PyYAML>=5.3: Installed. No version info available.
requests: 2.32.3
requests-toolbelt: 1.0.0
requests<3,>=2: Installed. No version info available.
rich: Installed. No version info available.
SQLAlchemy<3,>=1.4: Installed. No version info available.
sse-starlette: 2.1.3
starlette: 0.41.3
structlog: 25.2.0
tenacity: 8.5.0
tenacity!=8.4.0,<10,>=8.1.0: Installed. No version info available.
tenacity!=8.4.0,<10.0.0,>=8.1.0: Installed. No version info available.
tiktoken<1,>=0.7: Installed. No version info available.
truststore: 0.10.1
typing-extensions>=4.7: Installed. No version info available.
watchfiles: 0.20.0
zstandard: 0.23.0
Checked other resources
Example Code
Error Message and Stack Trace (if applicable)
Description
I have recreated the example from the docs (https://langchain-ai.github.io/langgraph/how-tos/human_in_the_loop/add-human-in-the-loop/#subgraphs-called-as-functions).
But instead of using interrupt and Command to resume, i am using interrupt_before that i define at the subgraph compilation.
The expected behavior, based on documentation for nested checkpoints and interruptible subgraphs, is that if the subgraph interrupts (e.g., before its human_node), the parent graph's calling node (parent_node) will re-execute upon resuming the main graph. When this re-executing parent_node calls the subgraph again, the subgraph should use its own checkpoint to resume from its internal breakpoint (i.e., start executing its human_node).
However, as you can see in the output, the subgraph is restarting from the entry point node, despite its checkpoint correctly indicating next=('human_node',).
This leads to some_node executing twice and human_node (the HIL target) never executing, effectively breaking the HIL flow within the subgraph.
System Info
System Information
Package Information
Optional packages not installed
Other Dependencies