uipath-core 0.1.3__tar.gz → 0.1.5__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.
- {uipath_core-0.1.3 → uipath_core-0.1.5}/PKG-INFO +1 -1
- {uipath_core-0.1.3 → uipath_core-0.1.5}/pyproject.toml +1 -1
- uipath_core-0.1.5/src/uipath/core/guardrails/_deterministic_guardrails_service.py +142 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/guardrails/_evaluators.py +108 -30
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/guardrails/guardrails.py +14 -4
- {uipath_core-0.1.3 → uipath_core-0.1.5}/tests/guardrails/test_deterministic_guardrails_service.py +446 -44
- {uipath_core-0.1.3 → uipath_core-0.1.5}/uv.lock +1 -1
- uipath_core-0.1.3/src/uipath/core/guardrails/_deterministic_guardrails_service.py +0 -79
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.cursorrules +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.editorconfig +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.gitattributes +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.github/workflows/cd.yml +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.github/workflows/ci.yml +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.github/workflows/commitlint.yml +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.github/workflows/lint.yml +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.github/workflows/publish-dev.yml +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.github/workflows/test.yml +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.gitignore +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.pre-commit-config.yaml +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.python-version +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.vscode/extensions.json +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.vscode/launch.json +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/.vscode/settings.json +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/CONTRIBUTING.md +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/LICENSE +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/README.md +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/justfile +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/__init__.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/__init__.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/async_stream.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/citation.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/content.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/conversation.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/error.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/event.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/exchange.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/interrupt.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/message.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/meta.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/chat/tool.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/errors/__init__.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/errors/errors.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/guardrails/__init__.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/py.typed +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/tracing/__init__.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/tracing/_utils.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/tracing/decorators.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/tracing/exporters.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/tracing/processors.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/tracing/span_utils.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/src/uipath/core/tracing/trace_manager.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/tests/__init__.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/tests/conftest.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/tests/tracing/test_external_integration.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/tests/tracing/test_serialization.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/tests/tracing/test_span_nesting.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/tests/tracing/test_span_registry.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/tests/tracing/test_trace_manager.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/tests/tracing/test_traced.py +0 -0
- {uipath_core-0.1.3 → uipath_core-0.1.5}/tests/tracing/test_tracing_utils.py +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from ..tracing.decorators import traced
|
|
6
|
+
from ._evaluators import (
|
|
7
|
+
evaluate_boolean_rule,
|
|
8
|
+
evaluate_number_rule,
|
|
9
|
+
evaluate_universal_rule,
|
|
10
|
+
evaluate_word_rule,
|
|
11
|
+
)
|
|
12
|
+
from .guardrails import (
|
|
13
|
+
AllFieldsSelector,
|
|
14
|
+
ApplyTo,
|
|
15
|
+
BooleanRule,
|
|
16
|
+
DeterministicGuardrail,
|
|
17
|
+
FieldSource,
|
|
18
|
+
GuardrailValidationResult,
|
|
19
|
+
NumberRule,
|
|
20
|
+
SpecificFieldsSelector,
|
|
21
|
+
UniversalRule,
|
|
22
|
+
WordRule,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DeterministicGuardrailsService(BaseModel):
|
|
27
|
+
@traced("evaluate_pre_deterministic_guardrail", run_type="uipath")
|
|
28
|
+
def evaluate_pre_deterministic_guardrail(
|
|
29
|
+
self,
|
|
30
|
+
input_data: dict[str, Any],
|
|
31
|
+
guardrail: DeterministicGuardrail,
|
|
32
|
+
) -> GuardrailValidationResult:
|
|
33
|
+
"""Evaluate deterministic guardrail rules against input data (pre-execution)."""
|
|
34
|
+
# Check if guardrail contains any output-dependent rules
|
|
35
|
+
has_output_rule = self._has_output_dependent_rule(guardrail, [ApplyTo.OUTPUT])
|
|
36
|
+
|
|
37
|
+
# If guardrail has output-dependent rules, skip evaluation in pre-execution
|
|
38
|
+
# Output rules will be evaluated during post-execution
|
|
39
|
+
if has_output_rule:
|
|
40
|
+
return GuardrailValidationResult(
|
|
41
|
+
validation_passed=True,
|
|
42
|
+
reason="Guardrail contains output-dependent rules that will be evaluated during post-execution",
|
|
43
|
+
)
|
|
44
|
+
return self._evaluate_deterministic_guardrail(
|
|
45
|
+
input_data=input_data,
|
|
46
|
+
output_data={},
|
|
47
|
+
guardrail=guardrail,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@traced("evaluate_post_deterministic_guardrails", run_type="uipath")
|
|
51
|
+
def evaluate_post_deterministic_guardrail(
|
|
52
|
+
self,
|
|
53
|
+
input_data: dict[str, Any],
|
|
54
|
+
output_data: dict[str, Any],
|
|
55
|
+
guardrail: DeterministicGuardrail,
|
|
56
|
+
) -> GuardrailValidationResult:
|
|
57
|
+
"""Evaluate deterministic guardrail rules against input and output data."""
|
|
58
|
+
# Check if guardrail contains any output-dependent rules
|
|
59
|
+
has_output_rule = self._has_output_dependent_rule(
|
|
60
|
+
guardrail, [ApplyTo.OUTPUT, ApplyTo.INPUT_AND_OUTPUT]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# If guardrail has no output-dependent rules, skip post-execution evaluation
|
|
64
|
+
# Only input rules exist and they should have been evaluated during pre-execution
|
|
65
|
+
if not has_output_rule:
|
|
66
|
+
return GuardrailValidationResult(
|
|
67
|
+
validation_passed=True,
|
|
68
|
+
reason="Guardrail contains only input-dependent rules that were evaluated during pre-execution",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return self._evaluate_deterministic_guardrail(
|
|
72
|
+
input_data=input_data,
|
|
73
|
+
output_data=output_data,
|
|
74
|
+
guardrail=guardrail,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def _has_output_dependent_rule(
|
|
79
|
+
guardrail: DeterministicGuardrail,
|
|
80
|
+
universal_rules_apply_to_values: list[ApplyTo],
|
|
81
|
+
) -> bool:
|
|
82
|
+
"""Check if at least one rule EXCLUSIVELY requires output data.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
guardrail: The guardrail to check
|
|
86
|
+
universal_rules_apply_to_values: List of ApplyTo values to consider as output-dependent for UniversalRules.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
True if at least one rule exclusively depends on output data, False otherwise.
|
|
90
|
+
"""
|
|
91
|
+
for rule in guardrail.rules:
|
|
92
|
+
# UniversalRule: only return True if it applies to values in universal_rules_apply_to_values
|
|
93
|
+
if isinstance(rule, UniversalRule):
|
|
94
|
+
if rule.apply_to in universal_rules_apply_to_values:
|
|
95
|
+
return True
|
|
96
|
+
# Rules with field_selector
|
|
97
|
+
elif isinstance(rule, (WordRule, NumberRule, BooleanRule)):
|
|
98
|
+
field_selector = rule.field_selector
|
|
99
|
+
# AllFieldsSelector applies to both input and output, not exclusively output
|
|
100
|
+
# SpecificFieldsSelector: only return True if at least one field has OUTPUT source
|
|
101
|
+
if isinstance(field_selector, SpecificFieldsSelector):
|
|
102
|
+
if field_selector.fields and any(
|
|
103
|
+
field.source == FieldSource.OUTPUT
|
|
104
|
+
for field in field_selector.fields
|
|
105
|
+
):
|
|
106
|
+
return True
|
|
107
|
+
elif isinstance(field_selector, AllFieldsSelector):
|
|
108
|
+
if FieldSource.OUTPUT in field_selector.sources:
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def _evaluate_deterministic_guardrail(
|
|
115
|
+
input_data: dict[str, Any],
|
|
116
|
+
output_data: dict[str, Any],
|
|
117
|
+
guardrail: DeterministicGuardrail,
|
|
118
|
+
) -> GuardrailValidationResult:
|
|
119
|
+
"""Evaluate deterministic guardrail rules against input and output data."""
|
|
120
|
+
for rule in guardrail.rules:
|
|
121
|
+
if isinstance(rule, WordRule):
|
|
122
|
+
passed, reason = evaluate_word_rule(rule, input_data, output_data)
|
|
123
|
+
elif isinstance(rule, NumberRule):
|
|
124
|
+
passed, reason = evaluate_number_rule(rule, input_data, output_data)
|
|
125
|
+
elif isinstance(rule, BooleanRule):
|
|
126
|
+
passed, reason = evaluate_boolean_rule(rule, input_data, output_data)
|
|
127
|
+
elif isinstance(rule, UniversalRule):
|
|
128
|
+
passed, reason = evaluate_universal_rule(rule, output_data)
|
|
129
|
+
else:
|
|
130
|
+
return GuardrailValidationResult(
|
|
131
|
+
validation_passed=False,
|
|
132
|
+
reason=f"Unknown rule type: {type(rule)}",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if not passed:
|
|
136
|
+
return GuardrailValidationResult(
|
|
137
|
+
validation_passed=False, reason=reason or "Rule validation failed"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
return GuardrailValidationResult(
|
|
141
|
+
validation_passed=True, reason="All deterministic guardrail rules passed"
|
|
142
|
+
)
|
|
@@ -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,
|
|
@@ -119,23 +120,25 @@ def get_fields_from_selector(
|
|
|
119
120
|
fields: list[tuple[Any, FieldReference]] = []
|
|
120
121
|
|
|
121
122
|
if isinstance(field_selector, AllFieldsSelector):
|
|
122
|
-
# For "all" selector, we need to collect all fields from
|
|
123
|
+
# For "all" selector, we need to collect all fields from the specified sources
|
|
123
124
|
# This is a simplified implementation - in practice, you might want to
|
|
124
125
|
# recursively collect all nested fields
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
(
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
if FieldSource.INPUT in field_selector.sources:
|
|
127
|
+
for key, value in input_data.items():
|
|
128
|
+
fields.append(
|
|
129
|
+
(
|
|
130
|
+
value,
|
|
131
|
+
FieldReference(path=key, source=FieldSource.INPUT),
|
|
132
|
+
)
|
|
130
133
|
)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
if FieldSource.OUTPUT in field_selector.sources:
|
|
135
|
+
for key, value in output_data.items():
|
|
136
|
+
fields.append(
|
|
137
|
+
(
|
|
138
|
+
value,
|
|
139
|
+
FieldReference(path=key, source=FieldSource.OUTPUT),
|
|
140
|
+
)
|
|
137
141
|
)
|
|
138
|
-
)
|
|
139
142
|
elif isinstance(field_selector, SpecificFieldsSelector):
|
|
140
143
|
# For specific fields, extract values based on field references
|
|
141
144
|
for field_ref in field_selector.fields:
|
|
@@ -163,7 +166,7 @@ def format_guardrail_error_message(
|
|
|
163
166
|
) -> str:
|
|
164
167
|
"""Format a guardrail error message following the standard pattern."""
|
|
165
168
|
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}"
|
|
169
|
+
message = f"{source} data didn't match the guardrail condition: [{field_ref.path}] comparing function [{operator}]"
|
|
167
170
|
if expected_value and expected_value.strip():
|
|
168
171
|
message += f" [{expected_value.strip()}]"
|
|
169
172
|
return message
|
|
@@ -187,16 +190,18 @@ def evaluate_word_rule(
|
|
|
187
190
|
field_str = field_value
|
|
188
191
|
|
|
189
192
|
# Use the custom function to evaluate the rule
|
|
193
|
+
# If detects_violation returns True, it means the rule was violated (validation fails)
|
|
190
194
|
try:
|
|
191
|
-
|
|
195
|
+
violation_detected = rule.detects_violation(field_str)
|
|
192
196
|
except Exception:
|
|
193
197
|
# If function raises an exception, treat as failure
|
|
194
|
-
|
|
198
|
+
violation_detected = True
|
|
195
199
|
|
|
196
|
-
if
|
|
197
|
-
|
|
198
|
-
|
|
200
|
+
if violation_detected:
|
|
201
|
+
operator = (
|
|
202
|
+
_humanize_guardrail_func(rule.detects_violation) or "violation check"
|
|
199
203
|
)
|
|
204
|
+
reason = format_guardrail_error_message(field_ref, operator, None)
|
|
200
205
|
return False, reason
|
|
201
206
|
|
|
202
207
|
return True, "All word rule validations passed"
|
|
@@ -221,16 +226,18 @@ def evaluate_number_rule(
|
|
|
221
226
|
field_num = float(field_value)
|
|
222
227
|
|
|
223
228
|
# Use the custom function to evaluate the rule
|
|
229
|
+
# If detects_violation returns True, it means the rule was violated (validation fails)
|
|
224
230
|
try:
|
|
225
|
-
|
|
231
|
+
violation_detected = rule.detects_violation(field_num)
|
|
226
232
|
except Exception:
|
|
227
233
|
# If function raises an exception, treat as failure
|
|
228
|
-
|
|
234
|
+
violation_detected = True
|
|
229
235
|
|
|
230
|
-
if
|
|
231
|
-
|
|
232
|
-
|
|
236
|
+
if violation_detected:
|
|
237
|
+
operator = (
|
|
238
|
+
_humanize_guardrail_func(rule.detects_violation) or "violation check"
|
|
233
239
|
)
|
|
240
|
+
reason = format_guardrail_error_message(field_ref, operator, None)
|
|
234
241
|
return False, reason
|
|
235
242
|
|
|
236
243
|
return True, "All number rule validations passed"
|
|
@@ -256,16 +263,18 @@ def evaluate_boolean_rule(
|
|
|
256
263
|
field_bool = field_value
|
|
257
264
|
|
|
258
265
|
# Use the custom function to evaluate the rule
|
|
266
|
+
# If detects_violation returns True, it means the rule was violated (validation fails)
|
|
259
267
|
try:
|
|
260
|
-
|
|
268
|
+
violation_detected = rule.detects_violation(field_bool)
|
|
261
269
|
except Exception:
|
|
262
270
|
# If function raises an exception, treat as failure
|
|
263
|
-
|
|
271
|
+
violation_detected = True
|
|
264
272
|
|
|
265
|
-
if
|
|
266
|
-
|
|
267
|
-
|
|
273
|
+
if violation_detected:
|
|
274
|
+
operator = (
|
|
275
|
+
_humanize_guardrail_func(rule.detects_violation) or "violation check"
|
|
268
276
|
)
|
|
277
|
+
reason = format_guardrail_error_message(field_ref, operator, None)
|
|
269
278
|
return False, reason
|
|
270
279
|
|
|
271
280
|
return True, "All boolean rule validations passed"
|
|
@@ -307,3 +316,72 @@ def evaluate_universal_rule(
|
|
|
307
316
|
return False, "Universal rule validation triggered (input and output)"
|
|
308
317
|
else:
|
|
309
318
|
return False, f"Unknown apply_to value: {rule.apply_to}"
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _humanize_guardrail_func(func: Callable[..., Any] | str | None) -> str | None:
|
|
322
|
+
"""Build a user-friendly description of a guardrail predicate.
|
|
323
|
+
|
|
324
|
+
Deterministic guardrails store Python callables (often lambdas) to evaluate
|
|
325
|
+
conditions. For diagnostics, it's useful to include a readable hint about the
|
|
326
|
+
predicate that failed.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
func: A Python callable used as a predicate, or a pre-rendered string
|
|
330
|
+
description (for example, ``"s:str -> bool: contains 'test'"``).
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
A human-readable description, or ``None`` if one cannot be produced.
|
|
334
|
+
"""
|
|
335
|
+
if func is None:
|
|
336
|
+
return None
|
|
337
|
+
|
|
338
|
+
if isinstance(func, str):
|
|
339
|
+
rendered = func.strip()
|
|
340
|
+
return rendered or None
|
|
341
|
+
|
|
342
|
+
name = getattr(func, "__name__", None)
|
|
343
|
+
if name and name != "<lambda>":
|
|
344
|
+
return name
|
|
345
|
+
|
|
346
|
+
# Best-effort extraction for lambdas / callables.
|
|
347
|
+
try:
|
|
348
|
+
sig = str(inspect.signature(func))
|
|
349
|
+
except (TypeError, ValueError):
|
|
350
|
+
sig = ""
|
|
351
|
+
|
|
352
|
+
try:
|
|
353
|
+
source_lines = inspect.getsourcelines(func)
|
|
354
|
+
source = "".join(source_lines[0]).strip()
|
|
355
|
+
# Collapse whitespace to keep the message compact.
|
|
356
|
+
source = " ".join(source.split())
|
|
357
|
+
|
|
358
|
+
# Remove "detects_violation=lambda" prefix if present
|
|
359
|
+
# Pattern: "detects_violation=lambda s: condition" -> "condition"
|
|
360
|
+
if "detects_violation=lambda" in source:
|
|
361
|
+
# Find the lambda part
|
|
362
|
+
lambda_start = source.find("detects_violation=lambda")
|
|
363
|
+
if lambda_start != -1:
|
|
364
|
+
# Get everything after "detects_violation=lambda"
|
|
365
|
+
lambda_part = source[
|
|
366
|
+
lambda_start + len("detects_violation=lambda") :
|
|
367
|
+
].strip()
|
|
368
|
+
# Find the colon that separates param from body
|
|
369
|
+
colon_idx = lambda_part.find(":")
|
|
370
|
+
if colon_idx != -1:
|
|
371
|
+
# Extract just the body (condition)
|
|
372
|
+
body = lambda_part[colon_idx + 1 :].strip()
|
|
373
|
+
# Remove trailing comma if present
|
|
374
|
+
body = body.rstrip(",").strip()
|
|
375
|
+
source = body
|
|
376
|
+
except (OSError, TypeError):
|
|
377
|
+
source = ""
|
|
378
|
+
|
|
379
|
+
if source and sig:
|
|
380
|
+
return f"{sig}: {source}"
|
|
381
|
+
if source:
|
|
382
|
+
return source
|
|
383
|
+
if sig:
|
|
384
|
+
return sig
|
|
385
|
+
|
|
386
|
+
rendered = repr(func).strip()
|
|
387
|
+
return rendered or None
|
|
@@ -59,6 +59,7 @@ class AllFieldsSelector(BaseModel):
|
|
|
59
59
|
"""All fields selector."""
|
|
60
60
|
|
|
61
61
|
selector_type: Literal["all"] = Field(alias="$selectorType")
|
|
62
|
+
sources: list[FieldSource]
|
|
62
63
|
|
|
63
64
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
64
65
|
|
|
@@ -92,7 +93,10 @@ class WordRule(BaseModel):
|
|
|
92
93
|
|
|
93
94
|
rule_type: Literal["word"] = Field(alias="$ruleType")
|
|
94
95
|
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
95
|
-
|
|
96
|
+
detects_violation: Callable[[str], bool] = Field(
|
|
97
|
+
exclude=True,
|
|
98
|
+
description="Function that returns True if the string violates the rule (validation should fail).",
|
|
99
|
+
)
|
|
96
100
|
|
|
97
101
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
98
102
|
|
|
@@ -111,7 +115,10 @@ class NumberRule(BaseModel):
|
|
|
111
115
|
|
|
112
116
|
rule_type: Literal["number"] = Field(alias="$ruleType")
|
|
113
117
|
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
114
|
-
|
|
118
|
+
detects_violation: Callable[[float], bool] = Field(
|
|
119
|
+
exclude=True,
|
|
120
|
+
description="Function that returns True if the number violates the rule (validation should fail).",
|
|
121
|
+
)
|
|
115
122
|
|
|
116
123
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
117
124
|
|
|
@@ -121,7 +128,10 @@ class BooleanRule(BaseModel):
|
|
|
121
128
|
|
|
122
129
|
rule_type: Literal["boolean"] = Field(alias="$ruleType")
|
|
123
130
|
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
124
|
-
|
|
131
|
+
detects_violation: Callable[[bool], bool] = Field(
|
|
132
|
+
exclude=True,
|
|
133
|
+
description="Function that returns True if the boolean violates the rule (validation should fail).",
|
|
134
|
+
)
|
|
125
135
|
|
|
126
136
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
127
137
|
|
|
@@ -164,7 +174,7 @@ class BaseGuardrail(BaseModel):
|
|
|
164
174
|
class DeterministicGuardrail(BaseGuardrail):
|
|
165
175
|
"""Deterministic guardrail model."""
|
|
166
176
|
|
|
167
|
-
guardrail_type: Literal["custom"] = Field(alias="
|
|
177
|
+
guardrail_type: Literal["custom"] = Field(alias="$guardrailType")
|
|
168
178
|
rules: list[Rule]
|
|
169
179
|
|
|
170
180
|
model_config = ConfigDict(populate_by_name=True, extra="allow")
|