HumanReview Config
Consolidate all human-in-the-loop settings into a single config object.
HumanReview groups all HITL settings into a single object instead of passing them as separate parameters. Step, Loop, Router, Condition, and Steps accept human_review=HumanReview(...). Parallel rejects every requires_* field because parallel branches cannot be individually paused. Behavior fields set on Parallel have no effect.
from agno.workflow.step import Step
from agno.workflow.types import HumanReview
from agno.workflow import OnReject, OnTimeout
Step(
name="draft_email",
agent=draft_agent,
human_review=HumanReview(
requires_output_review=True,
output_review_message="Review the draft before sending.",
on_reject=OnReject.retry,
max_retries=3,
timeout=300,
on_timeout=OnTimeout.approve,
),
)Workflow primitives accept HITL settings only through human_review. Move former flat fields into HumanReview, renaming hitl_timeout to timeout and hitl_max_retries to max_retries. Step(max_retries=...) still controls executor-error retries separately.
All Fields
| Field | Type | Default | Description |
|---|---|---|---|
requires_confirmation | bool | False | Pause for user confirmation before execution |
confirmation_message | str | None | Message shown during confirmation |
requires_user_input | bool | False | Pause to collect user input before execution |
user_input_message | str | None | Message shown when collecting input |
allow_multiple_selections | bool | False | Let a Router accept more than one selected route |
user_input_schema | List[UserInputField] | None | Schema defining expected input fields |
requires_output_review | bool or Callable[[StepOutput], bool] | False | Pause after execution for output review |
output_review_message | str | None | Message shown during output review |
requires_iteration_review | bool | False | Pause after each loop iteration for review |
iteration_review_message | str | None | Message shown during iteration review |
on_reject | OnReject | OnReject.skip | Action when user rejects |
on_error | OnError | OnError.skip | Action when a Step or Condition errors |
max_retries | int | 3 | Max retries on rejection (when on_reject=OnReject.retry) |
timeout | int | None | Seconds to wait for a Step confirmation or output review |
on_timeout | OnTimeout | OnTimeout.cancel | Action when timeout expires |
Supported Fields by Component
Not every field works on every component. Passing an unsupported requires_* flag raises a ValueError at construction time. The behavior fields (on_reject, on_error, max_retries, timeout, on_timeout) are not validated; they only take effect on the components marked below.
| Field | Step | Loop | Router | Condition | Steps | Parallel |
|---|---|---|---|---|---|---|
requires_confirmation | ✓ | ✓ | ✓ | ✓ | ✓ | - |
requires_user_input | ✓ | - | ✓ | - | - | - |
requires_output_review | ✓ | - | ✓ | - | - | - |
requires_iteration_review | - | ✓ | - | - | - | - |
allow_multiple_selections | - | - | ✓ | - | - | - |
on_reject | ✓ | ✓ | ✓ | ✓ | ✓ | - |
on_error | ✓ | - | - | ✓ | - | - |
max_retries | ✓ | - | ✓ | - | - | - |
timeout | ✓ | - | - | - | - | - |
on_timeout | ✓ | - | - | - | - | - |
Condition and Steps only support requires_confirmation. Parallel rejects any requires_* field. Passing an unsupported mode raises a clear error:
from agno.workflow.condition import Condition
from agno.workflow.types import HumanReview
# This raises ValueError: requires_output_review is not supported on Condition.
# Supported: requires_confirmation.
Condition(
name="my_condition",
steps=[...],
human_review=HumanReview(requires_output_review=True),
)Validation Rules
requires_output_reviewandrequires_iteration_reviewcannot both beTruein the same config.- Each component validates at construction time. You get a clear error if a field is unsupported.
HITL Modes
Pre-execution: Confirmation
Pause before a step runs. The user approves or rejects.
HumanReview(
requires_confirmation=True,
confirmation_message="Delete 1000 records?",
on_reject=OnReject.cancel,
)Pre-execution: User Input
Collect parameters from the user before execution.
from agno.workflow.types import HumanReview, UserInputField
HumanReview(
requires_user_input=True,
user_input_message="Configure report settings:",
user_input_schema=[
UserInputField(name="format", field_type="str", required=True),
UserInputField(name="include_charts", field_type="bool", required=False),
],
)Post-execution: Output Review
Pause after a step completes so the user can review the output. Supported on Step and Router.
HumanReview(
requires_output_review=True,
output_review_message="Review the generated report.",
on_reject=OnReject.retry,
max_retries=3,
)Per-iteration: Iteration Review
Pause after each loop iteration for review. Supported on Loop only.
HumanReview(
requires_iteration_review=True,
iteration_review_message="Review this iteration's output.",
on_reject=OnReject.retry,
)Timeout
Set a timeout for a Step confirmation or output review. If the timeout expires, on_timeout determines what happens when continue_run() is called. Step user-input pauses and other components ignore timeout policies.
from agno.workflow import OnTimeout
HumanReview(
requires_confirmation=True,
confirmation_message="Approve deployment?",
timeout=300, # 5 minutes
on_timeout=OnTimeout.approve, # Auto-approve after timeout
)OnTimeout Value | Behavior |
|---|---|
OnTimeout.approve | Automatically approve and continue |
OnTimeout.skip | Skip the step and continue |
OnTimeout.cancel | Cancel the workflow (default) |
Serialization
Workflow primitives serialize HumanReview under a nested "human_review" key. Current deserialization drops legacy flat HITL keys with a warning; it does not restore their approval gates. Migrate persisted configurations to nested human_review before rehydrating them. For example, move requires_confirmation into that object and rename legacy hitl_timeout / hitl_max_retries to timeout / max_retries inside it.
config = HumanReview(
requires_confirmation=True,
confirmation_message="Proceed?",
timeout=60,
)
data = config.to_dict()
# {"requires_confirmation": True, "confirmation_message": "Proceed?", "timeout": 60, ...}
restored = HumanReview.from_dict(data)A callable requires_output_review predicate serializes as True. Reapply the callable after reconstructing a workflow if review should remain conditional.