Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/uipath-platform/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath-platform"
version = "0.2.13"
version = "0.2.14"
description = "HTTP client library for programmatic access to UiPath Platform"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .decorators import (
BlockAction,
BuiltInGuardrailValidator,
ByoValidator,
CustomGuardrailValidator,
CustomValidator,
GuardrailAction,
Expand All @@ -43,6 +44,7 @@
register_guardrail_adapter,
)
from .guardrails import (
BYO_VALIDATOR_TYPE,
BuiltInValidatorGuardrail,
EnumListParameterValue,
GuardrailType,
Expand All @@ -53,6 +55,7 @@
# Service
"GuardrailsService",
# Guardrail models
"BYO_VALIDATOR_TYPE",
"BuiltInValidatorGuardrail",
"GuardrailType",
"GuardrailValidationResultType",
Expand All @@ -67,6 +70,7 @@
"guardrail",
"GuardrailValidatorBase",
"BuiltInGuardrailValidator",
"ByoValidator",
"CustomGuardrailValidator",
"HarmfulContentValidator",
"IntellectualPropertyValidator",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from ._registry import GuardrailTargetAdapter, register_guardrail_adapter
from .validators import (
BuiltInGuardrailValidator,
ByoValidator,
CustomGuardrailValidator,
CustomValidator,
GuardrailValidatorBase,
Expand All @@ -37,6 +38,7 @@
# Validators
"GuardrailValidatorBase",
"BuiltInGuardrailValidator",
"ByoValidator",
"CustomGuardrailValidator",
"HarmfulContentValidator",
"IntellectualPropertyValidator",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
CustomGuardrailValidator,
GuardrailValidatorBase,
)
from .byo import ByoValidator
from .custom import CustomValidator, RuleFunction
from .harmful_content import HarmfulContentValidator
from .intellectual_property import IntellectualPropertyValidator
Expand All @@ -16,6 +17,7 @@
__all__ = [
"GuardrailValidatorBase",
"BuiltInGuardrailValidator",
"ByoValidator",
"CustomGuardrailValidator",
"HarmfulContentValidator",
"IntellectualPropertyValidator",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""Bring Your Own Guardrail (BYOG) validator."""

from typing import Sequence
from uuid import uuid4

from uipath.platform.guardrails.guardrails import (
BYO_VALIDATOR_TYPE,
BuiltInValidatorGuardrail,
ValidatorParameter,
)

from ._base import BuiltInGuardrailValidator


class ByoValidator(BuiltInGuardrailValidator):
"""Validate data through a Bring Your Own Guardrail (BYOG) configuration.

BYOG lets an organization plug its own safety validator (e.g. a customer
Azure Content Safety subscription, a vendor connector, or a custom
Integration Service connector) into UiPath guardrails. An admin first
creates the configuration under ``Admin -> AI Trust Layer -> Guardrails
Configurations``; this validator references it by its validator name and
(recommended) Integration Service connection id.

Supported at all stages — BYO validator capabilities are connector-defined
and cannot be known statically, so no stage restriction is applied here.
Configuring a scope or stage the connector does not support surfaces as a
``PROVIDER_ERROR`` at evaluation time.

Example::

from uipath.platform.guardrails.decorators import (
BlockAction,
ByoValidator,
guardrail,
)

byog_harmful_content = ByoValidator(
"byog-harmful-content",
connection_id="24887687-6ed1-4fe2-9b87-087ffb232682",
)

@guardrail(validator=byog_harmful_content, action=BlockAction())
def summarize(text: str) -> str:
...

Args:
validator_name: The BYOG configuration's validator name
(``byoValidatorName``), as shown in Admin -> AI Trust Layer ->
Guardrails Configurations.
connection_id: Optional Integration Service connection id backing the
BYOG configuration. Strongly recommended: validator names are only
unique per connection, so omitting it lets the server pick the
first configuration matching the name.
parameters: Optional list of validator parameters. BYO parameter
schemas are connector-defined, so values are passed through as-is.

Raises:
ValueError: If *validator_name* is empty or whitespace.
"""

def __init__(
self,
validator_name: str,
*,
connection_id: str | None = None,
parameters: Sequence[ValidatorParameter] | None = None,
) -> None:
"""Initialize ByoValidator with a BYOG configuration reference."""
if not validator_name or not validator_name.strip():
raise ValueError("validator_name must be a non-empty string")
self.validator_name = validator_name
self.connection_id = connection_id
self.parameters = list(parameters or [])
Comment on lines +70 to +74

def get_built_in_guardrail(
self,
name: str,
description: str | None,
enabled_for_evals: bool,
) -> BuiltInValidatorGuardrail:
"""Build a BYOG :class:`BuiltInValidatorGuardrail`.

Args:
name: Name for the guardrail.
description: Optional description.
enabled_for_evals: Whether active in evaluation scenarios.

Returns:
Configured :class:`BuiltInValidatorGuardrail` referencing the BYOG
configuration via ``byoValidatorName``/``byoConnectionId``.
"""
return BuiltInValidatorGuardrail(
id=str(uuid4()),
name=name,
description=description
or f"Bring Your Own Guardrail validation '{self.validator_name}'",
enabled_for_evals=enabled_for_evals,
guardrail_type="builtInValidator",
validator_type=BYO_VALIDATOR_TYPE,
validator_parameters=self.parameters,
byo_validator_name=self.validator_name,
byo_connection_id=self.connection_id,
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from uipath.platform.guardrails.decorators import (
BlockAction,
ByoValidator,
CustomValidator,
GuardrailAction,
GuardrailBlockException,
Expand Down Expand Up @@ -1236,3 +1237,91 @@ def joke(topic: str) -> str:
with pytest.raises(GuardrailBlockException):
joke("cats")
mock_uipath.guardrails.evaluate_guardrail.assert_called_once()


# ---------------------------------------------------------------------------
# ByoValidator — Bring Your Own Guardrail configuration reference
# ---------------------------------------------------------------------------


class TestByoValidator:
def test_empty_validator_name_raises(self):
with pytest.raises(ValueError, match="validator_name"):
ByoValidator("")

def test_whitespace_validator_name_raises(self):
with pytest.raises(ValueError, match="validator_name"):
ByoValidator(" ")

def test_builds_byo_guardrail_with_name_and_connection(self):
v = ByoValidator(
"byog-harmful-content",
connection_id="24887687-6ed1-4fe2-9b87-087ffb232682",
)
g = v.get_built_in_guardrail("G", None, True)
assert g.validator_type == "byo"
assert g.byo_validator_name == "byog-harmful-content"
assert g.byo_connection_id == "24887687-6ed1-4fe2-9b87-087ffb232682"

def test_connection_id_defaults_to_none(self):
v = ByoValidator("byog-harmful-content")
g = v.get_built_in_guardrail("G", None, True)
assert g.byo_connection_id is None

def test_aliases_serialize_for_the_wire(self):
v = ByoValidator("byog-pii", connection_id="conn-1")
g = v.get_built_in_guardrail("G", None, True)
dumped = g.model_dump(by_alias=True)
assert dumped["validatorType"] == "byo"
assert dumped["byoValidatorName"] == "byog-pii"
assert dumped["byoConnectionId"] == "conn-1"
assert dumped["$guardrailType"] == "builtInValidator"

def test_parameters_pass_through(self):
from uipath.platform.guardrails.guardrails import NumberParameterValue

param = NumberParameterValue(parameter_type="number", id="threshold", value=0.7)
v = ByoValidator("byog-custom", parameters=[param])
g = v.get_built_in_guardrail("G", None, True)
assert g.validator_parameters == [param]

def test_parameters_default_empty(self):
v = ByoValidator("byog-custom")
g = v.get_built_in_guardrail("G", None, True)
assert g.validator_parameters == []

def test_default_description_includes_validator_name(self):
v = ByoValidator("byog-harmful-content")
g = v.get_built_in_guardrail("G", None, True)
assert g.description is not None
assert "byog-harmful-content" in g.description

def test_no_stage_restriction(self):
v = ByoValidator("byog-harmful-content")
# BYO capabilities are connector-defined — all stages allowed
v.validate_stage(GuardrailExecutionStage.PRE)
v.validate_stage(GuardrailExecutionStage.POST)

def test_selector_is_none(self):
v = ByoValidator("byog-harmful-content")
g = v.get_built_in_guardrail("G", None, True)
assert g.selector is None

def test_run_forwards_byo_guardrail_to_service(self):
v = ByoValidator("byog-harmful-content", connection_id="conn-1")
mock_uipath = MagicMock()
mock_uipath.guardrails.evaluate_guardrail.return_value = (
GuardrailValidationResult(
result=GuardrailValidationResultType.PASSED, reason=""
)
)
with patch("uipath.platform.UiPath", return_value=mock_uipath):
result = v.run(
"G", None, True, "some input", GuardrailExecutionStage.PRE, None, None
)
assert result.result == GuardrailValidationResultType.PASSED
data, g = mock_uipath.guardrails.evaluate_guardrail.call_args[0]
assert data == "some input"
assert g.validator_type == "byo"
assert g.byo_validator_name == "byog-harmful-content"
assert g.byo_connection_id == "conn-1"
2 changes: 1 addition & 1 deletion packages/uipath-platform/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/uipath/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading