From 5ceae7d193bdfaddd7eb8433e0bbff891071fd68 Mon Sep 17 00:00:00 2001 From: Valentina Bojan Date: Wed, 15 Jul 2026 13:12:58 +0300 Subject: [PATCH 1/5] feat(guardrails): forward byoConnectionId for BYO guardrails Add byo_connection_id (alias byoConnectionId) to BuiltInValidatorGuardrail and, for BYO guardrails, include it in the validate payload when present. The backend uses it to narrow BYOG configuration resolution to a specific connection (mirrors how a BYO model resolves). Resolution by validator name alone is unchanged when no connection id is set. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../guardrails/_guardrails_service.py | 2 + .../uipath/platform/guardrails/guardrails.py | 1 + .../tests/services/test_guardrails_service.py | 103 ++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/packages/uipath-platform/src/uipath/platform/guardrails/_guardrails_service.py b/packages/uipath-platform/src/uipath/platform/guardrails/_guardrails_service.py index b73d810e7..fbeb1f027 100644 --- a/packages/uipath-platform/src/uipath/platform/guardrails/_guardrails_service.py +++ b/packages/uipath-platform/src/uipath/platform/guardrails/_guardrails_service.py @@ -127,6 +127,8 @@ def evaluate_guardrail( "BYO (Bring Your Own) guardrails require byo_validator_name." ) payload["byoValidatorName"] = guardrail.byo_validator_name + if guardrail.byo_connection_id: + payload["byoConnectionId"] = guardrail.byo_connection_id spec = RequestSpec( method="POST", endpoint=Endpoint("/agentsruntime_/api/execution/guardrails/validate"), diff --git a/packages/uipath-platform/src/uipath/platform/guardrails/guardrails.py b/packages/uipath-platform/src/uipath/platform/guardrails/guardrails.py index dace18019..c7a968a61 100644 --- a/packages/uipath-platform/src/uipath/platform/guardrails/guardrails.py +++ b/packages/uipath-platform/src/uipath/platform/guardrails/guardrails.py @@ -92,6 +92,7 @@ class BuiltInValidatorGuardrail(BaseGuardrail): default_factory=list, alias="validatorParameters" ) byo_validator_name: str | None = Field(default=None, alias="byoValidatorName") + byo_connection_id: str | None = Field(default=None, alias="byoConnectionId") model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/packages/uipath-platform/tests/services/test_guardrails_service.py b/packages/uipath-platform/tests/services/test_guardrails_service.py index 6fd20bbc5..c7859c81f 100644 --- a/packages/uipath-platform/tests/services/test_guardrails_service.py +++ b/packages/uipath-platform/tests/services/test_guardrails_service.py @@ -350,6 +350,109 @@ def capture_request(request): assert request_payload["byoValidatorName"] == "my_databricks_pii" assert result.result == GuardrailValidationResultType.PASSED + def test_evaluate_guardrail_byog_forwards_byo_connection_id( + self, + httpx_mock: HTTPXMock, + service: GuardrailsService, + base_url: str, + org: str, + tenant: str, + ) -> None: + """A BYOG guardrail forwards byoConnectionId so the backend can narrow + configuration resolution to the specific connection.""" + captured_request = None + + def capture_request(request): + nonlocal captured_request + captured_request = request + return httpx.Response( + status_code=200, + json={"result": "PASSED", "details": "Validation passed"}, + ) + + httpx_mock.add_callback( + method="POST", + url=f"{base_url}{org}{tenant}/agentsruntime_/api/execution/guardrails/validate", + callback=capture_request, + ) + + byog_guardrail = BuiltInValidatorGuardrail( + id="byog-id", + name="Databricks PII (BYOG)", + enabled_for_evals=True, + selector=GuardrailSelector(scopes=[GuardrailScope.LLM]), + guardrail_type="builtInValidator", + validator_type="byo", + byo_validator_name="my_databricks_pii", + byo_connection_id="byog-conn-1", + validator_parameters=[], + ) + + service.evaluate_guardrail("some input", byog_guardrail) + + assert captured_request is not None + request_payload = json.loads(captured_request.content) + assert request_payload["byoValidatorName"] == "my_databricks_pii" + assert request_payload["byoConnectionId"] == "byog-conn-1" + + def test_evaluate_guardrail_byog_omits_connection_id_when_absent( + self, + httpx_mock: HTTPXMock, + service: GuardrailsService, + base_url: str, + org: str, + tenant: str, + ) -> None: + """byoConnectionId is only forwarded when present; a BYOG guardrail without + one resolves by validator name alone.""" + captured_request = None + + def capture_request(request): + nonlocal captured_request + captured_request = request + return httpx.Response( + status_code=200, + json={"result": "PASSED", "details": "Validation passed"}, + ) + + httpx_mock.add_callback( + method="POST", + url=f"{base_url}{org}{tenant}/agentsruntime_/api/execution/guardrails/validate", + callback=capture_request, + ) + + byog_guardrail = BuiltInValidatorGuardrail( + id="byog-id", + name="Databricks PII (BYOG)", + enabled_for_evals=True, + selector=GuardrailSelector(scopes=[GuardrailScope.LLM]), + guardrail_type="builtInValidator", + validator_type="byo", + byo_validator_name="my_databricks_pii", + validator_parameters=[], + ) + + service.evaluate_guardrail("some input", byog_guardrail) + + assert captured_request is not None + request_payload = json.loads(captured_request.content) + assert "byoConnectionId" not in request_payload + + def test_evaluate_guardrail_byo_connection_id_from_alias(self) -> None: + """byoConnectionId parses into the typed field via its camelCase alias.""" + guardrail = BuiltInValidatorGuardrail.model_validate( + { + "$guardrailType": "builtInValidator", + "id": "byog-id", + "name": "BYOG", + "validatorType": "byo", + "byoValidatorName": "my_databricks_pii", + "byoConnectionId": "byog-conn-1", + "validatorParameters": [], + } + ) + assert guardrail.byo_connection_id == "byog-conn-1" + def test_evaluate_guardrail_byo_without_name_raises( self, service: GuardrailsService, From ec9b6f7ac96bdaaf411058910e7624d8e91688ae Mon Sep 17 00:00:00 2001 From: Valentina Bojan Date: Wed, 15 Jul 2026 14:30:00 +0300 Subject: [PATCH 2/5] test(guardrails): assert byoConnectionId not forwarded for non-byo validator Addresses PR review: a regression test that byoConnectionId is dropped when validator_type != "byo" even if byo_connection_id is set, mirroring the existing byoValidatorName negative test. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../tests/services/test_guardrails_service.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/packages/uipath-platform/tests/services/test_guardrails_service.py b/packages/uipath-platform/tests/services/test_guardrails_service.py index c7859c81f..c94bf8be3 100644 --- a/packages/uipath-platform/tests/services/test_guardrails_service.py +++ b/packages/uipath-platform/tests/services/test_guardrails_service.py @@ -529,6 +529,49 @@ def capture_request(request): request_payload = json.loads(captured_request.content) assert "byoValidatorName" not in request_payload + def test_evaluate_guardrail_non_byo_type_does_not_forward_connection_id( + self, + httpx_mock: HTTPXMock, + service: GuardrailsService, + base_url: str, + org: str, + tenant: str, + ) -> None: + """byoConnectionId is only forwarded for the "byo" sentinel, never leaked + into a non-BYOG validator payload even if the field happens to be set.""" + captured_request = None + + def capture_request(request): + nonlocal captured_request + captured_request = request + return httpx.Response( + status_code=200, + json={"result": "PASSED", "details": "Validation passed"}, + ) + + httpx_mock.add_callback( + method="POST", + url=f"{base_url}{org}{tenant}/agentsruntime_/api/execution/guardrails/validate", + callback=capture_request, + ) + + guardrail = BuiltInValidatorGuardrail( + id="test-id", + name="PII detection guardrail", + enabled_for_evals=True, + selector=GuardrailSelector(scopes=[GuardrailScope.LLM]), + guardrail_type="builtInValidator", + validator_type="pii_detection", + byo_connection_id="stray-conn", + validator_parameters=[], + ) + + service.evaluate_guardrail("some input", guardrail) + + assert captured_request is not None + request_payload = json.loads(captured_request.content) + assert "byoConnectionId" not in request_payload + def test_evaluate_guardrail_ootb_omits_byo_validator_name( self, httpx_mock: HTTPXMock, From 6fcfc43e04c56f415e652bc3ddadac3516382d3f Mon Sep 17 00:00:00 2001 From: Valentina Bojan Date: Wed, 15 Jul 2026 15:29:27 +0300 Subject: [PATCH 3/5] chore(platform): bump uipath-platform to 0.2.10 for byoConnectionId forwarding --- packages/uipath-platform/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index 42178758a..f5c7522b9 100644 --- a/packages/uipath-platform/pyproject.toml +++ b/packages/uipath-platform/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-platform" -version = "0.2.9" +version = "0.2.10" description = "HTTP client library for programmatic access to UiPath Platform" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" From b3e717f0a7e8e0abe491494c6622666f46fdfdb8 Mon Sep 17 00:00:00 2001 From: Valentina Bojan Date: Wed, 15 Jul 2026 15:30:19 +0300 Subject: [PATCH 4/5] chore(platform): sync uv.lock to uipath-platform 0.2.10 --- packages/uipath-platform/uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/uipath-platform/uv.lock b/packages/uipath-platform/uv.lock index ea1f85ad3..744c292d3 100644 --- a/packages/uipath-platform/uv.lock +++ b/packages/uipath-platform/uv.lock @@ -1095,7 +1095,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.2.9" +version = "0.2.10" source = { editable = "." } dependencies = [ { name = "httpx" }, From 7be0d06bb54d758fc0c2bb7c5c0f8612c97d95db Mon Sep 17 00:00:00 2001 From: Valentina Bojan Date: Wed, 15 Jul 2026 15:30:21 +0300 Subject: [PATCH 5/5] chore(platform): sync uv.lock to uipath-platform 0.2.10 --- packages/uipath/uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index 1c38c3b33..aea72e7a1 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2741,7 +2741,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.2.9" +version = "0.2.10" source = { editable = "../uipath-platform" } dependencies = [ { name = "httpx" },