Skip to content

Commit f35b337

Browse files
valentinabojanValentina Bojan
andauthored
fix: add correct guardrail validation info on the node state (#459)
Co-authored-by: Valentina Bojan <valentina.bojan@uipath.com>
1 parent 154df53 commit f35b337

9 files changed

Lines changed: 72 additions & 24 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-langchain"
3-
version = "0.4.23"
3+
version = "0.4.24"
44
description = "Python SDK that enables developers to build and deploy LangGraph agents to the UiPath Cloud Platform"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"

src/uipath_langchain/agent/guardrails/actions/escalate_action.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ async def _node(
8989
"GuardrailDescription": guardrail.description,
9090
"Component": _build_component_name(scope, guarded_component_name),
9191
"ExecutionStage": _execution_stage_to_string(execution_stage),
92-
"GuardrailResult": state.inner_state.guardrail_validation_result,
92+
"GuardrailResult": state.inner_state.guardrail_validation_details,
9393
}
9494

9595
# Add tenant and trace URL if base_url is configured

src/uipath_langchain/agent/guardrails/actions/log_action.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def action_node(
4949
async def _node(_state: AgentGuardrailsGraphState) -> dict[str, Any]:
5050
message = (
5151
self.message
52-
or f"Guardrail [{guardrail.name}] validation failed for [{scope.name}] [{execution_stage.name}] with the following reason: {_state.inner_state.guardrail_validation_result}"
52+
or f"Guardrail [{guardrail.name}] validation failed for [{scope.name}] [{execution_stage.name}] with the following reason: {_state.inner_state.guardrail_validation_details}"
5353
)
5454

5555
logger.log(self.level, message)

src/uipath_langchain/agent/guardrails/guardrail_nodes.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,21 @@ def _create_validation_command(
109109
return Command(
110110
goto=success_node,
111111
update={
112-
"inner_state": {"guardrail_validation_result": guardrail_result.reason}
112+
"inner_state": {
113+
"guardrail_validation_result": True,
114+
"guardrail_validation_details": guardrail_result.reason,
115+
}
113116
},
114117
)
115118

116119
if guardrail_result.result == GuardrailValidationResultType.VALIDATION_FAILED:
117120
return Command(
118121
goto=failure_node,
119122
update={
120-
"inner_state": {"guardrail_validation_result": guardrail_result.reason}
123+
"inner_state": {
124+
"guardrail_validation_result": False,
125+
"guardrail_validation_details": guardrail_result.reason,
126+
}
121127
},
122128
)
123129

src/uipath_langchain/agent/react/types.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class InnerAgentGraphState(BaseModel):
2828
class InnerAgentGuardrailsGraphState(InnerAgentGraphState):
2929
"""Extended inner state for guardrails subgraph."""
3030

31-
guardrail_validation_result: Optional[str] = None
31+
guardrail_validation_result: Optional[bool] = None
32+
guardrail_validation_details: Optional[str] = None
3233
agent_result: Optional[dict[str, Any]] = None
3334

3435

tests/agent/guardrails/actions/test_escalate_action.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ async def test_node_interrupts_with_correct_message_data(
160160
state = AgentGuardrailsGraphState(
161161
messages=[HumanMessage(content="Test message")],
162162
inner_state=InnerAgentGuardrailsGraphState(
163-
guardrail_validation_result="Validation failed"
163+
guardrail_validation_details="Validation failed"
164164
),
165165
)
166166
else:
@@ -171,7 +171,7 @@ async def test_node_interrupts_with_correct_message_data(
171171
HumanMessage(content="Output message"),
172172
],
173173
inner_state=InnerAgentGuardrailsGraphState(
174-
guardrail_validation_result="Validation failed"
174+
guardrail_validation_details="Validation failed"
175175
),
176176
)
177177

@@ -238,7 +238,7 @@ async def test_node_post_agent_interrupts_with_correct_agent_result_data(
238238
],
239239
inner_state=InnerAgentGuardrailsGraphState(
240240
agent_result={"ok": True},
241-
guardrail_validation_result="Validation failed",
241+
guardrail_validation_details="Validation failed",
242242
),
243243
)
244244

@@ -855,7 +855,7 @@ async def test_node_interrupts_with_correct_data_pre_tool(self, mock_interrupt):
855855
)
856856
state = AgentGuardrailsGraphState(
857857
messages=[ai_message],
858-
guardrail_validation_result="Validation failed",
858+
guardrail_validation_details="Validation failed",
859859
)
860860

861861
await node(state)

tests/agent/guardrails/actions/test_log_action.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ async def test_node_name_and_logs_custom_message(
3939
with caplog.at_level(logging.ERROR):
4040
result = await node(
4141
AgentGuardrailsGraphState(
42-
messages=[], guardrail_validation_result="ignored"
42+
messages=[],
43+
inner_state=InnerAgentGuardrailsGraphState(
44+
guardrail_validation_details="ignored"
45+
),
4346
)
4447
)
4548

@@ -88,7 +91,8 @@ async def test_default_message_includes_context(
8891
AgentGuardrailsGraphState(
8992
messages=[],
9093
inner_state=InnerAgentGuardrailsGraphState(
91-
guardrail_validation_result="bad input"
94+
guardrail_validation_result=False,
95+
guardrail_validation_details="bad input",
9296
),
9397
)
9498
)

tests/agent/guardrails/test_guardrail_nodes.py

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,10 @@ async def test_llm_success_pre_and_post(
106106
cmd = await node(state)
107107
assert cmd.goto == "ok"
108108
assert cmd.update == {
109-
"inner_state": {"guardrail_validation_result": "validation passed"}
109+
"inner_state": {
110+
"guardrail_validation_result": True,
111+
"guardrail_validation_details": "validation passed",
112+
}
110113
}
111114

112115
@pytest.mark.asyncio
@@ -142,7 +145,10 @@ async def test_llm_failure_pre_and_post(
142145
cmd = await node(state)
143146
assert cmd.goto == "nope"
144147
assert cmd.update == {
145-
"inner_state": {"guardrail_validation_result": "policy_violation"}
148+
"inner_state": {
149+
"guardrail_validation_result": False,
150+
"guardrail_validation_details": "policy_violation",
151+
}
146152
}
147153

148154

@@ -183,7 +189,10 @@ async def test_agent_init_success_pre_and_post(
183189
cmd = await node(state)
184190
assert cmd.goto == "ok"
185191
assert cmd.update == {
186-
"inner_state": {"guardrail_validation_result": "validation passed"}
192+
"inner_state": {
193+
"guardrail_validation_result": True,
194+
"guardrail_validation_details": "validation passed",
195+
}
187196
}
188197
assert fake.guardrails.last_text == "payload"
189198

@@ -223,7 +232,10 @@ async def test_agent_init_failure_pre_and_post(
223232
cmd = await node(state)
224233
assert cmd.goto == "nope"
225234
assert cmd.update == {
226-
"inner_state": {"guardrail_validation_result": "policy_violation"}
235+
"inner_state": {
236+
"guardrail_validation_result": False,
237+
"guardrail_validation_details": "policy_violation",
238+
}
227239
}
228240

229241

@@ -268,7 +280,10 @@ async def test_agent_terminate_success_pre_and_post(
268280
cmd = await node(state)
269281
assert cmd.goto == "ok"
270282
assert cmd.update == {
271-
"inner_state": {"guardrail_validation_result": "validation passed"}
283+
"inner_state": {
284+
"guardrail_validation_result": True,
285+
"guardrail_validation_details": "validation passed",
286+
}
272287
}
273288
assert fake.guardrails.last_text == str(agent_result)
274289

@@ -311,7 +326,10 @@ async def test_agent_terminate_failure_pre_and_post(
311326
cmd = await node(state)
312327
assert cmd.goto == "nope"
313328
assert cmd.update == {
314-
"inner_state": {"guardrail_validation_result": "policy_violation"}
329+
"inner_state": {
330+
"guardrail_validation_result": False,
331+
"guardrail_validation_details": "policy_violation",
332+
}
315333
}
316334

317335

@@ -360,15 +378,25 @@ async def test_tool_success_pre_and_post(
360378
)
361379
cmd = await node(state)
362380
assert cmd.goto == "ok"
363-
assert cmd.update == {"inner_state": {"guardrail_validation_result": ""}}
381+
assert cmd.update == {
382+
"inner_state": {
383+
"guardrail_validation_result": True,
384+
"guardrail_validation_details": "",
385+
}
386+
}
364387
assert json.loads(fake.guardrails.last_text or "{}") == {"x": 1}
365388
else:
366389
state = AgentGuardrailsGraphState(
367390
messages=[ToolMessage(content="tool output", tool_call_id="call_1")]
368391
)
369392
cmd = await node(state)
370393
assert cmd.goto == "ok"
371-
assert cmd.update == {"inner_state": {"guardrail_validation_result": ""}}
394+
assert cmd.update == {
395+
"inner_state": {
396+
"guardrail_validation_result": True,
397+
"guardrail_validation_details": "",
398+
}
399+
}
372400
assert fake.guardrails.last_text == "tool output"
373401

374402
@pytest.mark.asyncio
@@ -423,7 +451,10 @@ async def test_tool_failure_pre_and_post(
423451
cmd = await node(state)
424452
assert cmd.goto == "nope"
425453
assert cmd.update == {
426-
"inner_state": {"guardrail_validation_result": "policy_violation"}
454+
"inner_state": {
455+
"guardrail_validation_result": False,
456+
"guardrail_validation_details": "policy_violation",
457+
}
427458
}
428459

429460

@@ -552,7 +583,10 @@ def test_create_validation_command_success(self):
552583

553584
assert command.goto == "success_node"
554585
assert command.update == {
555-
"inner_state": {"guardrail_validation_result": "validation passed"}
586+
"inner_state": {
587+
"guardrail_validation_result": True,
588+
"guardrail_validation_details": "validation passed",
589+
}
556590
}
557591

558592
def test_create_validation_command_failure(self):
@@ -569,7 +603,10 @@ def test_create_validation_command_failure(self):
569603

570604
assert command.goto == "failure_node"
571605
assert command.update == {
572-
"inner_state": {"guardrail_validation_result": "policy_violation"}
606+
"inner_state": {
607+
"guardrail_validation_result": False,
608+
"guardrail_validation_details": "policy_violation",
609+
}
573610
}
574611

575612
def test_create_validation_command_feature_disabled_raises_exception(self):

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)