Skip to content

Commit f902e5c

Browse files
refactor(guardrails): take a termination mode instead of the guardrail action [AL-614]
evaluate_guardrail takes termination_mode (GuardrailTerminationMode.FAIL_FAST or EVALUATE_ALL), the same concept bulk validate uses, instead of the guardrail's action. FAIL_FAST makes the service stop scanning at the first violation; without a mode the request body is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent b33d380 commit f902e5c

5 files changed

Lines changed: 38 additions & 17 deletions

File tree

‎packages/uipath-platform/src/uipath/platform/guardrails/__init__.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
BuiltInValidatorGuardrail,
4949
EnumListParameterValue,
5050
GuardrailAttachment,
51+
GuardrailTerminationMode,
5152
GuardrailType,
5253
MapEnumParameterValue,
5354
)
@@ -58,6 +59,7 @@
5859
# Guardrail models
5960
"BYO_VALIDATOR_TYPE",
6061
"GuardrailAttachment",
62+
"GuardrailTerminationMode",
6163
"BuiltInValidatorGuardrail",
6264
"GuardrailType",
6365
"GuardrailValidationResultType",

‎packages/uipath-platform/src/uipath/platform/guardrails/_guardrails_service.py‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
BYO_VALIDATOR_TYPE,
2222
BuiltInValidatorGuardrail,
2323
GuardrailAttachment,
24+
GuardrailTerminationMode,
2425
)
2526

2627
_ATTACHMENT_VALIDATE_TIMEOUT_SECONDS = 60.0
@@ -111,17 +112,16 @@ def evaluate_guardrail(
111112
guardrail: BuiltInValidatorGuardrail,
112113
*,
113114
attachments: list[GuardrailAttachment] | None = None,
114-
action: str | None = None,
115+
termination_mode: GuardrailTerminationMode | None = None,
115116
) -> GuardrailValidationResult:
116117
"""Validate input text using the provided guardrail.
117118
118119
Args:
119120
input_data: The text or structured data to validate. Dictionaries will be converted to a string before validation.
120121
guardrail: A guardrail instance used for validation.
121122
attachments: Files attached to the run that the guardrail may inspect.
122-
action: The guardrail's action in the agent (``block``, ``log``,
123-
``escalate``, ``filter``). For ``block`` and ``log`` the service stops
124-
scanning at the first violation.
123+
termination_mode: ``FAIL_FAST`` stops scanning at the first violation;
124+
``EVALUATE_ALL`` or ``None`` scans everything.
125125
126126
Returns:
127127
GuardrailValidationResult: The outcome of the guardrail evaluation.
@@ -143,8 +143,8 @@ def evaluate_guardrail(
143143
payload["byoValidatorName"] = guardrail.byo_validator_name
144144
if attachments:
145145
payload["attachments"] = [a.model_dump(by_alias=True) for a in attachments]
146-
if action:
147-
payload["action"] = action
146+
if termination_mode is not None:
147+
payload["terminationMode"] = termination_mode.value
148148
spec = RequestSpec(
149149
method="POST",
150150
endpoint=Endpoint("/agentsruntime_/api/execution/guardrails/validate"),

‎packages/uipath-platform/src/uipath/platform/guardrails/guardrails.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ class GuardrailAttachment(BaseModel):
112112
model_config = ConfigDict(populate_by_name=True)
113113

114114

115+
class GuardrailTerminationMode(str, Enum):
116+
"""When a guardrail evaluation stops scanning.
117+
118+
``FAIL_FAST`` stops at the first violation; ``EVALUATE_ALL`` scans everything.
119+
"""
120+
121+
FAIL_FAST = "FailFast"
122+
EVALUATE_ALL = "EvaluateAll"
123+
124+
115125
class GuardrailType(str, Enum):
116126
"""Guardrail type enumeration."""
117127

‎packages/uipath-platform/tests/services/test_guardrails_service.py‎

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
EnumListParameterValue,
1717
GuardrailAttachment,
1818
GuardrailsService,
19+
GuardrailTerminationMode,
1920
MapEnumParameterValue,
2021
)
2122

@@ -1007,36 +1008,44 @@ def test_attachment_round_trips_the_wire_shape(self) -> None:
10071008
assert parsed.model_dump(by_alias=True) == wire
10081009

10091010

1010-
class TestGuardrailAction:
1011-
"""evaluate_guardrail forwards the guardrail's action to the validate API."""
1011+
class TestGuardrailTerminationMode:
1012+
"""evaluate_guardrail forwards the termination mode to the validate API."""
10121013

1013-
def test_action_is_sent_when_supplied(
1014+
@pytest.mark.parametrize(
1015+
("mode", "wire"),
1016+
[
1017+
(GuardrailTerminationMode.FAIL_FAST, "FailFast"),
1018+
(GuardrailTerminationMode.EVALUATE_ALL, "EvaluateAll"),
1019+
],
1020+
)
1021+
def test_termination_mode_is_sent_when_supplied(
10141022
self,
10151023
httpx_mock: HTTPXMock,
10161024
service: GuardrailsService,
10171025
base_url: str,
10181026
org: str,
10191027
tenant: str,
1028+
mode: GuardrailTerminationMode,
1029+
wire: str,
10201030
) -> None:
10211031
httpx_mock.add_response(
10221032
url=f"{base_url}{org}{tenant}{_VALIDATE_PATH}",
10231033
status_code=200,
10241034
json={"result": "PASSED", "details": ""},
10251035
)
10261036

1027-
service.evaluate_guardrail("x", _judge_guardrail(), action="block")
1037+
service.evaluate_guardrail("x", _judge_guardrail(), termination_mode=mode)
10281038

1029-
assert json.loads(httpx_mock.get_requests()[0].content)["action"] == "block"
1039+
body = json.loads(httpx_mock.get_requests()[0].content)
1040+
assert body["terminationMode"] == wire
10301041

1031-
@pytest.mark.parametrize("action", [None, ""])
1032-
def test_action_key_is_absent_when_not_supplied(
1042+
def test_termination_mode_key_is_absent_when_not_supplied(
10331043
self,
10341044
httpx_mock: HTTPXMock,
10351045
service: GuardrailsService,
10361046
base_url: str,
10371047
org: str,
10381048
tenant: str,
1039-
action: str | None,
10401049
) -> None:
10411050
"""An older backend must see a byte-identical body to today."""
10421051
httpx_mock.add_response(
@@ -1045,9 +1054,9 @@ def test_action_key_is_absent_when_not_supplied(
10451054
json={"result": "PASSED", "details": ""},
10461055
)
10471056

1048-
service.evaluate_guardrail("x", _judge_guardrail(), action=action)
1057+
service.evaluate_guardrail("x", _judge_guardrail())
10491058

1050-
assert "action" not in json.loads(httpx_mock.get_requests()[0].content)
1059+
assert "terminationMode" not in json.loads(httpx_mock.get_requests()[0].content)
10511060

10521061

10531062
class TestGuardrailAttachmentFolderHeader:

‎packages/uipath/src/uipath/_resources/SDK_REFERENCE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ Guardrails service
668668

669669
```python
670670
# Validate input text using the provided guardrail.
671-
sdk.guardrails.evaluate_guardrail(input_data: str | dict[str, Any], guardrail: uipath.platform.guardrails.guardrails.BuiltInValidatorGuardrail, attachments: list[uipath.platform.guardrails.guardrails.GuardrailAttachment] | None=None, action: str | None=None) -> uipath.core.guardrails.guardrails.GuardrailValidationResult
671+
sdk.guardrails.evaluate_guardrail(input_data: str | dict[str, Any], guardrail: uipath.platform.guardrails.guardrails.BuiltInValidatorGuardrail, attachments: list[uipath.platform.guardrails.guardrails.GuardrailAttachment] | None=None, termination_mode: uipath.platform.guardrails.guardrails.GuardrailTerminationMode | None=None) -> uipath.core.guardrails.guardrails.GuardrailValidationResult
672672

673673
```
674674

0 commit comments

Comments
 (0)