uipath 2.0.12__py3-none-any.whl → 2.0.14__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.
Potentially problematic release.
This version of uipath might be problematic. Click here for more details.
- uipath/_cli/cli_run.py +2 -2
- uipath/tracing/__init__.py +2 -2
- uipath/tracing/_otel_exporters.py +32 -12
- uipath/tracing/_traced.py +116 -38
- {uipath-2.0.12.dist-info → uipath-2.0.14.dist-info}/METADATA +1 -1
- {uipath-2.0.12.dist-info → uipath-2.0.14.dist-info}/RECORD +9 -9
- {uipath-2.0.12.dist-info → uipath-2.0.14.dist-info}/WHEEL +0 -0
- {uipath-2.0.12.dist-info → uipath-2.0.14.dist-info}/entry_points.txt +0 -0
- {uipath-2.0.12.dist-info → uipath-2.0.14.dist-info}/licenses/LICENSE +0 -0
uipath/_cli/cli_run.py
CHANGED
|
@@ -115,9 +115,9 @@ def run(entrypoint: Optional[str], input: Optional[str], resume: bool) -> None:
|
|
|
115
115
|
|
|
116
116
|
# Handle result from middleware
|
|
117
117
|
if result.error_message:
|
|
118
|
-
click.echo(result.error_message)
|
|
118
|
+
click.echo(result.error_message, err=True)
|
|
119
119
|
if result.should_include_stacktrace:
|
|
120
|
-
click.echo(traceback.format_exc())
|
|
120
|
+
click.echo(traceback.format_exc(), err=True)
|
|
121
121
|
click.get_current_context().exit(1)
|
|
122
122
|
|
|
123
123
|
if result.info_message:
|
uipath/tracing/__init__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
from ._traced import
|
|
1
|
+
from ._traced import TracingManager, traced, wait_for_tracers # noqa: D104
|
|
2
2
|
|
|
3
|
-
__all__ = ["
|
|
3
|
+
__all__ = ["TracingManager", "traced", "wait_for_tracers"]
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
-
|
|
4
|
+
import time
|
|
5
|
+
from typing import Any, Dict, Sequence
|
|
5
6
|
|
|
6
7
|
from httpx import Client
|
|
7
8
|
from opentelemetry.sdk.trace import ReadableSpan
|
|
@@ -30,7 +31,7 @@ class LlmOpsHttpExporter(SpanExporter):
|
|
|
30
31
|
|
|
31
32
|
self.http_client = Client(headers=self.headers)
|
|
32
33
|
|
|
33
|
-
def export(self, spans: Sequence[ReadableSpan]):
|
|
34
|
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
|
34
35
|
"""Export spans to UiPath LLM Ops."""
|
|
35
36
|
logger.debug(
|
|
36
37
|
f"Exporting {len(spans)} spans to {self.base_url}/llmopstenant_/api/Traces/spans"
|
|
@@ -39,23 +40,42 @@ class LlmOpsHttpExporter(SpanExporter):
|
|
|
39
40
|
span_list = [
|
|
40
41
|
_SpanUtils.otel_span_to_uipath_span(span).to_dict() for span in spans
|
|
41
42
|
]
|
|
43
|
+
url = self._build_url(span_list)
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
url = f"{self.base_url}/llmopstenant_/api/Traces/spans?traceId={trace_id}&source=Robots"
|
|
45
|
-
|
|
46
|
-
logger.debug("payload: ", json.dumps(span_list))
|
|
47
|
-
|
|
48
|
-
res = self.http_client.post(url, json=span_list)
|
|
45
|
+
logger.debug("Payload: %s", json.dumps(span_list))
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
return SpanExportResult.SUCCESS
|
|
52
|
-
else:
|
|
53
|
-
return SpanExportResult.FAILURE
|
|
47
|
+
return self._send_with_retries(url, span_list)
|
|
54
48
|
|
|
55
49
|
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
56
50
|
"""Force flush the exporter."""
|
|
57
51
|
return True
|
|
58
52
|
|
|
53
|
+
def _build_url(self, span_list: list[Dict[str, Any]]) -> str:
|
|
54
|
+
"""Construct the URL for the API request."""
|
|
55
|
+
trace_id = str(span_list[0]["TraceId"])
|
|
56
|
+
return f"{self.base_url}/llmopstenant_/api/Traces/spans?traceId={trace_id}&source=Robots"
|
|
57
|
+
|
|
58
|
+
def _send_with_retries(
|
|
59
|
+
self, url: str, payload: list[Dict[str, Any]], max_retries: int = 4
|
|
60
|
+
) -> SpanExportResult:
|
|
61
|
+
"""Send the HTTP request with retry logic."""
|
|
62
|
+
for attempt in range(max_retries):
|
|
63
|
+
try:
|
|
64
|
+
response = self.http_client.post(url, json=payload)
|
|
65
|
+
if response.status_code == 200:
|
|
66
|
+
return SpanExportResult.SUCCESS
|
|
67
|
+
else:
|
|
68
|
+
logger.warning(
|
|
69
|
+
f"Attempt {attempt + 1} failed with status code {response.status_code}: {response.text}"
|
|
70
|
+
)
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.error(f"Attempt {attempt + 1} failed with exception: {e}")
|
|
73
|
+
|
|
74
|
+
if attempt < max_retries - 1:
|
|
75
|
+
time.sleep(1.5**attempt) # Exponential backoff
|
|
76
|
+
|
|
77
|
+
return SpanExportResult.FAILURE
|
|
78
|
+
|
|
59
79
|
def _get_base_url(self) -> str:
|
|
60
80
|
uipath_url = (
|
|
61
81
|
os.environ.get("UIPATH_URL")
|
uipath/tracing/_traced.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import importlib
|
|
1
2
|
import inspect
|
|
2
3
|
import json
|
|
3
4
|
import logging
|
|
4
5
|
from functools import wraps
|
|
5
|
-
from typing import Any, Callable, Optional
|
|
6
|
+
from typing import Any, Callable, List, Optional, Tuple
|
|
6
7
|
|
|
7
8
|
from opentelemetry import trace
|
|
8
9
|
from opentelemetry.sdk.trace import TracerProvider
|
|
@@ -18,9 +19,94 @@ trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(LlmOpsHttpExpo
|
|
|
18
19
|
tracer = trace.get_tracer(__name__)
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
"""
|
|
23
|
-
|
|
22
|
+
class TracingManager:
|
|
23
|
+
"""Static utility class to manage tracing implementations and decorated functions."""
|
|
24
|
+
|
|
25
|
+
# Registry to track original functions, decorated functions, and their parameters
|
|
26
|
+
# Each entry is (original_func, decorated_func, params)
|
|
27
|
+
_traced_registry: List[Tuple[Callable[..., Any], Callable[..., Any], Any]] = []
|
|
28
|
+
|
|
29
|
+
# Custom tracer implementation
|
|
30
|
+
_custom_tracer_implementation = None
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def get_custom_tracer_implementation(cls):
|
|
34
|
+
"""Get the currently set custom tracer implementation."""
|
|
35
|
+
return cls._custom_tracer_implementation
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def register_traced_function(cls, original_func, decorated_func, params):
|
|
39
|
+
"""Register a function decorated with @traced and its parameters.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
original_func: The original function before decoration
|
|
43
|
+
decorated_func: The function after decoration
|
|
44
|
+
params: The parameters used for tracing
|
|
45
|
+
"""
|
|
46
|
+
cls._traced_registry.append((original_func, decorated_func, params))
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def reapply_traced_decorator(cls, tracer_implementation):
|
|
50
|
+
"""Reapply a different tracer implementation to all functions previously decorated with @traced.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
tracer_implementation: A function that takes the same parameters as _opentelemetry_traced
|
|
54
|
+
and returns a decorator
|
|
55
|
+
"""
|
|
56
|
+
cls._custom_tracer_implementation = tracer_implementation
|
|
57
|
+
|
|
58
|
+
# Work with a copy of the registry to avoid modifying it during iteration
|
|
59
|
+
registry_copy = cls._traced_registry.copy()
|
|
60
|
+
|
|
61
|
+
for original_func, decorated_func, params in registry_copy:
|
|
62
|
+
# Apply the new decorator with the same parameters
|
|
63
|
+
new_decorated_func = tracer_implementation(**params)(original_func)
|
|
64
|
+
|
|
65
|
+
logger.debug(
|
|
66
|
+
f"Reapplying decorator to {original_func.__name__}, from {decorated_func.__name__}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# If this is a method on a class, we need to update the class
|
|
70
|
+
if hasattr(original_func, "__self__") and hasattr(
|
|
71
|
+
original_func, "__func__"
|
|
72
|
+
):
|
|
73
|
+
setattr(
|
|
74
|
+
original_func.__self__.__class__,
|
|
75
|
+
original_func.__name__,
|
|
76
|
+
new_decorated_func.__get__(
|
|
77
|
+
original_func.__self__, original_func.__self__.__class__
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
else:
|
|
81
|
+
# Replace the function in its module
|
|
82
|
+
if hasattr(original_func, "__module__") and hasattr(
|
|
83
|
+
original_func, "__qualname__"
|
|
84
|
+
):
|
|
85
|
+
try:
|
|
86
|
+
module = importlib.import_module(original_func.__module__)
|
|
87
|
+
parts = original_func.__qualname__.split(".")
|
|
88
|
+
|
|
89
|
+
# Handle nested objects
|
|
90
|
+
obj = module
|
|
91
|
+
for part in parts[:-1]:
|
|
92
|
+
obj = getattr(obj, part)
|
|
93
|
+
|
|
94
|
+
setattr(obj, parts[-1], new_decorated_func)
|
|
95
|
+
|
|
96
|
+
# Update the registry entry for this function
|
|
97
|
+
# Find the index and replace with updated entry
|
|
98
|
+
for i, (orig, _dec, _p) in enumerate(cls._traced_registry):
|
|
99
|
+
if orig is original_func:
|
|
100
|
+
cls._traced_registry[i] = (
|
|
101
|
+
original_func,
|
|
102
|
+
new_decorated_func,
|
|
103
|
+
params,
|
|
104
|
+
)
|
|
105
|
+
break
|
|
106
|
+
except (ImportError, AttributeError) as e:
|
|
107
|
+
# Log the error but continue processing other functions
|
|
108
|
+
logger.warning(f"Error reapplying decorator: {e}")
|
|
109
|
+
continue
|
|
24
110
|
|
|
25
111
|
|
|
26
112
|
def _default_input_processor(inputs):
|
|
@@ -33,23 +119,9 @@ def _default_output_processor(outputs):
|
|
|
33
119
|
return {"redacted": "Output data not logged for privacy/security"}
|
|
34
120
|
|
|
35
121
|
|
|
36
|
-
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
_decorators: dict[str, Any] = {}
|
|
40
|
-
_active_decorator = "opentelemetry"
|
|
41
|
-
|
|
42
|
-
@classmethod
|
|
43
|
-
def register_decorator(cls, name, decorator_factory):
|
|
44
|
-
"""Register a decorator factory function with a name."""
|
|
45
|
-
cls._decorators[name] = decorator_factory
|
|
46
|
-
cls._active_decorator = name
|
|
47
|
-
return cls
|
|
48
|
-
|
|
49
|
-
@classmethod
|
|
50
|
-
def get_decorator(cls):
|
|
51
|
-
"""Get the currently active decorator factory."""
|
|
52
|
-
return cls._decorators.get(cls._active_decorator)
|
|
122
|
+
def wait_for_tracers():
|
|
123
|
+
"""Wait for all tracers to finish."""
|
|
124
|
+
trace.get_tracer_provider().shutdown() # type: ignore
|
|
53
125
|
|
|
54
126
|
|
|
55
127
|
def _opentelemetry_traced(
|
|
@@ -58,6 +130,8 @@ def _opentelemetry_traced(
|
|
|
58
130
|
input_processor: Optional[Callable[..., Any]] = None,
|
|
59
131
|
output_processor: Optional[Callable[..., Any]] = None,
|
|
60
132
|
):
|
|
133
|
+
"""Default tracer implementation using OpenTelemetry."""
|
|
134
|
+
|
|
61
135
|
def decorator(func):
|
|
62
136
|
@wraps(func)
|
|
63
137
|
def sync_wrapper(*args, **kwargs):
|
|
@@ -78,9 +152,7 @@ def _opentelemetry_traced(
|
|
|
78
152
|
if input_processor is not None:
|
|
79
153
|
processed_inputs = input_processor(json.loads(inputs))
|
|
80
154
|
inputs = json.dumps(processed_inputs, default=str)
|
|
81
|
-
|
|
82
155
|
span.set_attribute("inputs", inputs)
|
|
83
|
-
|
|
84
156
|
try:
|
|
85
157
|
result = func(*args, **kwargs)
|
|
86
158
|
# Process output if processor is provided
|
|
@@ -115,9 +187,7 @@ def _opentelemetry_traced(
|
|
|
115
187
|
if input_processor is not None:
|
|
116
188
|
processed_inputs = input_processor(json.loads(inputs))
|
|
117
189
|
inputs = json.dumps(processed_inputs, default=str)
|
|
118
|
-
|
|
119
190
|
span.set_attribute("inputs", inputs)
|
|
120
|
-
|
|
121
191
|
try:
|
|
122
192
|
result = await func(*args, **kwargs)
|
|
123
193
|
# Process output if processor is provided
|
|
@@ -152,9 +222,7 @@ def _opentelemetry_traced(
|
|
|
152
222
|
if input_processor is not None:
|
|
153
223
|
processed_inputs = input_processor(json.loads(inputs))
|
|
154
224
|
inputs = json.dumps(processed_inputs, default=str)
|
|
155
|
-
|
|
156
225
|
span.set_attribute("inputs", inputs)
|
|
157
|
-
|
|
158
226
|
outputs = []
|
|
159
227
|
try:
|
|
160
228
|
for item in func(*args, **kwargs):
|
|
@@ -195,9 +263,7 @@ def _opentelemetry_traced(
|
|
|
195
263
|
if input_processor is not None:
|
|
196
264
|
processed_inputs = input_processor(json.loads(inputs))
|
|
197
265
|
inputs = json.dumps(processed_inputs, default=str)
|
|
198
|
-
|
|
199
266
|
span.set_attribute("inputs", inputs)
|
|
200
|
-
|
|
201
267
|
outputs = []
|
|
202
268
|
try:
|
|
203
269
|
async for item in func(*args, **kwargs):
|
|
@@ -254,16 +320,28 @@ def traced(
|
|
|
254
320
|
# Apply default processors selectively based on hide flags
|
|
255
321
|
if hide_input:
|
|
256
322
|
input_processor = _default_input_processor
|
|
257
|
-
|
|
258
323
|
if hide_output:
|
|
259
324
|
output_processor = _default_output_processor
|
|
260
325
|
|
|
261
|
-
|
|
326
|
+
# Store the parameters for later reapplication
|
|
327
|
+
params = {
|
|
328
|
+
"run_type": run_type,
|
|
329
|
+
"span_type": span_type,
|
|
330
|
+
"input_processor": input_processor,
|
|
331
|
+
"output_processor": output_processor,
|
|
332
|
+
}
|
|
262
333
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
334
|
+
# Check for custom implementation first
|
|
335
|
+
custom_implementation = TracingManager.get_custom_tracer_implementation()
|
|
336
|
+
tracer_impl: Any = (
|
|
337
|
+
custom_implementation if custom_implementation else _opentelemetry_traced
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
def decorator(func):
|
|
341
|
+
# Decorate the function
|
|
342
|
+
decorated_func = tracer_impl(**params)(func)
|
|
343
|
+
# Register both original and decorated function with parameters
|
|
344
|
+
TracingManager.register_traced_function(func, decorated_func, params)
|
|
345
|
+
return decorated_func
|
|
346
|
+
|
|
347
|
+
return decorator
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: uipath
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.14
|
|
4
4
|
Summary: Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools.
|
|
5
5
|
Project-URL: Homepage, https://uipath.com
|
|
6
6
|
Project-URL: Repository, https://github.com/UiPath/uipath-python
|
|
@@ -12,7 +12,7 @@ uipath/_cli/cli_init.py,sha256=idoqlGhhzXZKmLAg-3JgZ2fYMrK7qFXYV0EhnNaI3bg,3738
|
|
|
12
12
|
uipath/_cli/cli_new.py,sha256=SP7eWOa5valmCpc8UsOCIezL25euhglB3yJkx-N92W8,1903
|
|
13
13
|
uipath/_cli/cli_pack.py,sha256=x7Je61NYzR7CR8TUoT-xeZMrXXFAsXO9PGFDUVfBU8s,12876
|
|
14
14
|
uipath/_cli/cli_publish.py,sha256=_b9rehjsbxwkpH5_DtgFUaWWJqcZTg5nate-M5BnE_c,3586
|
|
15
|
-
uipath/_cli/cli_run.py,sha256=
|
|
15
|
+
uipath/_cli/cli_run.py,sha256=B5L7fE2IqysIEcweedU8GEy7ekMGwpeRagYBCB_cdQI,4597
|
|
16
16
|
uipath/_cli/middlewares.py,sha256=IiJgjsqrJVKSXx4RcIKHWoH-SqWqpHPbhzkQEybmAos,3937
|
|
17
17
|
uipath/_cli/_auth/_auth_server.py,sha256=vrzrE-hDx8exM5p2sFVoT9vKMblOyFWUvFXz-lTXceY,7077
|
|
18
18
|
uipath/_cli/_auth/_models.py,sha256=sYMCfvmprIqnZxStlD_Dxx2bcxgn0Ri4D7uwemwkcNg,948
|
|
@@ -69,12 +69,12 @@ uipath/models/job.py,sha256=f9L6_kg_VP0dAYvdcz1DWEWzy4NZPdlpHREod0uNK1E,3099
|
|
|
69
69
|
uipath/models/llm_gateway.py,sha256=0sl5Wtve94V14H3AHwmJSoXAhoc-Fai3wJxP8HrnBPg,1994
|
|
70
70
|
uipath/models/processes.py,sha256=Atvfrt6X4TYST3iA62jpS_Uxc3hg6uah11p-RaKZ6dk,2029
|
|
71
71
|
uipath/models/queues.py,sha256=N_s0GKucbyjh0RnO8SxPk6wlRgvq8KIIYsfaoIY46tM,6446
|
|
72
|
-
uipath/tracing/__init__.py,sha256=
|
|
73
|
-
uipath/tracing/_otel_exporters.py,sha256=
|
|
74
|
-
uipath/tracing/_traced.py,sha256=
|
|
72
|
+
uipath/tracing/__init__.py,sha256=GimSzv6qkCOlHOG1WtjYKJsZqcXpA28IgoXfR33JhiA,139
|
|
73
|
+
uipath/tracing/_otel_exporters.py,sha256=x0PDPmDKJcxashsuehVsSsqBCzRr6WsNFaq_3_HS5F0,3014
|
|
74
|
+
uipath/tracing/_traced.py,sha256=9nEjFjGuxPlJ_4OXoClJ79xcbFK6C8iyI03kQQSDaJg,14834
|
|
75
75
|
uipath/tracing/_utils.py,sha256=5SwsTGpHkIouXBndw-u8eCLnN4p7LM8DsTCCuf2jJgs,10165
|
|
76
|
-
uipath-2.0.
|
|
77
|
-
uipath-2.0.
|
|
78
|
-
uipath-2.0.
|
|
79
|
-
uipath-2.0.
|
|
80
|
-
uipath-2.0.
|
|
76
|
+
uipath-2.0.14.dist-info/METADATA,sha256=IaNBRJ_zhb0K7FfqtxqwtiH5LspaDLhgEZ45H93Pv8U,6078
|
|
77
|
+
uipath-2.0.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
78
|
+
uipath-2.0.14.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
|
|
79
|
+
uipath-2.0.14.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
|
|
80
|
+
uipath-2.0.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|