uipath-core 0.1.2__tar.gz → 0.1.4__tar.gz

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.
Files changed (59) hide show
  1. {uipath_core-0.1.2 → uipath_core-0.1.4}/PKG-INFO +1 -1
  2. {uipath_core-0.1.2 → uipath_core-0.1.4}/pyproject.toml +1 -1
  3. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/guardrails/__init__.py +2 -0
  4. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/guardrails/_evaluators.py +93 -17
  5. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/guardrails/guardrails.py +13 -4
  6. {uipath_core-0.1.2 → uipath_core-0.1.4}/tests/guardrails/test_deterministic_guardrails_service.py +86 -32
  7. {uipath_core-0.1.2 → uipath_core-0.1.4}/uv.lock +1 -1
  8. {uipath_core-0.1.2 → uipath_core-0.1.4}/.cursorrules +0 -0
  9. {uipath_core-0.1.2 → uipath_core-0.1.4}/.editorconfig +0 -0
  10. {uipath_core-0.1.2 → uipath_core-0.1.4}/.gitattributes +0 -0
  11. {uipath_core-0.1.2 → uipath_core-0.1.4}/.github/workflows/cd.yml +0 -0
  12. {uipath_core-0.1.2 → uipath_core-0.1.4}/.github/workflows/ci.yml +0 -0
  13. {uipath_core-0.1.2 → uipath_core-0.1.4}/.github/workflows/commitlint.yml +0 -0
  14. {uipath_core-0.1.2 → uipath_core-0.1.4}/.github/workflows/lint.yml +0 -0
  15. {uipath_core-0.1.2 → uipath_core-0.1.4}/.github/workflows/publish-dev.yml +0 -0
  16. {uipath_core-0.1.2 → uipath_core-0.1.4}/.github/workflows/test.yml +0 -0
  17. {uipath_core-0.1.2 → uipath_core-0.1.4}/.gitignore +0 -0
  18. {uipath_core-0.1.2 → uipath_core-0.1.4}/.pre-commit-config.yaml +0 -0
  19. {uipath_core-0.1.2 → uipath_core-0.1.4}/.python-version +0 -0
  20. {uipath_core-0.1.2 → uipath_core-0.1.4}/.vscode/extensions.json +0 -0
  21. {uipath_core-0.1.2 → uipath_core-0.1.4}/.vscode/launch.json +0 -0
  22. {uipath_core-0.1.2 → uipath_core-0.1.4}/.vscode/settings.json +0 -0
  23. {uipath_core-0.1.2 → uipath_core-0.1.4}/CONTRIBUTING.md +0 -0
  24. {uipath_core-0.1.2 → uipath_core-0.1.4}/LICENSE +0 -0
  25. {uipath_core-0.1.2 → uipath_core-0.1.4}/README.md +0 -0
  26. {uipath_core-0.1.2 → uipath_core-0.1.4}/justfile +0 -0
  27. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/__init__.py +0 -0
  28. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/__init__.py +0 -0
  29. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/async_stream.py +0 -0
  30. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/citation.py +0 -0
  31. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/content.py +0 -0
  32. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/conversation.py +0 -0
  33. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/error.py +0 -0
  34. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/event.py +0 -0
  35. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/exchange.py +0 -0
  36. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/interrupt.py +0 -0
  37. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/message.py +0 -0
  38. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/meta.py +0 -0
  39. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/chat/tool.py +0 -0
  40. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/errors/__init__.py +0 -0
  41. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/errors/errors.py +0 -0
  42. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/guardrails/_deterministic_guardrails_service.py +0 -0
  43. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/py.typed +0 -0
  44. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/tracing/__init__.py +0 -0
  45. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/tracing/_utils.py +0 -0
  46. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/tracing/decorators.py +0 -0
  47. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/tracing/exporters.py +0 -0
  48. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/tracing/processors.py +0 -0
  49. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/tracing/span_utils.py +0 -0
  50. {uipath_core-0.1.2 → uipath_core-0.1.4}/src/uipath/core/tracing/trace_manager.py +0 -0
  51. {uipath_core-0.1.2 → uipath_core-0.1.4}/tests/__init__.py +0 -0
  52. {uipath_core-0.1.2 → uipath_core-0.1.4}/tests/conftest.py +0 -0
  53. {uipath_core-0.1.2 → uipath_core-0.1.4}/tests/tracing/test_external_integration.py +0 -0
  54. {uipath_core-0.1.2 → uipath_core-0.1.4}/tests/tracing/test_serialization.py +0 -0
  55. {uipath_core-0.1.2 → uipath_core-0.1.4}/tests/tracing/test_span_nesting.py +0 -0
  56. {uipath_core-0.1.2 → uipath_core-0.1.4}/tests/tracing/test_span_registry.py +0 -0
  57. {uipath_core-0.1.2 → uipath_core-0.1.4}/tests/tracing/test_trace_manager.py +0 -0
  58. {uipath_core-0.1.2 → uipath_core-0.1.4}/tests/tracing/test_traced.py +0 -0
  59. {uipath_core-0.1.2 → uipath_core-0.1.4}/tests/tracing/test_tracing_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath-core
3
- Version: 0.1.2
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "uipath-core"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "UiPath Core abstractions"
5
5
  readme = { file = "README.md", content-type = "text/markdown" }
6
6
  requires-python = ">=3.11"
@@ -15,6 +15,7 @@ from .guardrails import (
15
15
  FieldSource,
16
16
  GuardrailScope,
17
17
  GuardrailSelector,
18
+ GuardrailValidationResult,
18
19
  NumberRule,
19
20
  Rule,
20
21
  SelectorType,
@@ -41,4 +42,5 @@ __all__ = [
41
42
  "Rule",
42
43
  "GuardrailScope",
43
44
  "GuardrailSelector",
45
+ "GuardrailValidationResult",
44
46
  ]
@@ -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")
@@ -50,7 +50,7 @@ class TestDeterministicGuardrailsService:
50
50
  selector_type="specific",
51
51
  fields=[FieldReference(path="age", source=FieldSource.INPUT)],
52
52
  ),
53
- func=lambda n: n >= 21.0,
53
+ detects_violation=lambda n: n < 21.0,
54
54
  ),
55
55
  BooleanRule(
56
56
  rule_type="boolean",
@@ -60,7 +60,7 @@ class TestDeterministicGuardrailsService:
60
60
  FieldReference(path="isActive", source=FieldSource.INPUT)
61
61
  ],
62
62
  ),
63
- func=lambda b: b is True,
63
+ detects_violation=lambda b: b is not True,
64
64
  ),
65
65
  ],
66
66
  )
@@ -80,7 +80,7 @@ class TestDeterministicGuardrailsService:
80
80
  )
81
81
 
82
82
  assert result.validation_passed is True
83
- assert "All deterministic guardrail rules passed" in result.reason
83
+ assert result.reason == "All deterministic guardrail rules passed"
84
84
 
85
85
  def test_evaluate_post_deterministic_guardrail_validation_failed_age(
86
86
  self,
@@ -103,7 +103,7 @@ class TestDeterministicGuardrailsService:
103
103
  selector_type="specific",
104
104
  fields=[FieldReference(path="age", source=FieldSource.INPUT)],
105
105
  ),
106
- func=lambda n: n >= 21.0,
106
+ detects_violation=lambda n: n < 21.0,
107
107
  ),
108
108
  BooleanRule(
109
109
  rule_type="boolean",
@@ -113,7 +113,7 @@ class TestDeterministicGuardrailsService:
113
113
  FieldReference(path="isActive", source=FieldSource.INPUT)
114
114
  ],
115
115
  ),
116
- func=lambda b: b is True,
116
+ detects_violation=lambda b: b is not True,
117
117
  ),
118
118
  ],
119
119
  )
@@ -133,7 +133,10 @@ class TestDeterministicGuardrailsService:
133
133
  )
134
134
 
135
135
  assert result.validation_passed is False
136
- assert "age" in result.reason.lower()
136
+ assert (
137
+ result.reason
138
+ == "Input data didn't match the guardrail condition: [age] comparing function [(n): n < 21.0]"
139
+ )
137
140
 
138
141
  def test_evaluate_post_deterministic_guardrail_validation_failed_is_active(
139
142
  self,
@@ -156,7 +159,7 @@ class TestDeterministicGuardrailsService:
156
159
  selector_type="specific",
157
160
  fields=[FieldReference(path="age", source=FieldSource.INPUT)],
158
161
  ),
159
- func=lambda n: n >= 21.0,
162
+ detects_violation=lambda n: n < 21.0,
160
163
  ),
161
164
  BooleanRule(
162
165
  rule_type="boolean",
@@ -166,7 +169,7 @@ class TestDeterministicGuardrailsService:
166
169
  FieldReference(path="isActive", source=FieldSource.INPUT)
167
170
  ],
168
171
  ),
169
- func=lambda b: b is True,
172
+ detects_violation=lambda b: b is not True,
170
173
  ),
171
174
  ],
172
175
  )
@@ -186,8 +189,10 @@ class TestDeterministicGuardrailsService:
186
189
  )
187
190
 
188
191
  assert result.validation_passed is False
189
- assert "isActive" in result.reason or "isactive" in result.reason.lower()
190
- assert "comparing function" in result.reason.lower()
192
+ assert (
193
+ result.reason
194
+ == "Input data didn't match the guardrail condition: [isActive] comparing function [(b): b is not True]"
195
+ )
191
196
 
192
197
  def test_evaluate_post_deterministic_guardrail_matches_regex_positive(
193
198
  self,
@@ -212,7 +217,7 @@ class TestDeterministicGuardrailsService:
212
217
  FieldReference(path="userName", source=FieldSource.INPUT)
213
218
  ],
214
219
  ),
215
- func=lambda s: bool(re.search(".*te.*3.*", s)),
220
+ detects_violation=lambda s: not bool(re.search(".*te.*3.*", s)),
216
221
  ),
217
222
  ],
218
223
  )
@@ -230,7 +235,7 @@ class TestDeterministicGuardrailsService:
230
235
  )
231
236
 
232
237
  assert result.validation_passed is True
233
- assert "All deterministic guardrail rules passed" in result.reason
238
+ assert result.reason == "All deterministic guardrail rules passed"
234
239
 
235
240
  def test_evaluate_post_deterministic_guardrail_matches_regex_negative(
236
241
  self,
@@ -255,7 +260,7 @@ class TestDeterministicGuardrailsService:
255
260
  FieldReference(path="userName", source=FieldSource.INPUT)
256
261
  ],
257
262
  ),
258
- func=lambda s: bool(re.search(".*te.*3.*", s)),
263
+ detects_violation=lambda s: not bool(re.search(".*te.*3.*", s)),
259
264
  ),
260
265
  ],
261
266
  )
@@ -273,7 +278,10 @@ class TestDeterministicGuardrailsService:
273
278
  )
274
279
 
275
280
  assert result.validation_passed is False
276
- assert "userName" in result.reason
281
+ assert (
282
+ result.reason
283
+ == 'Input data didn\'t match the guardrail condition: [userName] comparing function [(s): not bool(re.search(".*te.*3.*", s))]'
284
+ )
277
285
 
278
286
  def test_evaluate_post_deterministic_guardrail_word_func_positive(
279
287
  self,
@@ -298,7 +306,7 @@ class TestDeterministicGuardrailsService:
298
306
  FieldReference(path="userName", source=FieldSource.INPUT)
299
307
  ],
300
308
  ),
301
- func=lambda s: len(s) > 5,
309
+ detects_violation=lambda s: len(s) <= 5,
302
310
  ),
303
311
  ],
304
312
  )
@@ -316,7 +324,7 @@ class TestDeterministicGuardrailsService:
316
324
  )
317
325
 
318
326
  assert result.validation_passed is True
319
- assert "All deterministic guardrail rules passed" in result.reason
327
+ assert result.reason == "All deterministic guardrail rules passed"
320
328
 
321
329
  def test_evaluate_post_deterministic_guardrail_word_func_negative(
322
330
  self,
@@ -341,7 +349,7 @@ class TestDeterministicGuardrailsService:
341
349
  FieldReference(path="userName", source=FieldSource.INPUT)
342
350
  ],
343
351
  ),
344
- func=lambda s: len(s) > 5,
352
+ detects_violation=lambda s: len(s) <= 5,
345
353
  ),
346
354
  ],
347
355
  )
@@ -360,6 +368,52 @@ class TestDeterministicGuardrailsService:
360
368
 
361
369
  assert result.validation_passed is False
362
370
 
371
+ def test_evaluate_post_deterministic_guardrail_word_contains_substring_detects_violation(
372
+ self,
373
+ service: DeterministicGuardrailsService,
374
+ ) -> None:
375
+ """Test deterministic guardrail validation fails when string contains forbidden substring."""
376
+ deterministic_guardrail = DeterministicGuardrail(
377
+ id="test-deterministic-id",
378
+ name="Word Contains Guardrail",
379
+ description="Test word contains guardrail",
380
+ enabled_for_evals=True,
381
+ guardrail_type="custom",
382
+ selector=GuardrailSelector(
383
+ scopes=[GuardrailScope.TOOL], match_names=["test"]
384
+ ),
385
+ rules=[
386
+ WordRule(
387
+ rule_type="word",
388
+ field_selector=SpecificFieldsSelector(
389
+ selector_type="specific",
390
+ fields=[
391
+ FieldReference(path="userName", source=FieldSource.INPUT)
392
+ ],
393
+ ),
394
+ detects_violation=lambda s: "dre" in s,
395
+ ),
396
+ ],
397
+ )
398
+
399
+ # Input data with userName that contains "dre" - should fail
400
+ input_data = {
401
+ "userName": "andrei",
402
+ }
403
+ output_data: dict[str, Any] = {}
404
+
405
+ result = service.evaluate_post_deterministic_guardrail(
406
+ input_data=input_data,
407
+ output_data=output_data,
408
+ guardrail=deterministic_guardrail,
409
+ )
410
+
411
+ assert result.validation_passed is False
412
+ assert (
413
+ result.reason
414
+ == 'Input data didn\'t match the guardrail condition: [userName] comparing function [(s): "dre" in s]'
415
+ )
416
+
363
417
  def test_evaluate_post_deterministic_guardrail_number_func_positive(
364
418
  self,
365
419
  service: DeterministicGuardrailsService,
@@ -381,7 +435,7 @@ class TestDeterministicGuardrailsService:
381
435
  selector_type="specific",
382
436
  fields=[FieldReference(path="age", source=FieldSource.INPUT)],
383
437
  ),
384
- func=lambda n: n >= 18 and n <= 65,
438
+ detects_violation=lambda n: n < 18 or n > 65,
385
439
  ),
386
440
  ],
387
441
  )
@@ -399,7 +453,7 @@ class TestDeterministicGuardrailsService:
399
453
  )
400
454
 
401
455
  assert result.validation_passed is True
402
- assert "All deterministic guardrail rules passed" in result.reason
456
+ assert result.reason == "All deterministic guardrail rules passed"
403
457
 
404
458
  def test_evaluate_post_deterministic_guardrail_number_func_negative(
405
459
  self,
@@ -422,7 +476,7 @@ class TestDeterministicGuardrailsService:
422
476
  selector_type="specific",
423
477
  fields=[FieldReference(path="age", source=FieldSource.INPUT)],
424
478
  ),
425
- func=lambda n: n >= 18 and n <= 65,
479
+ detects_violation=lambda n: n < 18 or n > 65,
426
480
  ),
427
481
  ],
428
482
  )
@@ -678,7 +732,7 @@ class TestDeterministicGuardrailsService:
678
732
  NumberRule(
679
733
  rule_type="number",
680
734
  field_selector=AllFieldsSelector(selector_type="all"),
681
- func=lambda n: n == 25.0,
735
+ detects_violation=lambda n: n != 25.0,
682
736
  ),
683
737
  ],
684
738
  )
@@ -720,7 +774,7 @@ class TestDeterministicGuardrailsService:
720
774
  NumberRule(
721
775
  rule_type="number",
722
776
  field_selector=AllFieldsSelector(selector_type="all"),
723
- func=lambda n: n == 200.0,
777
+ detects_violation=lambda n: n != 200.0,
724
778
  ),
725
779
  ],
726
780
  )
@@ -898,7 +952,7 @@ class TestDeterministicGuardrailsService:
898
952
  selector_type="specific",
899
953
  fields=[FieldReference(path="age", source=FieldSource.INPUT)],
900
954
  ),
901
- func=lambda n: n >= 21.0,
955
+ detects_violation=lambda n: n < 21.0,
902
956
  ),
903
957
  BooleanRule(
904
958
  rule_type="boolean",
@@ -908,7 +962,7 @@ class TestDeterministicGuardrailsService:
908
962
  FieldReference(path="isActive", source=FieldSource.INPUT)
909
963
  ],
910
964
  ),
911
- func=lambda b: b is True,
965
+ detects_violation=lambda b: b is not True,
912
966
  ),
913
967
  ],
914
968
  )
@@ -931,7 +985,7 @@ class TestDeterministicGuardrailsService:
931
985
  selector_type="specific",
932
986
  fields=[FieldReference(path="age", source=FieldSource.INPUT)],
933
987
  ),
934
- func=lambda n: n >= 21.0,
988
+ detects_violation=lambda n: n < 21.0,
935
989
  ),
936
990
  NumberRule(
937
991
  rule_type="number",
@@ -941,7 +995,7 @@ class TestDeterministicGuardrailsService:
941
995
  FieldReference(path="status", source=FieldSource.OUTPUT)
942
996
  ],
943
997
  ),
944
- func=lambda n: n == 200.0,
998
+ detects_violation=lambda n: n != 200.0,
945
999
  ),
946
1000
  ],
947
1001
  )
@@ -964,7 +1018,7 @@ class TestDeterministicGuardrailsService:
964
1018
  selector_type="specific",
965
1019
  fields=[FieldReference(path="age", source=FieldSource.INPUT)],
966
1020
  ),
967
- func=lambda n: n >= 21.0,
1021
+ detects_violation=lambda n: n < 21.0,
968
1022
  ),
969
1023
  BooleanRule(
970
1024
  rule_type="boolean",
@@ -974,7 +1028,7 @@ class TestDeterministicGuardrailsService:
974
1028
  FieldReference(path="isActive", source=FieldSource.INPUT)
975
1029
  ],
976
1030
  ),
977
- func=lambda b: b is True,
1031
+ detects_violation=lambda b: b is not True,
978
1032
  ),
979
1033
  NumberRule(
980
1034
  rule_type="number",
@@ -984,7 +1038,7 @@ class TestDeterministicGuardrailsService:
984
1038
  FieldReference(path="status", source=FieldSource.OUTPUT)
985
1039
  ],
986
1040
  ),
987
- func=lambda n: n == 200.0,
1041
+ detects_violation=lambda n: n != 200.0,
988
1042
  ),
989
1043
  ],
990
1044
  )
@@ -1009,7 +1063,7 @@ class TestDeterministicGuardrailsService:
1009
1063
  selector_type="specific",
1010
1064
  fields=[FieldReference(path="age", source=FieldSource.INPUT)],
1011
1065
  ),
1012
- func=lambda n: n >= 21.0,
1066
+ detects_violation=lambda n: n < 21.0,
1013
1067
  ),
1014
1068
  BooleanRule(
1015
1069
  rule_type="boolean",
@@ -1019,7 +1073,7 @@ class TestDeterministicGuardrailsService:
1019
1073
  FieldReference(path="isActive", source=FieldSource.INPUT)
1020
1074
  ],
1021
1075
  ),
1022
- func=lambda b: b is True,
1076
+ detects_violation=lambda b: b is not True,
1023
1077
  ),
1024
1078
  NumberRule(
1025
1079
  rule_type="number",
@@ -1029,7 +1083,7 @@ class TestDeterministicGuardrailsService:
1029
1083
  FieldReference(path="status", source=FieldSource.OUTPUT)
1030
1084
  ],
1031
1085
  ),
1032
- func=lambda n: n == 200.0,
1086
+ detects_violation=lambda n: n != 200.0,
1033
1087
  ),
1034
1088
  ],
1035
1089
  )
@@ -991,7 +991,7 @@ wheels = [
991
991
 
992
992
  [[package]]
993
993
  name = "uipath-core"
994
- version = "0.1.2"
994
+ version = "0.1.4"
995
995
  source = { editable = "." }
996
996
  dependencies = [
997
997
  { name = "opentelemetry-instrumentation" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes