uipath-core 0.1.1__py3-none-any.whl → 0.1.2__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/__init__.py +44 -0
- uipath/core/guardrails/_deterministic_guardrails_service.py +79 -0
- uipath/core/guardrails/_evaluators.py +309 -0
- uipath/core/guardrails/guardrails.py +170 -0
- {uipath_core-0.1.1.dist-info → uipath_core-0.1.2.dist-info}/METADATA +1 -1
- {uipath_core-0.1.1.dist-info → uipath_core-0.1.2.dist-info}/RECORD +8 -4
- {uipath_core-0.1.1.dist-info → uipath_core-0.1.2.dist-info}/WHEEL +0 -0
- {uipath_core-0.1.1.dist-info → uipath_core-0.1.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""UiPath Guardrails Models.
|
|
2
|
+
|
|
3
|
+
This module contains models related to UiPath Guardrails.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ._deterministic_guardrails_service import DeterministicGuardrailsService
|
|
7
|
+
from .guardrails import (
|
|
8
|
+
AllFieldsSelector,
|
|
9
|
+
ApplyTo,
|
|
10
|
+
BaseGuardrail,
|
|
11
|
+
BooleanRule,
|
|
12
|
+
DeterministicGuardrail,
|
|
13
|
+
FieldReference,
|
|
14
|
+
FieldSelector,
|
|
15
|
+
FieldSource,
|
|
16
|
+
GuardrailScope,
|
|
17
|
+
GuardrailSelector,
|
|
18
|
+
NumberRule,
|
|
19
|
+
Rule,
|
|
20
|
+
SelectorType,
|
|
21
|
+
SpecificFieldsSelector,
|
|
22
|
+
UniversalRule,
|
|
23
|
+
WordRule,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"DeterministicGuardrailsService",
|
|
28
|
+
"FieldSource",
|
|
29
|
+
"ApplyTo",
|
|
30
|
+
"FieldReference",
|
|
31
|
+
"SelectorType",
|
|
32
|
+
"AllFieldsSelector",
|
|
33
|
+
"SpecificFieldsSelector",
|
|
34
|
+
"FieldSelector",
|
|
35
|
+
"BaseGuardrail",
|
|
36
|
+
"DeterministicGuardrail",
|
|
37
|
+
"WordRule",
|
|
38
|
+
"NumberRule",
|
|
39
|
+
"BooleanRule",
|
|
40
|
+
"UniversalRule",
|
|
41
|
+
"Rule",
|
|
42
|
+
"GuardrailScope",
|
|
43
|
+
"GuardrailSelector",
|
|
44
|
+
]
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
BooleanRule,
|
|
14
|
+
DeterministicGuardrail,
|
|
15
|
+
GuardrailValidationResult,
|
|
16
|
+
NumberRule,
|
|
17
|
+
UniversalRule,
|
|
18
|
+
WordRule,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DeterministicGuardrailsService(BaseModel):
|
|
23
|
+
@traced("evaluate_pre_deterministic_guardrail", run_type="uipath")
|
|
24
|
+
def evaluate_pre_deterministic_guardrail(
|
|
25
|
+
self,
|
|
26
|
+
input_data: dict[str, Any],
|
|
27
|
+
guardrail: DeterministicGuardrail,
|
|
28
|
+
) -> GuardrailValidationResult:
|
|
29
|
+
"""Evaluate deterministic guardrail rules against input data (pre-execution)."""
|
|
30
|
+
return self._evaluate_deterministic_guardrail(
|
|
31
|
+
input_data=input_data,
|
|
32
|
+
output_data={},
|
|
33
|
+
guardrail=guardrail,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
@traced("evaluate_post_deterministic_guardrails", run_type="uipath")
|
|
37
|
+
def evaluate_post_deterministic_guardrail(
|
|
38
|
+
self,
|
|
39
|
+
input_data: dict[str, Any],
|
|
40
|
+
output_data: dict[str, Any],
|
|
41
|
+
guardrail: DeterministicGuardrail,
|
|
42
|
+
) -> GuardrailValidationResult:
|
|
43
|
+
"""Evaluate deterministic guardrail rules against input and output data."""
|
|
44
|
+
return self._evaluate_deterministic_guardrail(
|
|
45
|
+
input_data=input_data,
|
|
46
|
+
output_data=output_data,
|
|
47
|
+
guardrail=guardrail,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def _evaluate_deterministic_guardrail(
|
|
52
|
+
input_data: dict[str, Any],
|
|
53
|
+
output_data: dict[str, Any],
|
|
54
|
+
guardrail: DeterministicGuardrail,
|
|
55
|
+
) -> GuardrailValidationResult:
|
|
56
|
+
"""Evaluate deterministic guardrail rules against input and output data."""
|
|
57
|
+
for rule in guardrail.rules:
|
|
58
|
+
if isinstance(rule, WordRule):
|
|
59
|
+
passed, reason = evaluate_word_rule(rule, input_data, output_data)
|
|
60
|
+
elif isinstance(rule, NumberRule):
|
|
61
|
+
passed, reason = evaluate_number_rule(rule, input_data, output_data)
|
|
62
|
+
elif isinstance(rule, BooleanRule):
|
|
63
|
+
passed, reason = evaluate_boolean_rule(rule, input_data, output_data)
|
|
64
|
+
elif isinstance(rule, UniversalRule):
|
|
65
|
+
passed, reason = evaluate_universal_rule(rule, output_data)
|
|
66
|
+
else:
|
|
67
|
+
return GuardrailValidationResult(
|
|
68
|
+
validation_passed=False,
|
|
69
|
+
reason=f"Unknown rule type: {type(rule)}",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if not passed:
|
|
73
|
+
return GuardrailValidationResult(
|
|
74
|
+
validation_passed=False, reason=reason or "Rule validation failed"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return GuardrailValidationResult(
|
|
78
|
+
validation_passed=True, reason="All deterministic guardrail rules passed"
|
|
79
|
+
)
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""Guardrail rule evaluators.
|
|
2
|
+
|
|
3
|
+
This module provides functions for evaluating different types of guardrail rules
|
|
4
|
+
against input and output data.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from enum import IntEnum
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from .guardrails import (
|
|
11
|
+
AllFieldsSelector,
|
|
12
|
+
ApplyTo,
|
|
13
|
+
BooleanRule,
|
|
14
|
+
FieldReference,
|
|
15
|
+
FieldSource,
|
|
16
|
+
NumberRule,
|
|
17
|
+
SpecificFieldsSelector,
|
|
18
|
+
UniversalRule,
|
|
19
|
+
WordRule,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ArrayDepth(IntEnum):
|
|
24
|
+
"""Array depth enumeration for path parsing."""
|
|
25
|
+
|
|
26
|
+
NONE = 0 # Not an array
|
|
27
|
+
SINGLE = 1 # Single array [*]
|
|
28
|
+
MATRIX = 2 # Matrix [*][*]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def extract_field_value(path: str, data: dict[str, Any]) -> list[Any]:
|
|
32
|
+
"""Extract field values from data using dot-notation path.
|
|
33
|
+
|
|
34
|
+
Supports array notation with [*] and [*][*] for arrays and matrices.
|
|
35
|
+
If an array is encountered at any point in the path, all elements are checked.
|
|
36
|
+
"""
|
|
37
|
+
if not isinstance(data, dict):
|
|
38
|
+
return []
|
|
39
|
+
|
|
40
|
+
results: list[Any] = []
|
|
41
|
+
|
|
42
|
+
def _parse_path_segment(segment: str) -> tuple[str, ArrayDepth]:
|
|
43
|
+
"""Parse a path segment to extract field name and array depth."""
|
|
44
|
+
if "[*][*]" in segment:
|
|
45
|
+
field_name = segment.replace("[*][*]", "")
|
|
46
|
+
return field_name, ArrayDepth.MATRIX
|
|
47
|
+
elif "[*]" in segment:
|
|
48
|
+
field_name = segment.replace("[*]", "")
|
|
49
|
+
return field_name, ArrayDepth.SINGLE
|
|
50
|
+
else:
|
|
51
|
+
return segment, ArrayDepth.NONE
|
|
52
|
+
|
|
53
|
+
def _traverse(current: Any, remaining_parts: list[str]) -> None:
|
|
54
|
+
"""Recursively traverse the path, handling arrays and matrices."""
|
|
55
|
+
if not remaining_parts:
|
|
56
|
+
# End of path, add current value
|
|
57
|
+
if current is not None:
|
|
58
|
+
if isinstance(current, list):
|
|
59
|
+
# If current is a list, add all elements
|
|
60
|
+
results.extend(current)
|
|
61
|
+
else:
|
|
62
|
+
results.append(current)
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
part = remaining_parts[0]
|
|
66
|
+
next_parts = remaining_parts[1:]
|
|
67
|
+
field_name, array_depth = _parse_path_segment(part)
|
|
68
|
+
|
|
69
|
+
if isinstance(current, dict):
|
|
70
|
+
if field_name not in current:
|
|
71
|
+
return
|
|
72
|
+
next_value = current.get(field_name)
|
|
73
|
+
|
|
74
|
+
if array_depth == ArrayDepth.MATRIX:
|
|
75
|
+
# Matrix [*][*] - expect 2D array
|
|
76
|
+
if isinstance(next_value, list):
|
|
77
|
+
for row in next_value:
|
|
78
|
+
if isinstance(row, list):
|
|
79
|
+
for item in row:
|
|
80
|
+
_traverse(item, next_parts)
|
|
81
|
+
else:
|
|
82
|
+
# Not a 2D array, treat as 1D
|
|
83
|
+
_traverse(row, next_parts)
|
|
84
|
+
elif array_depth == ArrayDepth.SINGLE:
|
|
85
|
+
# Array [*] - expect 1D array
|
|
86
|
+
if isinstance(next_value, list):
|
|
87
|
+
for item in next_value:
|
|
88
|
+
_traverse(item, next_parts)
|
|
89
|
+
else:
|
|
90
|
+
# Not an array, but path expects one - continue traversal
|
|
91
|
+
_traverse(next_value, next_parts)
|
|
92
|
+
else:
|
|
93
|
+
# No array notation, continue traversal
|
|
94
|
+
if isinstance(next_value, list):
|
|
95
|
+
# Array encountered without notation - check all elements
|
|
96
|
+
for item in next_value:
|
|
97
|
+
_traverse(item, next_parts)
|
|
98
|
+
else:
|
|
99
|
+
_traverse(next_value, next_parts)
|
|
100
|
+
elif isinstance(current, list):
|
|
101
|
+
# Current is an array - check all elements
|
|
102
|
+
for item in current:
|
|
103
|
+
_traverse(item, remaining_parts)
|
|
104
|
+
else:
|
|
105
|
+
# Cannot traverse further
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
path_parts = path.split(".")
|
|
109
|
+
_traverse(data, path_parts)
|
|
110
|
+
return results
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_fields_from_selector(
|
|
114
|
+
field_selector: AllFieldsSelector | SpecificFieldsSelector,
|
|
115
|
+
input_data: dict[str, Any],
|
|
116
|
+
output_data: dict[str, Any],
|
|
117
|
+
) -> list[tuple[Any, FieldReference]]:
|
|
118
|
+
"""Get field values and their references based on the field selector."""
|
|
119
|
+
fields: list[tuple[Any, FieldReference]] = []
|
|
120
|
+
|
|
121
|
+
if isinstance(field_selector, AllFieldsSelector):
|
|
122
|
+
# For "all" selector, we need to collect all fields from both input and output
|
|
123
|
+
# This is a simplified implementation - in practice, you might want to
|
|
124
|
+
# recursively collect all nested fields
|
|
125
|
+
for key, value in input_data.items():
|
|
126
|
+
fields.append(
|
|
127
|
+
(
|
|
128
|
+
value,
|
|
129
|
+
FieldReference(path=key, source=FieldSource.INPUT),
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
for key, value in output_data.items():
|
|
133
|
+
fields.append(
|
|
134
|
+
(
|
|
135
|
+
value,
|
|
136
|
+
FieldReference(path=key, source=FieldSource.OUTPUT),
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
elif isinstance(field_selector, SpecificFieldsSelector):
|
|
140
|
+
# For specific fields, extract values based on field references
|
|
141
|
+
for field_ref in field_selector.fields:
|
|
142
|
+
# Use FieldSource to determine whether to use input_data or output_data
|
|
143
|
+
if field_ref.source == FieldSource.INPUT:
|
|
144
|
+
data = input_data
|
|
145
|
+
elif field_ref.source == FieldSource.OUTPUT:
|
|
146
|
+
data = output_data
|
|
147
|
+
else:
|
|
148
|
+
# Unknown source, skip this field
|
|
149
|
+
continue
|
|
150
|
+
# Extract values (may return multiple if arrays are in the path)
|
|
151
|
+
values = extract_field_value(field_ref.path, data)
|
|
152
|
+
# Add each value as a separate field reference
|
|
153
|
+
for value in values:
|
|
154
|
+
fields.append((value, field_ref))
|
|
155
|
+
|
|
156
|
+
return fields
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def format_guardrail_error_message(
|
|
160
|
+
field_ref: FieldReference,
|
|
161
|
+
operator: str,
|
|
162
|
+
expected_value: str | None = None,
|
|
163
|
+
) -> str:
|
|
164
|
+
"""Format a guardrail error message following the standard pattern."""
|
|
165
|
+
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
|
+
if expected_value and expected_value.strip():
|
|
168
|
+
message += f" [{expected_value.strip()}]"
|
|
169
|
+
return message
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def evaluate_word_rule(
|
|
173
|
+
rule: WordRule, input_data: dict[str, Any], output_data: dict[str, Any]
|
|
174
|
+
) -> tuple[bool, str | None]:
|
|
175
|
+
"""Evaluate a word rule against input and output data."""
|
|
176
|
+
fields = get_fields_from_selector(rule.field_selector, input_data, output_data)
|
|
177
|
+
|
|
178
|
+
for field_value, field_ref in fields:
|
|
179
|
+
if field_value is None:
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
# Word rules should only be applied to string values
|
|
183
|
+
# Skip non-string values (numbers, booleans, objects, arrays, etc.)
|
|
184
|
+
if not isinstance(field_value, str):
|
|
185
|
+
continue
|
|
186
|
+
|
|
187
|
+
field_str = field_value
|
|
188
|
+
|
|
189
|
+
# Use the custom function to evaluate the rule
|
|
190
|
+
try:
|
|
191
|
+
passed = rule.func(field_str)
|
|
192
|
+
except Exception:
|
|
193
|
+
# If function raises an exception, treat as failure
|
|
194
|
+
passed = False
|
|
195
|
+
|
|
196
|
+
if not passed:
|
|
197
|
+
reason = format_guardrail_error_message(
|
|
198
|
+
field_ref, "comparing function", None
|
|
199
|
+
)
|
|
200
|
+
return False, reason
|
|
201
|
+
|
|
202
|
+
return True, "All word rule validations passed"
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def evaluate_number_rule(
|
|
206
|
+
rule: NumberRule, input_data: dict[str, Any], output_data: dict[str, Any]
|
|
207
|
+
) -> tuple[bool, str | None]:
|
|
208
|
+
"""Evaluate a number rule against input and output data."""
|
|
209
|
+
fields = get_fields_from_selector(rule.field_selector, input_data, output_data)
|
|
210
|
+
|
|
211
|
+
for field_value, field_ref in fields:
|
|
212
|
+
if field_value is None:
|
|
213
|
+
continue
|
|
214
|
+
|
|
215
|
+
# Number rules should only be applied to numeric values
|
|
216
|
+
# Skip non-numeric values (strings, booleans, objects, arrays, etc.)
|
|
217
|
+
# Note: bool is a subclass of int in Python, so we must check for bool first
|
|
218
|
+
if isinstance(field_value, bool) or not isinstance(field_value, (int, float)):
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
field_num = float(field_value)
|
|
222
|
+
|
|
223
|
+
# Use the custom function to evaluate the rule
|
|
224
|
+
try:
|
|
225
|
+
passed = rule.func(field_num)
|
|
226
|
+
except Exception:
|
|
227
|
+
# If function raises an exception, treat as failure
|
|
228
|
+
passed = False
|
|
229
|
+
|
|
230
|
+
if not passed:
|
|
231
|
+
reason = format_guardrail_error_message(
|
|
232
|
+
field_ref, "comparing function", None
|
|
233
|
+
)
|
|
234
|
+
return False, reason
|
|
235
|
+
|
|
236
|
+
return True, "All number rule validations passed"
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def evaluate_boolean_rule(
|
|
240
|
+
rule: BooleanRule,
|
|
241
|
+
input_data: dict[str, Any],
|
|
242
|
+
output_data: dict[str, Any],
|
|
243
|
+
) -> tuple[bool, str | None]:
|
|
244
|
+
"""Evaluate a boolean rule against input and output data."""
|
|
245
|
+
fields = get_fields_from_selector(rule.field_selector, input_data, output_data)
|
|
246
|
+
|
|
247
|
+
for field_value, field_ref in fields:
|
|
248
|
+
if field_value is None:
|
|
249
|
+
continue
|
|
250
|
+
|
|
251
|
+
# Boolean rules should only be applied to boolean values
|
|
252
|
+
# Skip non-boolean values (strings, numbers, objects, arrays, etc.)
|
|
253
|
+
if not isinstance(field_value, bool):
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
field_bool = field_value
|
|
257
|
+
|
|
258
|
+
# Use the custom function to evaluate the rule
|
|
259
|
+
try:
|
|
260
|
+
passed = rule.func(field_bool)
|
|
261
|
+
except Exception:
|
|
262
|
+
# If function raises an exception, treat as failure
|
|
263
|
+
passed = False
|
|
264
|
+
|
|
265
|
+
if not passed:
|
|
266
|
+
reason = format_guardrail_error_message(
|
|
267
|
+
field_ref, "comparing function", None
|
|
268
|
+
)
|
|
269
|
+
return False, reason
|
|
270
|
+
|
|
271
|
+
return True, "All boolean rule validations passed"
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def evaluate_universal_rule(
|
|
275
|
+
rule: UniversalRule,
|
|
276
|
+
output_data: dict[str, Any],
|
|
277
|
+
) -> tuple[bool, str | None]:
|
|
278
|
+
"""Evaluate a universal rule against input and output data.
|
|
279
|
+
|
|
280
|
+
Universal rules trigger based on the apply_to scope and execution phase:
|
|
281
|
+
- Pre-execution (empty output_data):
|
|
282
|
+
- INPUT: triggers (validation_passed = False)
|
|
283
|
+
- OUTPUT: does not trigger (validation_passed = True)
|
|
284
|
+
- INPUT_AND_OUTPUT: triggers (validation_passed = False)
|
|
285
|
+
- Post-execution (output_data has data):
|
|
286
|
+
- INPUT: does not trigger (validation_passed = True)
|
|
287
|
+
- OUTPUT: triggers (validation_passed = False)
|
|
288
|
+
- INPUT_AND_OUTPUT: triggers (validation_passed = False)
|
|
289
|
+
"""
|
|
290
|
+
# Determine if this is pre-execution (no output data) or post-execution
|
|
291
|
+
is_pre_execution = not output_data or len(output_data) == 0
|
|
292
|
+
|
|
293
|
+
if rule.apply_to == ApplyTo.INPUT:
|
|
294
|
+
# INPUT: triggers in pre-execution, does not trigger in post-execution
|
|
295
|
+
if is_pre_execution:
|
|
296
|
+
return False, "Universal rule validation triggered (pre-execution, input)"
|
|
297
|
+
else:
|
|
298
|
+
return True, "Universal rule validation passed (post-execution, input)"
|
|
299
|
+
elif rule.apply_to == ApplyTo.OUTPUT:
|
|
300
|
+
# OUTPUT: does not trigger in pre-execution, triggers in post-execution
|
|
301
|
+
if is_pre_execution:
|
|
302
|
+
return True, "Universal rule validation passed (pre-execution, output)"
|
|
303
|
+
else:
|
|
304
|
+
return False, "Universal rule validation triggered (post-execution, output)"
|
|
305
|
+
elif rule.apply_to == ApplyTo.INPUT_AND_OUTPUT:
|
|
306
|
+
# INPUT_AND_OUTPUT: triggers in both phases
|
|
307
|
+
return False, "Universal rule validation triggered (input and output)"
|
|
308
|
+
else:
|
|
309
|
+
return False, f"Unknown apply_to value: {rule.apply_to}"
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Guardrails models for UiPath Platform."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Annotated, Callable, Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GuardrailValidationResult(BaseModel):
|
|
10
|
+
"""Result returned from validating input with a given guardrail.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
validation_passed: Indicates whether the input data passed the guardrail validation.
|
|
14
|
+
reason: Textual explanation describing why the validation passed or failed.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
18
|
+
|
|
19
|
+
validation_passed: bool = Field(
|
|
20
|
+
alias="validation_passed", description="Whether the input passed validation."
|
|
21
|
+
)
|
|
22
|
+
reason: str = Field(
|
|
23
|
+
alias="reason", description="Explanation for the validation result."
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FieldSource(str, Enum):
|
|
28
|
+
"""Field source enumeration."""
|
|
29
|
+
|
|
30
|
+
INPUT = "input"
|
|
31
|
+
OUTPUT = "output"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ApplyTo(str, Enum):
|
|
35
|
+
"""Apply to enumeration."""
|
|
36
|
+
|
|
37
|
+
INPUT = "input"
|
|
38
|
+
INPUT_AND_OUTPUT = "inputAndOutput"
|
|
39
|
+
OUTPUT = "output"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class FieldReference(BaseModel):
|
|
43
|
+
"""Field reference model."""
|
|
44
|
+
|
|
45
|
+
path: str
|
|
46
|
+
source: FieldSource
|
|
47
|
+
|
|
48
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class SelectorType(str, Enum):
|
|
52
|
+
"""Selector type enumeration."""
|
|
53
|
+
|
|
54
|
+
ALL = "all"
|
|
55
|
+
SPECIFIC = "specific"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class AllFieldsSelector(BaseModel):
|
|
59
|
+
"""All fields selector."""
|
|
60
|
+
|
|
61
|
+
selector_type: Literal["all"] = Field(alias="$selectorType")
|
|
62
|
+
|
|
63
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class SpecificFieldsSelector(BaseModel):
|
|
67
|
+
"""Specific fields selector."""
|
|
68
|
+
|
|
69
|
+
selector_type: Literal["specific"] = Field(alias="$selectorType")
|
|
70
|
+
fields: list[FieldReference]
|
|
71
|
+
|
|
72
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
FieldSelector = Annotated[
|
|
76
|
+
AllFieldsSelector | SpecificFieldsSelector,
|
|
77
|
+
Field(discriminator="selector_type"),
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class RuleType(str, Enum):
|
|
82
|
+
"""Rule type enumeration."""
|
|
83
|
+
|
|
84
|
+
BOOLEAN = "boolean"
|
|
85
|
+
NUMBER = "number"
|
|
86
|
+
UNIVERSAL = "always"
|
|
87
|
+
WORD = "word"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class WordRule(BaseModel):
|
|
91
|
+
"""Word rule model."""
|
|
92
|
+
|
|
93
|
+
rule_type: Literal["word"] = Field(alias="$ruleType")
|
|
94
|
+
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
95
|
+
func: Callable[[str], bool] = Field(exclude=True)
|
|
96
|
+
|
|
97
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class UniversalRule(BaseModel):
|
|
101
|
+
"""Universal rule model."""
|
|
102
|
+
|
|
103
|
+
rule_type: Literal["always"] = Field(alias="$ruleType")
|
|
104
|
+
apply_to: ApplyTo = Field(alias="applyTo")
|
|
105
|
+
|
|
106
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class NumberRule(BaseModel):
|
|
110
|
+
"""Number rule model."""
|
|
111
|
+
|
|
112
|
+
rule_type: Literal["number"] = Field(alias="$ruleType")
|
|
113
|
+
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
114
|
+
func: Callable[[float], bool] = Field(exclude=True)
|
|
115
|
+
|
|
116
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class BooleanRule(BaseModel):
|
|
120
|
+
"""Boolean rule model."""
|
|
121
|
+
|
|
122
|
+
rule_type: Literal["boolean"] = Field(alias="$ruleType")
|
|
123
|
+
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
124
|
+
func: Callable[[bool], bool] = Field(exclude=True)
|
|
125
|
+
|
|
126
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
Rule = Annotated[
|
|
130
|
+
WordRule | NumberRule | BooleanRule | UniversalRule,
|
|
131
|
+
Field(discriminator="rule_type"),
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class GuardrailScope(str, Enum):
|
|
136
|
+
"""Guardrail scope enumeration."""
|
|
137
|
+
|
|
138
|
+
AGENT = "Agent"
|
|
139
|
+
LLM = "Llm"
|
|
140
|
+
TOOL = "Tool"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class GuardrailSelector(BaseModel):
|
|
144
|
+
"""Guardrail selector model."""
|
|
145
|
+
|
|
146
|
+
scopes: list[GuardrailScope] = Field(default=[GuardrailScope.TOOL])
|
|
147
|
+
match_names: list[str] | None = Field(None, alias="matchNames")
|
|
148
|
+
|
|
149
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class BaseGuardrail(BaseModel):
|
|
153
|
+
"""Base guardrail model."""
|
|
154
|
+
|
|
155
|
+
id: str
|
|
156
|
+
name: str
|
|
157
|
+
description: str | None = None
|
|
158
|
+
enabled_for_evals: bool = Field(True, alias="enabledForEvals")
|
|
159
|
+
selector: GuardrailSelector
|
|
160
|
+
|
|
161
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class DeterministicGuardrail(BaseGuardrail):
|
|
165
|
+
"""Deterministic guardrail model."""
|
|
166
|
+
|
|
167
|
+
guardrail_type: Literal["custom"] = Field(alias="custom")
|
|
168
|
+
rules: list[Rule]
|
|
169
|
+
|
|
170
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
@@ -14,6 +14,10 @@ uipath/core/chat/meta.py,sha256=3t0eS9UHoAPHre97QTUeVbjDhnMX4zj4-qG6ju0B8wY,315
|
|
|
14
14
|
uipath/core/chat/tool.py,sha256=6e5pyX3hOWM5fIzr_fdG49Mbzz6XzJD3nsmha-yGa2k,2308
|
|
15
15
|
uipath/core/errors/__init__.py,sha256=gjxdLibZ0fjwgzPuLJY04P8dIX9rbSM2wQ97jP34ucE,278
|
|
16
16
|
uipath/core/errors/errors.py,sha256=5LajjuTfNW82ju07wT5mD3tXk0S-Ju7OqJqQpPN0F6g,486
|
|
17
|
+
uipath/core/guardrails/__init__.py,sha256=oPJnSJmeDlXNpN6XfnnZUGCDg_BkaG3Tk-chLM743C0,892
|
|
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
|
|
17
21
|
uipath/core/tracing/__init__.py,sha256=1XNLYZ4J76XkRrizGO486mS6yxzVXUbrldpvxTyJe3E,483
|
|
18
22
|
uipath/core/tracing/_utils.py,sha256=FiCFGOFa4czruhlSF87Q5Q4jX9KKPHZiw8k14K7W5v4,6636
|
|
19
23
|
uipath/core/tracing/decorators.py,sha256=ag_MFwZ0TywrhbpLKqQwF1guvRA9sYiItxao5LN9_Iw,10942
|
|
@@ -21,7 +25,7 @@ uipath/core/tracing/exporters.py,sha256=FClouEEQfk3F8J7G_NFoarDJM3R0-gA5jUxA5xRH
|
|
|
21
25
|
uipath/core/tracing/processors.py,sha256=R_652rtjPmfpUtaXoIcmfZrRZylVXFRNwjOmJUUxOQw,1408
|
|
22
26
|
uipath/core/tracing/span_utils.py,sha256=WYBrd6ZbawAs7r1Js-Zvo9_8GzkD9LhHNOls00bK_xI,12235
|
|
23
27
|
uipath/core/tracing/trace_manager.py,sha256=51rscJcepkTK4bWoCZdE-DFc9wt2F-aSuFBaSXmkHl0,3130
|
|
24
|
-
uipath_core-0.1.
|
|
25
|
-
uipath_core-0.1.
|
|
26
|
-
uipath_core-0.1.
|
|
27
|
-
uipath_core-0.1.
|
|
28
|
+
uipath_core-0.1.2.dist-info/METADATA,sha256=rRT3V6Rlvi1KVNC128ie__-tZ1ymkt1ZFZbbGGJ07m4,938
|
|
29
|
+
uipath_core-0.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
30
|
+
uipath_core-0.1.2.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
|
|
31
|
+
uipath_core-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|