Skip to content

feat(guardrails): add HITL escalation for decorator-style guardrails [AL-289]#894

Open
apetraru-uipath wants to merge 3 commits into
feat/langchain_guardrails_escalationsfrom
feat/langchain_guardrails_decorator_escalation
Open

feat(guardrails): add HITL escalation for decorator-style guardrails [AL-289]#894
apetraru-uipath wants to merge 3 commits into
feat/langchain_guardrails_escalationsfrom
feat/langchain_guardrails_decorator_escalation

Conversation

@apetraru-uipath

Copy link
Copy Markdown
Contributor

Stacked on #888 (feat/langchain_guardrails_escalations). Review/merge that first; this PR's base should be retargeted to main once #888 merges.

What changed?

Brings the HITL escalation capability added for middleware guardrails in #888 to the decorator path (@guardrail(action=EscalateAction(...))).

  • _langchain_adapter.py — adds a _run_action() helper that publishes the guardrail runtime context (scope / stage / component) on a ContextVar around each action call (tool/LLM/agent, PRE+POST), mirroring the middleware's _base.py. This lets EscalateAction derive Component / ExecutionStage at runtime instead of requiring them hardcoded. EscalateAction and _action_context are reused unchanged from feat: add EscalateAction HITL middleware action for guardrails [AL-289] #888.
  • No exception-handling change: the action call sites already catch only GuardrailBlockException, so a LangGraph GraphInterrupt raised by the escalation propagates naturally and the run suspends.
  • samples/joke-agent-decorator — adds an AGENT-scope EMAIL-PII EscalateAction on create_joke_agent (env-configurable app name/folder, same app as the middleware sample), plus README scenarios. Bumps the sample's uipath-langchain constraint to >=0.11.13, <0.12.0 to match the current SDK and the middleware sample.
  • Out of scope (follow-up): node / plain-function @guardrail escalation — those go through the core @guardrail in uipath-python, which doesn't publish context.

How has this been tested?

  • Unit: tests/guardrails/test_adapter_context_publishing.py — asserts the adapter publishes GuardrailActionContext (scope/stage/component) for tool/LLM/agent, resets it afterward, skips the action on PASS, and re-raises GraphInterrupt.
  • Integration: TestDecoratorEscalation in tests/cli/test_guardrails_in_langgraph.py — real adapter + EscalateAction + LangGraph interrupt/Command resume with MemorySaver: suspend payload (Component=Agent, ExecutionStage=PreExecution, JSON ToolInputs), approve substitutes the reviewed input, reject raises AgentRuntimeError. Payload matches the middleware flavor.
  • ruff check, ruff format --check, mypy all clean.
  • Live against Guardrail.Escalation.Action.App.2 (alpha / APetraruTenant): suspend creates the Action Center task with Component=Agent / ExecutionStage=PreExecution; approve resumes and completes; reject terminates with AgentRuntimeError: PII detected: Email.

Are there any breaking changes?

None.

…[AL-289]

Publish the guardrail runtime context (scope / stage / component) around each
action call in the LangChain adapter, mirroring the middleware path, so a
decorator @guardrail(action=EscalateAction(...)) can derive Component /
ExecutionStage at runtime instead of requiring them hardcoded. The action call
sites already let GraphInterrupt propagate (they catch only
GuardrailBlockException), so interrupt-based escalation suspends correctly with
no exception-handling change.

- _langchain_adapter.py: add _run_action() helper that publishes
  GuardrailActionContext for tool/llm/agent pre+post, reusing EscalateAction +
  _action_context from the middleware escalation (PR #888).
- joke-agent-decorator: add an AGENT-scope EMAIL-PII EscalateAction example on
  create_joke_agent, env-configurable app name/folder, plus README scenarios.
- tests: adapter context-publishing unit tests; decorator escalation
  integration test (suspend -> approve -> reject) alongside the middleware one.

Verified live against Guardrail.Escalation.Action.App.2: suspend (Component=Agent,
ExecutionStage=PreExecution), approve completes, reject terminates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 8, 2026 10:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds HITL escalation parity for decorator-style guardrails by publishing guardrail runtime context (scope/stage/component) around action invocations in the LangChain/LangGraph @guardrail adapter, enabling EscalateAction to derive Component and ExecutionStage at runtime (matching the middleware behavior introduced in #888).

Changes:

  • Add _run_action() helper in the decorator adapter to publish/reset GuardrailActionContext via ContextVar around each action call and to convert GuardrailBlockException into AgentRuntimeError while letting GraphInterrupt bubble up.
  • Add unit and integration coverage validating context publishing, reset semantics, PASS behavior (no action), and interrupt propagation/resume for decorator escalation.
  • Update the joke-agent-decorator sample to demonstrate AGENT-scope EMAIL PII escalation and bump its uipath-langchain dependency constraint to the current SDK range.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/uipath_langchain/guardrails/_langchain_adapter.py Publishes guardrail action context around decorator-path action calls via _run_action() for TOOL/LLM/AGENT scopes.
tests/guardrails/test_adapter_context_publishing.py New unit tests asserting context publication/reset and GraphInterrupt propagation for decorator adapter call sites.
tests/cli/test_guardrails_in_langgraph.py Adds decorator-path escalation E2E tests verifying interrupt → resume behavior and payload parity with middleware.
samples/joke-agent-decorator/graph.py Demonstrates agent-scope EscalateAction configured via env vars for the escalation action app.
samples/joke-agent-decorator/README.md Documents the new decorator-path escalation scenario and how to run/resume.
samples/joke-agent-decorator/pyproject.toml Updates the sample’s uipath-langchain version constraint to >=0.11.13,<0.12.0.

apetraru-uipath and others added 2 commits June 8, 2026 15:43
…-289]

Add a TestDecoratorEscalation case that resumes with Approve and no
ReviewedInputs: the run completes on the original input (the action returns
None, so the adapter leaves the message untouched) and does not re-suspend
despite the input still tripping the guardrail on replay.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an 'Escalation action (human-in-the-loop)' subsection to the decorator
pattern docs: same EscalateAction as middleware, on an agent/LLM/tool factory;
Component/ExecutionStage derived automatically from the inferred scope; recipient
example; with a note that node/plain-function targets (core @guardrail) still
escalate but don't populate the context fields.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants