Skip to content

HumanNode

nae.HumanNode

HumanNode(name: Optional[str] = None, node_prompt: str = 'Please review the following message and provide feedback.', n: int = 1, show: Optional[Union[str, Callable]] = None, into: str = 'messages', cache_ttl: Optional[int] = None, retry: bool = False)

Bases: Node

Resumable human-in-the-loop step, backed by LangGraph's interrupt().

When the run reaches a HumanNode it pauses and surfaces a payload for a person to review (by default the content of a recent message, chosen by n; override with show). The run resumes when you invoke again with Command(resume=value) on the same thread_id; the resume value lands into a state key — "messages" (default) wraps it as a HumanMessage and appends it, any other key writes the raw value (so a router can gate on the answer). Resume requires the graph to be built with a checkpointer.

Examples:

from nae import AgentNode, HumanNode, AgenticGraph
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command

draft = AgentNode(llm=llm, node_prompt="Draft a reply.")
review = HumanNode(node_prompt="Approve or edit this draft:", n=1)
finalize = AgentNode(llm=llm, node_prompt="Incorporate the human feedback.")

draft > review > finalize

graph = AgenticGraph(start_node=draft, end_nodes={finalize},
                     checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "demo-1"}}
graph.invoke("Reply to the customer.", config=config)   # pauses at review
graph.invoke(Command(resume="Looks good, ship it."), config=config)

Initialize a HumanNode — free-form human-in-the-loop via interrupt/resume.

Parameters:

Name Type Description Default
name Optional[str]

Unique identifier for the node.

None
node_prompt str

key under which show is surfaced in the interrupt payload.

'Please review the following message and provide feedback.'
n int

which prior message to surface for review when show is None (state["messages"][-n]; n<=0 shows nothing).

1
show Optional[Union[str, Callable]]

what to surface for review — None => the n-th message's content (default behavior); a str => that literal; a callable => show(state).

None
into str

where the human's free-form resume value lands — "messages" (default) wraps it as a HumanMessage and appends; any other key writes the raw value to that key (for a router to gate on).

'messages'
cache_ttl Optional[int]

see Node — node-level result caching.

None
retry bool

see Node — node-level retry on exception.

False

__call__

__call__(state)

Pause the run for human input and return the reply as a delta.

Computes the display payload — show(state) / the literal show / the n-th message's content (nothing when n <= 0 or too few messages) — then calls LangGraph's interrupt() with {node_prompt: display}. On the FIRST pass this raises the interrupt and suspends the run; when resumed via Command(resume=value), interrupt() returns that value, which is written into the target key ("messages" wraps it as a HumanMessage, otherwise the raw value is written).

Raises:

Type Description
ValueError

If state has no messages key.