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.
@@ -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
- passed = rule.func(field_str)
193
+ violation_detected = rule.detects_violation(field_str)
192
194
  except Exception:
193
195
  # If function raises an exception, treat as failure
194
- passed = False
196
+ violation_detected = True
195
197
 
196
- if not passed:
197
- reason = format_guardrail_error_message(
198
- field_ref, "comparing function", None
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
- passed = rule.func(field_num)
229
+ violation_detected = rule.detects_violation(field_num)
226
230
  except Exception:
227
231
  # If function raises an exception, treat as failure
228
- passed = False
232
+ violation_detected = True
229
233
 
230
- if not passed:
231
- reason = format_guardrail_error_message(
232
- field_ref, "comparing function", None
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
- passed = rule.func(field_bool)
266
+ violation_detected = rule.detects_violation(field_bool)
261
267
  except Exception:
262
268
  # If function raises an exception, treat as failure
263
- passed = False
269
+ violation_detected = True
264
270
 
265
- if not passed:
266
- reason = format_guardrail_error_message(
267
- field_ref, "comparing function", None
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
- func: Callable[[str], bool] = Field(exclude=True)
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
- func: Callable[[float], bool] = Field(exclude=True)
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
- func: Callable[[bool], bool] = Field(exclude=True)
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="custom")
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")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath-core
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: UiPath Core abstractions
5
5
  Project-URL: Homepage, https://uipath.com
6
6
  Project-URL: Repository, https://github.com/UiPath/uipath-core-python
@@ -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=nIODDFu0IjrgVcf0pshmXV0BNAEU-16-liwJv50Lcy4,11343
20
- uipath/core/guardrails/guardrails.py,sha256=jM_In6kxttkuFoH5KiiE5iREcFAMY2wTxNQkg35ZQqw,4286
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.3.dist-info/METADATA,sha256=L3Hqe-B-OZ9pc2uIK73zSa_xLwBg4568I4RhJ9Fg47I,938
29
- uipath_core-0.1.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
30
- uipath_core-0.1.3.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
31
- uipath_core-0.1.3.dist-info/RECORD,,
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,,