uipath-core 0.1.3__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- uipath/core/guardrails/_evaluators.py +93 -17
- uipath/core/guardrails/guardrails.py +13 -4
- {uipath_core-0.1.3.dist-info → uipath_core-0.1.4.dist-info}/METADATA +1 -1
- {uipath_core-0.1.3.dist-info → uipath_core-0.1.4.dist-info}/RECORD +6 -6
- {uipath_core-0.1.3.dist-info → uipath_core-0.1.4.dist-info}/WHEEL +0 -0
- {uipath_core-0.1.3.dist-info → uipath_core-0.1.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,8 +4,9 @@ This module provides functions for evaluating different types of guardrail rules
|
|
|
4
4
|
against input and output data.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import inspect
|
|
7
8
|
from enum import IntEnum
|
|
8
|
-
from typing import Any
|
|
9
|
+
from typing import Any, Callable
|
|
9
10
|
|
|
10
11
|
from .guardrails import (
|
|
11
12
|
AllFieldsSelector,
|
|
@@ -163,7 +164,7 @@ def format_guardrail_error_message(
|
|
|
163
164
|
) -> str:
|
|
164
165
|
"""Format a guardrail error message following the standard pattern."""
|
|
165
166
|
source = "Input" if field_ref.source == FieldSource.INPUT else "Output"
|
|
166
|
-
message = f"{source} data didn't match the guardrail condition: [{field_ref.path}] {operator}"
|
|
167
|
+
message = f"{source} data didn't match the guardrail condition: [{field_ref.path}] comparing function [{operator}]"
|
|
167
168
|
if expected_value and expected_value.strip():
|
|
168
169
|
message += f" [{expected_value.strip()}]"
|
|
169
170
|
return message
|
|
@@ -187,16 +188,18 @@ def evaluate_word_rule(
|
|
|
187
188
|
field_str = field_value
|
|
188
189
|
|
|
189
190
|
# Use the custom function to evaluate the rule
|
|
191
|
+
# If detects_violation returns True, it means the rule was violated (validation fails)
|
|
190
192
|
try:
|
|
191
|
-
|
|
193
|
+
violation_detected = rule.detects_violation(field_str)
|
|
192
194
|
except Exception:
|
|
193
195
|
# If function raises an exception, treat as failure
|
|
194
|
-
|
|
196
|
+
violation_detected = True
|
|
195
197
|
|
|
196
|
-
if
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
if violation_detected:
|
|
199
|
+
operator = (
|
|
200
|
+
_humanize_guardrail_func(rule.detects_violation) or "violation check"
|
|
199
201
|
)
|
|
202
|
+
reason = format_guardrail_error_message(field_ref, operator, None)
|
|
200
203
|
return False, reason
|
|
201
204
|
|
|
202
205
|
return True, "All word rule validations passed"
|
|
@@ -221,16 +224,18 @@ def evaluate_number_rule(
|
|
|
221
224
|
field_num = float(field_value)
|
|
222
225
|
|
|
223
226
|
# Use the custom function to evaluate the rule
|
|
227
|
+
# If detects_violation returns True, it means the rule was violated (validation fails)
|
|
224
228
|
try:
|
|
225
|
-
|
|
229
|
+
violation_detected = rule.detects_violation(field_num)
|
|
226
230
|
except Exception:
|
|
227
231
|
# If function raises an exception, treat as failure
|
|
228
|
-
|
|
232
|
+
violation_detected = True
|
|
229
233
|
|
|
230
|
-
if
|
|
231
|
-
|
|
232
|
-
|
|
234
|
+
if violation_detected:
|
|
235
|
+
operator = (
|
|
236
|
+
_humanize_guardrail_func(rule.detects_violation) or "violation check"
|
|
233
237
|
)
|
|
238
|
+
reason = format_guardrail_error_message(field_ref, operator, None)
|
|
234
239
|
return False, reason
|
|
235
240
|
|
|
236
241
|
return True, "All number rule validations passed"
|
|
@@ -256,16 +261,18 @@ def evaluate_boolean_rule(
|
|
|
256
261
|
field_bool = field_value
|
|
257
262
|
|
|
258
263
|
# Use the custom function to evaluate the rule
|
|
264
|
+
# If detects_violation returns True, it means the rule was violated (validation fails)
|
|
259
265
|
try:
|
|
260
|
-
|
|
266
|
+
violation_detected = rule.detects_violation(field_bool)
|
|
261
267
|
except Exception:
|
|
262
268
|
# If function raises an exception, treat as failure
|
|
263
|
-
|
|
269
|
+
violation_detected = True
|
|
264
270
|
|
|
265
|
-
if
|
|
266
|
-
|
|
267
|
-
|
|
271
|
+
if violation_detected:
|
|
272
|
+
operator = (
|
|
273
|
+
_humanize_guardrail_func(rule.detects_violation) or "violation check"
|
|
268
274
|
)
|
|
275
|
+
reason = format_guardrail_error_message(field_ref, operator, None)
|
|
269
276
|
return False, reason
|
|
270
277
|
|
|
271
278
|
return True, "All boolean rule validations passed"
|
|
@@ -307,3 +314,72 @@ def evaluate_universal_rule(
|
|
|
307
314
|
return False, "Universal rule validation triggered (input and output)"
|
|
308
315
|
else:
|
|
309
316
|
return False, f"Unknown apply_to value: {rule.apply_to}"
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _humanize_guardrail_func(func: Callable[..., Any] | str | None) -> str | None:
|
|
320
|
+
"""Build a user-friendly description of a guardrail predicate.
|
|
321
|
+
|
|
322
|
+
Deterministic guardrails store Python callables (often lambdas) to evaluate
|
|
323
|
+
conditions. For diagnostics, it's useful to include a readable hint about the
|
|
324
|
+
predicate that failed.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
func: A Python callable used as a predicate, or a pre-rendered string
|
|
328
|
+
description (for example, ``"s:str -> bool: contains 'test'"``).
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
A human-readable description, or ``None`` if one cannot be produced.
|
|
332
|
+
"""
|
|
333
|
+
if func is None:
|
|
334
|
+
return None
|
|
335
|
+
|
|
336
|
+
if isinstance(func, str):
|
|
337
|
+
rendered = func.strip()
|
|
338
|
+
return rendered or None
|
|
339
|
+
|
|
340
|
+
name = getattr(func, "__name__", None)
|
|
341
|
+
if name and name != "<lambda>":
|
|
342
|
+
return name
|
|
343
|
+
|
|
344
|
+
# Best-effort extraction for lambdas / callables.
|
|
345
|
+
try:
|
|
346
|
+
sig = str(inspect.signature(func))
|
|
347
|
+
except (TypeError, ValueError):
|
|
348
|
+
sig = ""
|
|
349
|
+
|
|
350
|
+
try:
|
|
351
|
+
source_lines = inspect.getsourcelines(func)
|
|
352
|
+
source = "".join(source_lines[0]).strip()
|
|
353
|
+
# Collapse whitespace to keep the message compact.
|
|
354
|
+
source = " ".join(source.split())
|
|
355
|
+
|
|
356
|
+
# Remove "detects_violation=lambda" prefix if present
|
|
357
|
+
# Pattern: "detects_violation=lambda s: condition" -> "condition"
|
|
358
|
+
if "detects_violation=lambda" in source:
|
|
359
|
+
# Find the lambda part
|
|
360
|
+
lambda_start = source.find("detects_violation=lambda")
|
|
361
|
+
if lambda_start != -1:
|
|
362
|
+
# Get everything after "detects_violation=lambda"
|
|
363
|
+
lambda_part = source[
|
|
364
|
+
lambda_start + len("detects_violation=lambda") :
|
|
365
|
+
].strip()
|
|
366
|
+
# Find the colon that separates param from body
|
|
367
|
+
colon_idx = lambda_part.find(":")
|
|
368
|
+
if colon_idx != -1:
|
|
369
|
+
# Extract just the body (condition)
|
|
370
|
+
body = lambda_part[colon_idx + 1 :].strip()
|
|
371
|
+
# Remove trailing comma if present
|
|
372
|
+
body = body.rstrip(",").strip()
|
|
373
|
+
source = body
|
|
374
|
+
except (OSError, TypeError):
|
|
375
|
+
source = ""
|
|
376
|
+
|
|
377
|
+
if source and sig:
|
|
378
|
+
return f"{sig}: {source}"
|
|
379
|
+
if source:
|
|
380
|
+
return source
|
|
381
|
+
if sig:
|
|
382
|
+
return sig
|
|
383
|
+
|
|
384
|
+
rendered = repr(func).strip()
|
|
385
|
+
return rendered or None
|
|
@@ -92,7 +92,10 @@ class WordRule(BaseModel):
|
|
|
92
92
|
|
|
93
93
|
rule_type: Literal["word"] = Field(alias="$ruleType")
|
|
94
94
|
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
95
|
-
|
|
95
|
+
detects_violation: Callable[[str], bool] = Field(
|
|
96
|
+
exclude=True,
|
|
97
|
+
description="Function that returns True if the string violates the rule (validation should fail).",
|
|
98
|
+
)
|
|
96
99
|
|
|
97
100
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
98
101
|
|
|
@@ -111,7 +114,10 @@ class NumberRule(BaseModel):
|
|
|
111
114
|
|
|
112
115
|
rule_type: Literal["number"] = Field(alias="$ruleType")
|
|
113
116
|
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
114
|
-
|
|
117
|
+
detects_violation: Callable[[float], bool] = Field(
|
|
118
|
+
exclude=True,
|
|
119
|
+
description="Function that returns True if the number violates the rule (validation should fail).",
|
|
120
|
+
)
|
|
115
121
|
|
|
116
122
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
117
123
|
|
|
@@ -121,7 +127,10 @@ class BooleanRule(BaseModel):
|
|
|
121
127
|
|
|
122
128
|
rule_type: Literal["boolean"] = Field(alias="$ruleType")
|
|
123
129
|
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
124
|
-
|
|
130
|
+
detects_violation: Callable[[bool], bool] = Field(
|
|
131
|
+
exclude=True,
|
|
132
|
+
description="Function that returns True if the boolean violates the rule (validation should fail).",
|
|
133
|
+
)
|
|
125
134
|
|
|
126
135
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
127
136
|
|
|
@@ -164,7 +173,7 @@ class BaseGuardrail(BaseModel):
|
|
|
164
173
|
class DeterministicGuardrail(BaseGuardrail):
|
|
165
174
|
"""Deterministic guardrail model."""
|
|
166
175
|
|
|
167
|
-
guardrail_type: Literal["custom"] = Field(alias="
|
|
176
|
+
guardrail_type: Literal["custom"] = Field(alias="$guardrailType")
|
|
168
177
|
rules: list[Rule]
|
|
169
178
|
|
|
170
179
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
@@ -16,8 +16,8 @@ uipath/core/errors/__init__.py,sha256=gjxdLibZ0fjwgzPuLJY04P8dIX9rbSM2wQ97jP34uc
|
|
|
16
16
|
uipath/core/errors/errors.py,sha256=5LajjuTfNW82ju07wT5mD3tXk0S-Ju7OqJqQpPN0F6g,486
|
|
17
17
|
uipath/core/guardrails/__init__.py,sha256=baH9Vj8f6spKOpxv3dUl5UKg-LWthaoZ0RidtlFjyEQ,956
|
|
18
18
|
uipath/core/guardrails/_deterministic_guardrails_service.py,sha256=3WGcgpyUIvTXBvheEXjX-XguytxquWJkBevrVeM2tcc,2841
|
|
19
|
-
uipath/core/guardrails/_evaluators.py,sha256=
|
|
20
|
-
uipath/core/guardrails/guardrails.py,sha256=
|
|
19
|
+
uipath/core/guardrails/_evaluators.py,sha256=5llj14Xh1mzREh8JC9LjsymGsuGrz8x6Sv6K5s4P1sE,14446
|
|
20
|
+
uipath/core/guardrails/guardrails.py,sha256=X2EVZVhD2PSXmfudgLdPB9ccsyqfPWESzuOzH9MGCrg,4703
|
|
21
21
|
uipath/core/tracing/__init__.py,sha256=1XNLYZ4J76XkRrizGO486mS6yxzVXUbrldpvxTyJe3E,483
|
|
22
22
|
uipath/core/tracing/_utils.py,sha256=FiCFGOFa4czruhlSF87Q5Q4jX9KKPHZiw8k14K7W5v4,6636
|
|
23
23
|
uipath/core/tracing/decorators.py,sha256=ag_MFwZ0TywrhbpLKqQwF1guvRA9sYiItxao5LN9_Iw,10942
|
|
@@ -25,7 +25,7 @@ uipath/core/tracing/exporters.py,sha256=FClouEEQfk3F8J7G_NFoarDJM3R0-gA5jUxA5xRH
|
|
|
25
25
|
uipath/core/tracing/processors.py,sha256=R_652rtjPmfpUtaXoIcmfZrRZylVXFRNwjOmJUUxOQw,1408
|
|
26
26
|
uipath/core/tracing/span_utils.py,sha256=WYBrd6ZbawAs7r1Js-Zvo9_8GzkD9LhHNOls00bK_xI,12235
|
|
27
27
|
uipath/core/tracing/trace_manager.py,sha256=51rscJcepkTK4bWoCZdE-DFc9wt2F-aSuFBaSXmkHl0,3130
|
|
28
|
-
uipath_core-0.1.
|
|
29
|
-
uipath_core-0.1.
|
|
30
|
-
uipath_core-0.1.
|
|
31
|
-
uipath_core-0.1.
|
|
28
|
+
uipath_core-0.1.4.dist-info/METADATA,sha256=z8rkXBn1096teW-f17J-a5QNegco4Qm02xWVvutjwQQ,938
|
|
29
|
+
uipath_core-0.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
30
|
+
uipath_core-0.1.4.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
|
|
31
|
+
uipath_core-0.1.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|