-
Notifications
You must be signed in to change notification settings - Fork 60
fix(tests): three false-negative guardrail failures from the 2026-09-10 nightlies #3202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d85c30b
02668f6
2ebfac0
2add28f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| #!/usr/bin/env python3 | ||
| """Selective `uip` shim for the BYOG guardrail tasks. | ||
|
|
||
| Shared by `coded/guardrails/byog_middleware`, `coded/guardrails/byog_decorator` | ||
| and `lowcode/guardrails/byog_pinning` -- one payload, so a CLI field rename is a | ||
| one-file edit and the three tasks cannot drift apart. Serves the two read-only | ||
| discovery paths an agent walks before it can pin a bring-your-own guardrail, | ||
| both answering with the configuration the prompts name (`byog-smoke-agent-pin`, | ||
| pii_detection): | ||
|
|
||
| * `uip agent guardrails list [--byo]` -> GuardrailDefinitionsList | ||
| * `uip guardrails byo-configurations list` -> ByoGuardrailConfigurationsList | ||
|
|
||
| Without `--byo`, `agent guardrails list` also returns the BUILT-IN | ||
| `pii_detection` entry sharing the same `Validator` name, so the agent must | ||
| disambiguate via `IsByo`/`ByoValidatorName` exactly as the skill teaches | ||
| rather than grabbing the first PII row. The two views agree on ids and | ||
| connector metadata, so cross-checking one against the other is consistent. | ||
|
|
||
| Every other invocation exec's the REAL `uip` found later on PATH, and | ||
| `--help` is handed to the real CLI so flag questions get real answers | ||
| (a canned payload there reads as "that flag does not exist"). | ||
|
|
||
| Why mock: the graded behavior is code wiring in `graph.py`, and both | ||
| checkers are pure AST analysis that never touch a tenant -- only the | ||
| agent's DISCOVERY step needed one. It could not keep depending on the | ||
| shared smoke tenant. `byo-configurations create` probes the connection | ||
| server-side with no skip flag, so a configuration cannot be seeded | ||
| per-run, and the tenant has no guardrail-capable connection to bind | ||
| (same reasoning as the byog_pinning mock, which fixed the low-code half | ||
| of this family). Left live, these tasks were graded on whether an | ||
| external tenant happened to hold a fixture: on 2026-08-24 the tenant | ||
| held three configurations, none of them `byog-smoke-agent-pin`; by | ||
| 2026-09-10 it held none at all, and both discovery calls returned | ||
| `Data: []`. The agent then correctly refused to fabricate a validator | ||
| name and changed nothing, scoring 0.00 -- a fixture outage rendered as a | ||
| skill regression. | ||
|
|
||
| `mock_path_dirs: [mocks]` PATH-prepends this script's directory inside the | ||
| sandbox. Linux smoke only (no `windows` tag on any of the three tasks), hence no | ||
| `uip.cmd` twin -- same as the ixp and platform guardrails mocks. | ||
| """ | ||
|
|
||
| import json | ||
| import os | ||
| import shutil | ||
| import sys | ||
|
|
||
| MOCK_DIR = os.path.dirname(os.path.abspath(__file__)) | ||
|
|
||
| VALIDATOR_NAME = "byog-smoke-agent-pin" | ||
| CONNECTION_ID = "18fb337c-29b7-4162-a9e8-0c05b01cf4df" | ||
| CONFIGURATION_ID = "e5723bb8-fbc2-4317-c7d7-08de803bc010" | ||
| CONNECTOR_NAME = "Azure AI Content Safety" | ||
| CONNECTOR_KEY = "uipath-azure-contentsafety" | ||
|
|
||
| PII_PARAMETERS = [ | ||
| { | ||
| "Type": "enum-list", | ||
| "Id": "entities", | ||
| "Required": True, | ||
| "DefaultValue": ["Email", "PhoneNumber"], | ||
| "Options": [ | ||
| "Email", | ||
| "PhoneNumber", | ||
| "Person", | ||
| "Address", | ||
| "USSocialSecurityNumber", | ||
| ], | ||
| "DisplayName": "Entities", | ||
| "Description": "PII entity types to detect.", | ||
| }, | ||
| { | ||
| "Type": "map-enum", | ||
| "Id": "entityThresholds", | ||
| "Required": True, | ||
| "DefaultValue": {"Email": 0.5, "PhoneNumber": 0.5}, | ||
| "KeySource": "entities", | ||
| "Min": 0, | ||
| "Max": 1, | ||
| "DisplayName": "Per-entity threshold", | ||
| "Description": "Confidence threshold (0-1) per selected entity.", | ||
| }, | ||
| ] | ||
|
|
||
| BYO_ENTRY = { | ||
| "Validator": "pii_detection", | ||
| "IsByo": True, | ||
| "Status": "Available", | ||
| "AllowedScopes": ["Agent", "Llm", "Tool"], | ||
| "GuardrailStages": { | ||
| "Agent": ["PreExecution", "PostExecution"], | ||
| "Llm": ["PreExecution", "PostExecution"], | ||
| "Tool": ["PreExecution", "PostExecution"], | ||
| }, | ||
| "DisplayName": "PII Detection", | ||
| "Description": ( | ||
| "Detects personally identifiable information. Served by the " | ||
| "tenant's external bring-your-own guardrail provider." | ||
| ), | ||
| "ByoValidatorName": VALIDATOR_NAME, | ||
| "ByoConnectionId": CONNECTION_ID, | ||
| "ByoConfigurationId": CONFIGURATION_ID, | ||
| "ByoConnectorName": CONNECTOR_NAME, | ||
| "ByoConnectorKey": CONNECTOR_KEY, | ||
| "FolderKey": "627fe423-5c73-464a-abff-41fdaad6ac19", | ||
| "Parameters": PII_PARAMETERS, | ||
| } | ||
|
|
||
| BUILTIN_ENTRY = { | ||
| "Validator": "pii_detection", | ||
| "IsByo": False, | ||
| "Status": "Available", | ||
| "AllowedScopes": ["Agent", "Llm", "Tool"], | ||
| "GuardrailStages": { | ||
| "Agent": ["PreExecution", "PostExecution"], | ||
| "Llm": ["PreExecution", "PostExecution"], | ||
| "Tool": ["PreExecution", "PostExecution"], | ||
| }, | ||
| "DisplayName": "PII Detection", | ||
| "Description": ( | ||
| "Detects personally identifiable information using Azure " | ||
| "Cognitive Services (UiPath built-in)." | ||
| ), | ||
| "Parameters": PII_PARAMETERS, | ||
| } | ||
|
|
||
| # The admin-side view of the same record. Field names mirror the CLI's own | ||
| # documented examples for `guardrails byo-configurations`, so the fields the | ||
| # skill teaches are the fields that come back. | ||
| CONFIGURATION = { | ||
| "Id": CONFIGURATION_ID, | ||
| "ConnectionId": CONNECTION_ID, | ||
| "ValidatorName": VALIDATOR_NAME, | ||
| "ValidatorType": "pii_detection", | ||
| "FallbackOnUiPath": False, | ||
| "Enabled": True, | ||
| "CreatedAt": "2026-07-02T11:40:00Z", | ||
| "UpdatedAt": None, | ||
| "ConnectorKey": CONNECTOR_KEY, | ||
| "ConnectorName": CONNECTOR_NAME, | ||
| "ConnectionName": f"{CONNECTOR_NAME} - Prod", | ||
| "ValidConnection": True, | ||
| } | ||
|
|
||
|
|
||
| def real_uip(): | ||
| path = os.environ.get("PATH", "") | ||
| parts = [p for p in path.split(os.pathsep) if p and os.path.abspath(p) != MOCK_DIR] | ||
| return shutil.which("uip", path=os.pathsep.join(parts)) | ||
|
|
||
|
|
||
| def emit(code, data): | ||
| print(json.dumps({"Result": "Success", "Code": code, "Data": data}, indent=2)) | ||
| return 0 | ||
|
|
||
|
|
||
| def main(): | ||
| args = sys.argv[1:] | ||
| literal = [a for a in args if not a.startswith("-")] | ||
|
|
||
| # A help request is a question about flags, not about tenant data. | ||
| if "--help" not in args and "-h" not in args: | ||
| if literal[:3] == ["agent", "guardrails", "list"]: | ||
| data = [BYO_ENTRY] if "--byo" in args else [BUILTIN_ENTRY, BYO_ENTRY] | ||
| return emit("GuardrailDefinitionsList", data) | ||
| if literal[:3] == ["guardrails", "byo-configurations", "list"]: | ||
| return emit("ByoGuardrailConfigurationsList", [CONFIGURATION]) | ||
|
|
||
| real = real_uip() | ||
| if not real: | ||
| sys.stderr.write("uip (shim): real uip CLI not found on PATH\n") | ||
| return 127 | ||
| os.execv(real, [real] + args) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,10 @@ | ||
| task_id: skill-agent-guardrail-coded-byog-middleware | ||
| description: > | ||
| Middleware-style bring-your-own guardrail (BYOG) for a coded agent. A simple | ||
| LangGraph customer support agent is seeded with no guardrails. The tenant already | ||
| has a BYOG configuration registered (admin-side); the user asks to route the | ||
| agent's PII checks through it. Verifies the agent wires | ||
| LangGraph customer support agent is seeded with no guardrails. A mocked `uip` | ||
| serves the BYOG configuration the prompt names, so discovery answers the same | ||
| way on every run; the user asks to route the agent's PII checks through it. | ||
| Verifies the agent wires | ||
| UiPathByoGuardrailMiddleware — pinned to the configuration by validator_name, | ||
| spread into create_agent(), with a block action — rather than reaching for the | ||
| built-in PII validator. | ||
|
|
@@ -21,7 +22,13 @@ agent: | |
| disallowed_tools: ["Task"] | ||
|
|
||
| sandbox: | ||
| # Discovery is mocked, so the task no longer depends on a fixture living on the | ||
| # shared smoke tenant. See _fixtures/ByogMockCli/mocks/uip for why that | ||
| # dependency was untenable and what the shim serves. | ||
| mock_path_dirs: [mocks] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 minor — nothing grades the discovery this mock exists to serve. The low-code sibling pairs its shim with a criterion ( - type: command_executed
description: "Agent discovered the BYO entry via uip agent guardrails list (--byo or full list)"
tool_name: "Bash"
command_pattern: 'uip\s+agent\s+guardrails\s+list'
min_count: 1Here there is no such criterion, and the prompt already names
Suggest adding the |
||
| template_sources: | ||
| - type: template_dir | ||
| path: ../../../_fixtures/ByogMockCli | ||
| - type: template_dir | ||
| path: ../_fixtures/SimpleCodedAgent | ||
|
|
||
|
|
@@ -39,6 +46,14 @@ initial_prompt: | | |
| end-to-end in a single pass. | ||
|
|
||
| success_criteria: | ||
| - type: command_executed | ||
| description: "Agent discovered the BYO configuration from the CLI rather than copying the name out of the prompt" | ||
| tool_name: "Bash" | ||
| command_pattern: '(uip|\$UIP)\s+(agent\s+guardrails\s+list|guardrails\s+byo-configurations\s+list)' | ||
| min_count: 1 | ||
| weight: 2.0 | ||
| pass_threshold: 1.0 | ||
|
|
||
| - type: run_command | ||
| description: "BYOG middleware guardrail correctly added to graph.py" | ||
| command: "python3 $TASK_DIR/check_byog_middleware.py" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 minor — second copy of this payload, and the twin is now the weaker one.
tests/tasks/uipath-agents/lowcode/guardrails/byog_pinning/mock_template/mocks/uipcarries the sameBYO_ENTRYandBUILTIN_ENTRY, the same GUIDs, the same validator name. This file adds two things it does not have:guardrails byo-configurations list, so a low-code agent that follows theByoConfigurationIdcross-reference atlowcode/.../guardrails.md:642still falls through to the real CLI and getsData: [], the exact fixture-outage failure this PR is fixing on the coded half.--helppassthrough, sobyog_pinningstill answers a flag question with a canned payload.Two copies that already disagree will keep disagreeing, and a CLI field rename now needs both edited. Suggest pointing
byog_pinning.yamlat this fixture and deleting its local copy (it already reaches across with../_fixtures/..., though I have not verified the harness accepts a path that climbs out oflowcode/), or hoisting the shim to a sharedtests/tasks/uipath-agents/_fixtures/.Not blocking, and either way this file is the better of the two.