contributing/workflow_samples/request_input_rerun/README.md
This sample demonstrates an alternative way to handle a Human-in-the-Loop workflow in ADK Workflows using the RequestInput event combined with the @node(rerun_on_resume=True) decorator.
Like the standard request_input sample, this workflow simulates a customer support scenario where an AI drafts an email and a human reviews it. The key difference lies in how the human input is processed when the workflow resumes.
request_input vs request_input_rerunrequest_input): The workflow pauses after a node yields RequestInput. When the user provides input and execution resumes, the input is automatically passed as the argument to the next node in the edge definition. This requires two separate nodes: one to request the input and one to handle it.request_input_rerun): The node yielding RequestInput is decorated with @node(rerun_on_resume=True). When execution resumes, the workflow re-runs the exact same node that asked for the input. The node can then access the provided input via the execution Context.This allows you to combine the requesting and handling of human input into a single, cohesive node.
The delivery was a week lateI received the wrong itemMy account was charged twice [ START ]
|
v
[draft_email] <------------------+
| |
v |
[human_review] -- (revise) ------+
(reruns on resume)
/ \
/ \
v v
[send_email] [END (rejected)]
Decorate the node that needs human input with @node(rerun_on_resume=True). Ensure the function signature includes the workflow Context.
from google.adk.workflow import node
from google.adk import Context
@node(rerun_on_resume=True)
def human_review(draft: str, ctx: Context):
# ...
Inside the node, check if you are being resumed by looking for the interrupt_id in ctx.resume_inputs.
resume_input = ctx.resume_inputs.get('human_review')
If resume_input is missing (i.e., this is the first time the node is executing), yield the RequestInput event to pause the workflow. Include an explicit interrupt_id.
if not resume_input:
yield RequestInput(
interrupt_id="human_review",
message="Please review the draft...",
)
return # Important: Stop execution of this node for now
If resume_input is present (i.e., the workflow was resumed with user input), process the input and yield the appropriate routing events.
if resume_input == "reject":
yield Event(route="rejected")
elif resume_input == "approve":
yield Event(route="approved")
else:
yield Event(state={"feedback": resume_input}, route="revise")
The edge definition is much simpler because the single human_review node handles everything:
Workflow(
name="request_input",
edges=[
("START", process_input, draft_email, human_review),
(human_review, {"revise": draft_email, "approved": send_email}),
],
)