Repository navigation
feat: allow guardrail evaluation to carry attachment references #1895
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
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 |
|---|---|---|
|
|
@@ -8,16 +8,25 @@ | |
| ) | ||
| from uipath.core.tracing import traced | ||
|
|
||
| from uipath.platform.constants import HEADER_GUARDRAILS_SOURCE | ||
| from uipath.platform.constants import HEADER_FOLDER_KEY, HEADER_GUARDRAILS_SOURCE | ||
|
|
||
| from ..chat.llm_trace_context import build_trace_context_headers | ||
| from ..common._base_service import BaseService | ||
| from ..common._config import UiPathApiConfig | ||
| from ..common._config import UiPathApiConfig, UiPathConfig | ||
| from ..common._execution_context import UiPathExecutionContext | ||
| from ..common._job_context import header_job_key | ||
| from ..common._models import Endpoint, RequestSpec | ||
| from ..errors import EnrichedException | ||
| from .guardrails import BYO_VALIDATOR_TYPE, BuiltInValidatorGuardrail | ||
| from .guardrails import ( | ||
| BYO_VALIDATOR_TYPE, | ||
| BuiltInValidatorGuardrail, | ||
| GuardrailAttachment, | ||
| ) | ||
|
|
||
| #: Timeout for a validate call carrying attachments. The backend fetches and decodes each | ||
| #: file inside the request, which the default 30s client timeout does not allow for. | ||
| _ATTACHMENT_VALIDATE_TIMEOUT_SECONDS = 60.0 | ||
|
|
||
|
|
||
| # x-uipath-traceparent-id header format: {version}-{trace_id}-{span_id}[-{trace_flags}] | ||
| # Based on W3C traceparent but allows 16- or 32-hex span IDs. | ||
|
|
@@ -102,12 +111,18 @@ def evaluate_guardrail( | |
| self, | ||
| input_data: str | dict[str, Any], | ||
| guardrail: BuiltInValidatorGuardrail, | ||
| *, | ||
| attachments: list[GuardrailAttachment] | None = None, | ||
| ) -> GuardrailValidationResult: | ||
| """Validate input text using the provided guardrail. | ||
|
|
||
| Args: | ||
| input_data: The text or structured data to validate. Dictionaries will be converted to a string before validation. | ||
| guardrail: A guardrail instance used for validation. | ||
| attachments: Files attached to the run that the guardrail may inspect, so a | ||
| validator can evaluate a file's contents rather than only its metadata. | ||
| Which validators can use them, and which file types are readable, is | ||
| decided server-side. Omitted from the request body when empty. | ||
|
|
||
| Returns: | ||
| GuardrailValidationResult: The outcome of the guardrail evaluation. | ||
|
|
@@ -127,6 +142,8 @@ def evaluate_guardrail( | |
| "BYO (Bring Your Own) guardrails require byo_validator_name." | ||
| ) | ||
| payload["byoValidatorName"] = guardrail.byo_validator_name | ||
| if attachments: | ||
| payload["attachments"] = [a.model_dump(by_alias=True) for a in attachments] | ||
| spec = RequestSpec( | ||
| method="POST", | ||
| endpoint=Endpoint("/agentsruntime_/api/execution/guardrails/validate"), | ||
|
|
@@ -141,19 +158,35 @@ def evaluate_guardrail( | |
| execution_source = self._execution_context.execution_source | ||
| if execution_source: | ||
| source_headers[HEADER_GUARDRAILS_SOURCE] = execution_source | ||
| # When attachments are present, tell helix which folder the run executed in | ||
|
Contributor
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. Same here. Drop the helix terminology. In fact, do we need this comment at all? Let's reduce the comments' verbosity and keep only what's really needed and useful.
Contributor
Author
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. Addressed in #1903 — project name dropped and the surrounding comments trimmed. |
||
| # so it can resolve each attachment id through Orchestrator's folder-scoped | ||
| # API. Only sent alongside attachments: it is meaningless otherwise. | ||
| folder_headers: dict[str, str] = {} | ||
| if attachments and UiPathConfig.folder_key: | ||
| folder_headers[HEADER_FOLDER_KEY] = UiPathConfig.folder_key | ||
| request_headers = { | ||
| **(spec.headers or {}), | ||
| **trace_headers, | ||
| **source_headers, | ||
| **header_job_key(), | ||
| **folder_headers, | ||
| } | ||
| # The default client timeout is 30s (common/_http_config.py). A validate call | ||
| # carrying attachments waits for the backend to fetch and decode each one, so give | ||
| # it more room. RequestSpec.timeout exists but is never forwarded, so pass it here. | ||
| request_kwargs: dict[str, Any] = { | ||
| "json": spec.json, | ||
| "headers": request_headers, | ||
| } | ||
| if attachments: | ||
| request_kwargs["timeout"] = _ATTACHMENT_VALIDATE_TIMEOUT_SECONDS | ||
|
apetraru-uipath marked this conversation as resolved.
|
||
|
|
||
| span_id = None | ||
| try: | ||
| response = self.request( | ||
| spec.method, | ||
| url=spec.endpoint, | ||
| json=spec.json, | ||
| headers=request_headers, | ||
| **request_kwargs, | ||
| ) | ||
| span_id = self._extract_span_id_from_traceparent( | ||
| response.headers.get("x-uipath-traceparent-id") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -96,6 +96,33 @@ class BuiltInValidatorGuardrail(BaseGuardrail): | |
| model_config = ConfigDict(populate_by_name=True, extra="allow") | ||
|
|
||
|
|
||
| class GuardrailAttachment(BaseModel): | ||
| """A reference to a file attached to the run that a guardrail may inspect. | ||
|
|
||
| Passed to [`GuardrailsService.evaluate_guardrail`][uipath.platform.guardrails.GuardrailsService.evaluate_guardrail] | ||
| so the guardrails backend can read the file's contents rather than only its metadata. | ||
|
|
||
| Only the Orchestrator attachment id crosses the wire: helix resolves it through | ||
|
Contributor
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. Don't refer the helix project name in the SDK docs please
Contributor
Author
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. Addressed in #1903 — project name dropped and the surrounding comments trimmed. |
||
| its own Orchestrator client (folder-scoped when the run's folder key is sent | ||
| alongside), so Orchestrator's access control is what decides whether the file can | ||
| be read — the runtime never resolves or forwards a signed URL itself. | ||
|
|
||
| Attributes: | ||
| id: The Orchestrator attachment id, as a string UUID. Used by the backend to | ||
| resolve the file through Orchestrator, as an extraction cache key, and for | ||
| trace correlation. | ||
| file_name: Original file name, shown to a judge model so it can name the | ||
| offending file. | ||
| mime_type: Original mime type. The backend decides what it can inspect. | ||
| """ | ||
|
|
||
| id: str | ||
| file_name: str = Field(alias="fileName") | ||
| mime_type: str = Field(alias="mimeType") | ||
|
|
||
| model_config = ConfigDict(populate_by_name=True) | ||
|
|
||
|
|
||
| class GuardrailType(str, Enum): | ||
| """Guardrail type enumeration.""" | ||
|
|
||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.