uipath-core 0.1.7__py3-none-any.whl → 0.1.9__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/_deterministic_guardrails_service.py +25 -9
- uipath/core/guardrails/_evaluators.py +74 -37
- uipath/core/guardrails/guardrails.py +15 -0
- uipath/core/tracing/decorators.py +22 -11
- uipath/core/tracing/span_utils.py +24 -2
- {uipath_core-0.1.7.dist-info → uipath_core-0.1.9.dist-info}/METADATA +1 -1
- {uipath_core-0.1.7.dist-info → uipath_core-0.1.9.dist-info}/RECORD +9 -9
- {uipath_core-0.1.7.dist-info → uipath_core-0.1.9.dist-info}/WHEEL +0 -0
- {uipath_core-0.1.7.dist-info → uipath_core-0.1.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -40,7 +40,7 @@ class DeterministicGuardrailsService(BaseModel):
|
|
|
40
40
|
if has_output_rule:
|
|
41
41
|
return GuardrailValidationResult(
|
|
42
42
|
result=GuardrailValidationResultType.PASSED,
|
|
43
|
-
reason="
|
|
43
|
+
reason="No rules to apply for input data.",
|
|
44
44
|
)
|
|
45
45
|
return self._evaluate_deterministic_guardrail(
|
|
46
46
|
input_data=input_data,
|
|
@@ -66,7 +66,7 @@ class DeterministicGuardrailsService(BaseModel):
|
|
|
66
66
|
if not has_output_rule:
|
|
67
67
|
return GuardrailValidationResult(
|
|
68
68
|
result=GuardrailValidationResultType.PASSED,
|
|
69
|
-
reason="
|
|
69
|
+
reason="No rules to apply for output data.",
|
|
70
70
|
)
|
|
71
71
|
|
|
72
72
|
return self._evaluate_deterministic_guardrail(
|
|
@@ -117,7 +117,12 @@ class DeterministicGuardrailsService(BaseModel):
|
|
|
117
117
|
output_data: dict[str, Any],
|
|
118
118
|
guardrail: DeterministicGuardrail,
|
|
119
119
|
) -> GuardrailValidationResult:
|
|
120
|
-
"""Evaluate deterministic guardrail rules against input and output data.
|
|
120
|
+
"""Evaluate deterministic guardrail rules against input and output data.
|
|
121
|
+
|
|
122
|
+
Validation fails only if ALL guardrail rules are violated.
|
|
123
|
+
"""
|
|
124
|
+
validated_conditions: list[str] = []
|
|
125
|
+
|
|
121
126
|
for rule in guardrail.rules:
|
|
122
127
|
if isinstance(rule, WordRule):
|
|
123
128
|
passed, reason = evaluate_word_rule(rule, input_data, output_data)
|
|
@@ -132,14 +137,25 @@ class DeterministicGuardrailsService(BaseModel):
|
|
|
132
137
|
result=GuardrailValidationResultType.VALIDATION_FAILED,
|
|
133
138
|
reason=f"Unknown rule type: {type(rule)}",
|
|
134
139
|
)
|
|
135
|
-
|
|
136
|
-
if
|
|
140
|
+
validated_conditions.append(reason)
|
|
141
|
+
if passed:
|
|
137
142
|
return GuardrailValidationResult(
|
|
138
|
-
result=GuardrailValidationResultType.
|
|
139
|
-
reason=reason
|
|
143
|
+
result=GuardrailValidationResultType.PASSED,
|
|
144
|
+
reason=reason,
|
|
140
145
|
)
|
|
141
146
|
|
|
147
|
+
has_always_rule = any(
|
|
148
|
+
condition == "Always rule enforced" for condition in validated_conditions
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
validated_conditions_str = ", ".join(validated_conditions)
|
|
152
|
+
final_reason = (
|
|
153
|
+
"Always rule enforced"
|
|
154
|
+
if has_always_rule
|
|
155
|
+
else f"Data matched all guardrail conditions: [{validated_conditions_str}]"
|
|
156
|
+
)
|
|
157
|
+
|
|
142
158
|
return GuardrailValidationResult(
|
|
143
|
-
result=GuardrailValidationResultType.
|
|
144
|
-
reason=
|
|
159
|
+
result=GuardrailValidationResultType.VALIDATION_FAILED,
|
|
160
|
+
reason=final_reason,
|
|
145
161
|
)
|
|
@@ -159,24 +159,44 @@ def get_fields_from_selector(
|
|
|
159
159
|
return fields
|
|
160
160
|
|
|
161
161
|
|
|
162
|
-
def
|
|
162
|
+
def format_guardrail_passed_validation_result_message(
|
|
163
163
|
field_ref: FieldReference,
|
|
164
|
-
operator: str,
|
|
165
|
-
|
|
164
|
+
operator: str | None,
|
|
165
|
+
rule_description: str | None,
|
|
166
166
|
) -> str:
|
|
167
|
-
"""Format a guardrail
|
|
167
|
+
"""Format a guardrail validation result message following the standard pattern."""
|
|
168
168
|
source = "Input" if field_ref.source == FieldSource.INPUT else "Output"
|
|
169
|
-
|
|
170
|
-
if
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
|
|
170
|
+
if rule_description:
|
|
171
|
+
return (
|
|
172
|
+
f"{source} data didn't match the guardrail condition for field "
|
|
173
|
+
f"[{field_ref.path}]: {rule_description}"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
f"{source} data didn't match the guardrail condition: "
|
|
178
|
+
f"[{field_ref.path}] comparing function [{operator}]"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def get_validated_conditions_description(
|
|
183
|
+
field_path: str,
|
|
184
|
+
operator: str | None,
|
|
185
|
+
rule_description: str | None,
|
|
186
|
+
) -> str:
|
|
187
|
+
if rule_description:
|
|
188
|
+
return rule_description
|
|
189
|
+
|
|
190
|
+
return f"[{field_path}] comparing function [{operator}]"
|
|
173
191
|
|
|
174
192
|
|
|
175
193
|
def evaluate_word_rule(
|
|
176
194
|
rule: WordRule, input_data: dict[str, Any], output_data: dict[str, Any]
|
|
177
|
-
) -> tuple[bool, str
|
|
195
|
+
) -> tuple[bool, str]:
|
|
178
196
|
"""Evaluate a word rule against input and output data."""
|
|
179
197
|
fields = get_fields_from_selector(rule.field_selector, input_data, output_data)
|
|
198
|
+
operator = _humanize_guardrail_func(rule.detects_violation) or "violation check"
|
|
199
|
+
field_paths = ", ".join({field_ref.path for _, field_ref in fields})
|
|
180
200
|
|
|
181
201
|
for field_value, field_ref in fields:
|
|
182
202
|
if field_value is None:
|
|
@@ -197,22 +217,28 @@ def evaluate_word_rule(
|
|
|
197
217
|
# If function raises an exception, treat as failure
|
|
198
218
|
violation_detected = True
|
|
199
219
|
|
|
200
|
-
if violation_detected:
|
|
201
|
-
|
|
202
|
-
|
|
220
|
+
if not violation_detected:
|
|
221
|
+
reason = format_guardrail_passed_validation_result_message(
|
|
222
|
+
field_ref=field_ref,
|
|
223
|
+
operator=operator,
|
|
224
|
+
rule_description=rule.rule_description,
|
|
203
225
|
)
|
|
204
|
-
|
|
205
|
-
return False, reason
|
|
226
|
+
return True, reason
|
|
206
227
|
|
|
207
|
-
return
|
|
228
|
+
return False, get_validated_conditions_description(
|
|
229
|
+
field_path=field_paths,
|
|
230
|
+
operator=operator,
|
|
231
|
+
rule_description=rule.rule_description,
|
|
232
|
+
)
|
|
208
233
|
|
|
209
234
|
|
|
210
235
|
def evaluate_number_rule(
|
|
211
236
|
rule: NumberRule, input_data: dict[str, Any], output_data: dict[str, Any]
|
|
212
|
-
) -> tuple[bool, str
|
|
237
|
+
) -> tuple[bool, str]:
|
|
213
238
|
"""Evaluate a number rule against input and output data."""
|
|
214
239
|
fields = get_fields_from_selector(rule.field_selector, input_data, output_data)
|
|
215
|
-
|
|
240
|
+
operator = _humanize_guardrail_func(rule.detects_violation) or "violation check"
|
|
241
|
+
field_paths = ", ".join({field_ref.path for _, field_ref in fields})
|
|
216
242
|
for field_value, field_ref in fields:
|
|
217
243
|
if field_value is None:
|
|
218
244
|
continue
|
|
@@ -233,24 +259,30 @@ def evaluate_number_rule(
|
|
|
233
259
|
# If function raises an exception, treat as failure
|
|
234
260
|
violation_detected = True
|
|
235
261
|
|
|
236
|
-
if violation_detected:
|
|
237
|
-
|
|
238
|
-
|
|
262
|
+
if not violation_detected:
|
|
263
|
+
reason = format_guardrail_passed_validation_result_message(
|
|
264
|
+
field_ref=field_ref,
|
|
265
|
+
operator=operator,
|
|
266
|
+
rule_description=rule.rule_description,
|
|
239
267
|
)
|
|
240
|
-
|
|
241
|
-
return False, reason
|
|
268
|
+
return True, reason
|
|
242
269
|
|
|
243
|
-
return
|
|
270
|
+
return False, get_validated_conditions_description(
|
|
271
|
+
field_path=field_paths,
|
|
272
|
+
operator=operator,
|
|
273
|
+
rule_description=rule.rule_description,
|
|
274
|
+
)
|
|
244
275
|
|
|
245
276
|
|
|
246
277
|
def evaluate_boolean_rule(
|
|
247
278
|
rule: BooleanRule,
|
|
248
279
|
input_data: dict[str, Any],
|
|
249
280
|
output_data: dict[str, Any],
|
|
250
|
-
) -> tuple[bool, str
|
|
281
|
+
) -> tuple[bool, str]:
|
|
251
282
|
"""Evaluate a boolean rule against input and output data."""
|
|
252
283
|
fields = get_fields_from_selector(rule.field_selector, input_data, output_data)
|
|
253
|
-
|
|
284
|
+
operator = _humanize_guardrail_func(rule.detects_violation) or "violation check"
|
|
285
|
+
field_paths = ", ".join({field_ref.path for _, field_ref in fields})
|
|
254
286
|
for field_value, field_ref in fields:
|
|
255
287
|
if field_value is None:
|
|
256
288
|
continue
|
|
@@ -270,20 +302,25 @@ def evaluate_boolean_rule(
|
|
|
270
302
|
# If function raises an exception, treat as failure
|
|
271
303
|
violation_detected = True
|
|
272
304
|
|
|
273
|
-
if violation_detected:
|
|
274
|
-
|
|
275
|
-
|
|
305
|
+
if not violation_detected:
|
|
306
|
+
reason = format_guardrail_passed_validation_result_message(
|
|
307
|
+
field_ref=field_ref,
|
|
308
|
+
operator=operator,
|
|
309
|
+
rule_description=rule.rule_description,
|
|
276
310
|
)
|
|
277
|
-
|
|
278
|
-
return False, reason
|
|
311
|
+
return True, reason
|
|
279
312
|
|
|
280
|
-
return
|
|
313
|
+
return False, get_validated_conditions_description(
|
|
314
|
+
field_path=field_paths,
|
|
315
|
+
operator=operator,
|
|
316
|
+
rule_description=rule.rule_description,
|
|
317
|
+
)
|
|
281
318
|
|
|
282
319
|
|
|
283
320
|
def evaluate_universal_rule(
|
|
284
321
|
rule: UniversalRule,
|
|
285
322
|
output_data: dict[str, Any],
|
|
286
|
-
) -> tuple[bool, str
|
|
323
|
+
) -> tuple[bool, str]:
|
|
287
324
|
"""Evaluate a universal rule against input and output data.
|
|
288
325
|
|
|
289
326
|
Universal rules trigger based on the apply_to scope and execution phase:
|
|
@@ -302,18 +339,18 @@ def evaluate_universal_rule(
|
|
|
302
339
|
if rule.apply_to == ApplyTo.INPUT:
|
|
303
340
|
# INPUT: triggers in pre-execution, does not trigger in post-execution
|
|
304
341
|
if is_pre_execution:
|
|
305
|
-
return False, "
|
|
342
|
+
return False, "Always rule enforced"
|
|
306
343
|
else:
|
|
307
|
-
return True, "
|
|
344
|
+
return True, "No rules to apply for output data"
|
|
308
345
|
elif rule.apply_to == ApplyTo.OUTPUT:
|
|
309
346
|
# OUTPUT: does not trigger in pre-execution, triggers in post-execution
|
|
310
347
|
if is_pre_execution:
|
|
311
|
-
return True, "
|
|
348
|
+
return True, "No rules to apply for input data"
|
|
312
349
|
else:
|
|
313
|
-
return False, "
|
|
350
|
+
return False, "Always rule enforced"
|
|
314
351
|
elif rule.apply_to == ApplyTo.INPUT_AND_OUTPUT:
|
|
315
352
|
# INPUT_AND_OUTPUT: triggers in both phases
|
|
316
|
-
return False, "
|
|
353
|
+
return False, "Always rule enforced"
|
|
317
354
|
else:
|
|
318
355
|
return False, f"Unknown apply_to value: {rule.apply_to}"
|
|
319
356
|
|
|
@@ -102,6 +102,11 @@ class WordRule(BaseModel):
|
|
|
102
102
|
|
|
103
103
|
rule_type: Literal["word"] = Field(alias="$ruleType")
|
|
104
104
|
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
105
|
+
rule_description: str | None = Field(
|
|
106
|
+
default=None,
|
|
107
|
+
exclude=True,
|
|
108
|
+
description="Human-friendly description of the rule condition.",
|
|
109
|
+
)
|
|
105
110
|
detects_violation: Callable[[str], bool] = Field(
|
|
106
111
|
exclude=True,
|
|
107
112
|
description="Function that returns True if the string violates the rule (validation should fail).",
|
|
@@ -124,6 +129,11 @@ class NumberRule(BaseModel):
|
|
|
124
129
|
|
|
125
130
|
rule_type: Literal["number"] = Field(alias="$ruleType")
|
|
126
131
|
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
132
|
+
rule_description: str | None = Field(
|
|
133
|
+
default=None,
|
|
134
|
+
exclude=True,
|
|
135
|
+
description="Human-friendly description of the rule condition.",
|
|
136
|
+
)
|
|
127
137
|
detects_violation: Callable[[float], bool] = Field(
|
|
128
138
|
exclude=True,
|
|
129
139
|
description="Function that returns True if the number violates the rule (validation should fail).",
|
|
@@ -137,6 +147,11 @@ class BooleanRule(BaseModel):
|
|
|
137
147
|
|
|
138
148
|
rule_type: Literal["boolean"] = Field(alias="$ruleType")
|
|
139
149
|
field_selector: FieldSelector = Field(alias="fieldSelector")
|
|
150
|
+
rule_description: str | None = Field(
|
|
151
|
+
default=None,
|
|
152
|
+
exclude=True,
|
|
153
|
+
description="Human-friendly description of the rule condition.",
|
|
154
|
+
)
|
|
140
155
|
detects_violation: Callable[[bool], bool] = Field(
|
|
141
156
|
exclude=True,
|
|
142
157
|
description="Function that returns True if the boolean violates the rule (validation should fail).",
|
|
@@ -9,7 +9,7 @@ from typing import Any, Callable, Optional
|
|
|
9
9
|
from opentelemetry import context as context_api
|
|
10
10
|
from opentelemetry import trace
|
|
11
11
|
from opentelemetry.context import _SUPPRESS_INSTRUMENTATION_KEY
|
|
12
|
-
from opentelemetry.trace import
|
|
12
|
+
from opentelemetry.trace import SpanContext, TraceFlags
|
|
13
13
|
from opentelemetry.trace.status import StatusCode
|
|
14
14
|
|
|
15
15
|
from uipath.core.tracing._utils import (
|
|
@@ -17,7 +17,11 @@ from uipath.core.tracing._utils import (
|
|
|
17
17
|
set_span_input_attributes,
|
|
18
18
|
set_span_output_attributes,
|
|
19
19
|
)
|
|
20
|
-
from uipath.core.tracing.span_utils import
|
|
20
|
+
from uipath.core.tracing.span_utils import (
|
|
21
|
+
ParentedNonRecordingSpan,
|
|
22
|
+
UiPathSpanUtils,
|
|
23
|
+
_span_registry,
|
|
24
|
+
)
|
|
21
25
|
|
|
22
26
|
logger = logging.getLogger(__name__)
|
|
23
27
|
|
|
@@ -50,7 +54,10 @@ def _opentelemetry_traced(
|
|
|
50
54
|
trace_name = name or func.__name__
|
|
51
55
|
|
|
52
56
|
def get_span():
|
|
57
|
+
ctx = UiPathSpanUtils.get_parent_context()
|
|
53
58
|
if not recording:
|
|
59
|
+
parent_context = trace.get_current_span(ctx).get_span_context()
|
|
60
|
+
|
|
54
61
|
# Create a valid but non-sampled trace context
|
|
55
62
|
# Generate a valid trace ID (not INVALID)
|
|
56
63
|
trace_id = random.getrandbits(128)
|
|
@@ -62,20 +69,24 @@ def _opentelemetry_traced(
|
|
|
62
69
|
is_remote=False,
|
|
63
70
|
trace_flags=TraceFlags(0x00), # NOT sampled
|
|
64
71
|
)
|
|
65
|
-
non_recording =
|
|
72
|
+
non_recording = ParentedNonRecordingSpan(
|
|
73
|
+
non_sampled_context, parent=parent_context
|
|
74
|
+
)
|
|
66
75
|
|
|
67
76
|
# Make it active so children see it
|
|
68
77
|
span_cm = trace.use_span(non_recording)
|
|
69
78
|
span_cm.__enter__()
|
|
70
|
-
return span_cm, non_recording
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
_span_registry.register_span(non_recording)
|
|
81
|
+
|
|
82
|
+
return span_cm, non_recording
|
|
83
|
+
else:
|
|
84
|
+
# Normal recording span
|
|
85
|
+
span_cm = trace.get_tracer(__name__).start_as_current_span(
|
|
86
|
+
trace_name, context=ctx
|
|
87
|
+
)
|
|
88
|
+
span = span_cm.__enter__()
|
|
89
|
+
return span_cm, span
|
|
79
90
|
|
|
80
91
|
# --------- Sync wrapper ---------
|
|
81
92
|
@wraps(func)
|
|
@@ -4,11 +4,33 @@ import logging
|
|
|
4
4
|
from typing import Callable, Optional
|
|
5
5
|
|
|
6
6
|
from opentelemetry import context, trace
|
|
7
|
-
from opentelemetry.trace import Span, set_span_in_context
|
|
7
|
+
from opentelemetry.trace import NonRecordingSpan, Span, set_span_in_context
|
|
8
8
|
|
|
9
9
|
logger = logging.getLogger(__name__)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
class ParentedNonRecordingSpan(NonRecordingSpan):
|
|
13
|
+
"""Non-recording span with explicit parent tracking.
|
|
14
|
+
|
|
15
|
+
Extends NonRecordingSpan to include a parent attribute, allowing the SpanRegistry
|
|
16
|
+
to properly track parent-child relationships for non-recording spans.
|
|
17
|
+
This is necessary because NonRecordingSpan instances created directly don't have
|
|
18
|
+
their parent automatically set like normal recording spans do.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self, context: trace.SpanContext, parent: Optional[trace.SpanContext] = None
|
|
23
|
+
):
|
|
24
|
+
"""Initialize a parented non-recording span.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
context: The SpanContext for this span
|
|
28
|
+
parent: Optional parent SpanContext
|
|
29
|
+
"""
|
|
30
|
+
super().__init__(context)
|
|
31
|
+
self.parent = parent
|
|
32
|
+
|
|
33
|
+
|
|
12
34
|
class SpanRegistry:
|
|
13
35
|
"""Registry to track all spans and their parent relationships."""
|
|
14
36
|
|
|
@@ -309,4 +331,4 @@ class UiPathSpanUtils:
|
|
|
309
331
|
return UiPathSpanUtils._current_span_ancestors_provider
|
|
310
332
|
|
|
311
333
|
|
|
312
|
-
__all__ = ["UiPathSpanUtils"]
|
|
334
|
+
__all__ = ["ParentedNonRecordingSpan", "UiPathSpanUtils"]
|
|
@@ -15,17 +15,17 @@ 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
17
|
uipath/core/guardrails/__init__.py,sha256=hUCmD4y5te2iy01YnJlBuf2RWvqxmsNzoyOamXLXf2E,1028
|
|
18
|
-
uipath/core/guardrails/_deterministic_guardrails_service.py,sha256=
|
|
19
|
-
uipath/core/guardrails/_evaluators.py,sha256=
|
|
20
|
-
uipath/core/guardrails/guardrails.py,sha256=
|
|
18
|
+
uipath/core/guardrails/_deterministic_guardrails_service.py,sha256=61ROXYmX3rjBfFUp7E83fm4Lk7h1DlJeukpZm7uzVqQ,6355
|
|
19
|
+
uipath/core/guardrails/_evaluators.py,sha256=10tIRUufxoy9MkZPb-ytjsCSCIfIqQSOYXyEl4G7PSw,15649
|
|
20
|
+
uipath/core/guardrails/guardrails.py,sha256=sTxsNHilEX908aF-WtQWVOQ4dCCwMgAepDebH35ngvo,5430
|
|
21
21
|
uipath/core/tracing/__init__.py,sha256=1XNLYZ4J76XkRrizGO486mS6yxzVXUbrldpvxTyJe3E,483
|
|
22
22
|
uipath/core/tracing/_utils.py,sha256=FiCFGOFa4czruhlSF87Q5Q4jX9KKPHZiw8k14K7W5v4,6636
|
|
23
|
-
uipath/core/tracing/decorators.py,sha256=
|
|
23
|
+
uipath/core/tracing/decorators.py,sha256=JDNULkUu-Ufg8pJslG4i6Q2pmqaGNDS8NFRPgFs29Dw,11937
|
|
24
24
|
uipath/core/tracing/exporters.py,sha256=FClouEEQfk3F8J7G_NFoarDJM3R0-gA5jUxA5xRHx5s,1562
|
|
25
25
|
uipath/core/tracing/processors.py,sha256=R_652rtjPmfpUtaXoIcmfZrRZylVXFRNwjOmJUUxOQw,1408
|
|
26
|
-
uipath/core/tracing/span_utils.py,sha256=
|
|
26
|
+
uipath/core/tracing/span_utils.py,sha256=LZXNdnI0-fhKe49CLPsvMJIfh9zdzk8rK4g4YN5RfDU,13064
|
|
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.9.dist-info/METADATA,sha256=wLk7EKaDR01IqA7RvbhRzw13dw2DRX6twknAm793_OY,938
|
|
29
|
+
uipath_core-0.1.9.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
30
|
+
uipath_core-0.1.9.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
|
|
31
|
+
uipath_core-0.1.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|