uipath-core 0.1.10__tar.gz → 0.2.1__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 (62) hide show
  1. {uipath_core-0.1.10 → uipath_core-0.2.1}/PKG-INFO +1 -1
  2. {uipath_core-0.1.10 → uipath_core-0.2.1}/pyproject.toml +1 -1
  3. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/tracing/__init__.py +2 -0
  4. uipath_core-0.2.1/src/uipath/core/tracing/processors.py +76 -0
  5. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/tracing/trace_manager.py +13 -3
  6. uipath_core-0.2.1/src/uipath/core/tracing/types.py +21 -0
  7. uipath_core-0.2.1/tests/tracing/test_span_filtering.py +245 -0
  8. {uipath_core-0.1.10 → uipath_core-0.2.1}/uv.lock +13 -13
  9. uipath_core-0.1.10/src/uipath/core/tracing/processors.py +0 -46
  10. {uipath_core-0.1.10 → uipath_core-0.2.1}/.cursorrules +0 -0
  11. {uipath_core-0.1.10 → uipath_core-0.2.1}/.editorconfig +0 -0
  12. {uipath_core-0.1.10 → uipath_core-0.2.1}/.gitattributes +0 -0
  13. {uipath_core-0.1.10 → uipath_core-0.2.1}/.github/workflows/cd.yml +0 -0
  14. {uipath_core-0.1.10 → uipath_core-0.2.1}/.github/workflows/ci.yml +0 -0
  15. {uipath_core-0.1.10 → uipath_core-0.2.1}/.github/workflows/commitlint.yml +0 -0
  16. {uipath_core-0.1.10 → uipath_core-0.2.1}/.github/workflows/lint.yml +0 -0
  17. {uipath_core-0.1.10 → uipath_core-0.2.1}/.github/workflows/publish-dev.yml +0 -0
  18. {uipath_core-0.1.10 → uipath_core-0.2.1}/.github/workflows/test.yml +0 -0
  19. {uipath_core-0.1.10 → uipath_core-0.2.1}/.gitignore +0 -0
  20. {uipath_core-0.1.10 → uipath_core-0.2.1}/.pre-commit-config.yaml +0 -0
  21. {uipath_core-0.1.10 → uipath_core-0.2.1}/.python-version +0 -0
  22. {uipath_core-0.1.10 → uipath_core-0.2.1}/.vscode/extensions.json +0 -0
  23. {uipath_core-0.1.10 → uipath_core-0.2.1}/.vscode/launch.json +0 -0
  24. {uipath_core-0.1.10 → uipath_core-0.2.1}/.vscode/settings.json +0 -0
  25. {uipath_core-0.1.10 → uipath_core-0.2.1}/CONTRIBUTING.md +0 -0
  26. {uipath_core-0.1.10 → uipath_core-0.2.1}/LICENSE +0 -0
  27. {uipath_core-0.1.10 → uipath_core-0.2.1}/README.md +0 -0
  28. {uipath_core-0.1.10 → uipath_core-0.2.1}/justfile +0 -0
  29. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/__init__.py +0 -0
  30. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/__init__.py +0 -0
  31. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/async_stream.py +0 -0
  32. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/citation.py +0 -0
  33. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/content.py +0 -0
  34. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/conversation.py +0 -0
  35. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/error.py +0 -0
  36. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/event.py +0 -0
  37. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/exchange.py +0 -0
  38. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/interrupt.py +0 -0
  39. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/message.py +0 -0
  40. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/meta.py +0 -0
  41. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/chat/tool.py +0 -0
  42. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/errors/__init__.py +0 -0
  43. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/errors/errors.py +0 -0
  44. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/guardrails/__init__.py +0 -0
  45. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/guardrails/_deterministic_guardrails_service.py +0 -0
  46. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/guardrails/_evaluators.py +0 -0
  47. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/guardrails/guardrails.py +0 -0
  48. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/py.typed +0 -0
  49. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/tracing/_utils.py +0 -0
  50. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/tracing/decorators.py +0 -0
  51. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/tracing/exporters.py +0 -0
  52. {uipath_core-0.1.10 → uipath_core-0.2.1}/src/uipath/core/tracing/span_utils.py +0 -0
  53. {uipath_core-0.1.10 → uipath_core-0.2.1}/tests/__init__.py +0 -0
  54. {uipath_core-0.1.10 → uipath_core-0.2.1}/tests/conftest.py +0 -0
  55. {uipath_core-0.1.10 → uipath_core-0.2.1}/tests/guardrails/test_deterministic_guardrails_service.py +0 -0
  56. {uipath_core-0.1.10 → uipath_core-0.2.1}/tests/tracing/test_external_integration.py +0 -0
  57. {uipath_core-0.1.10 → uipath_core-0.2.1}/tests/tracing/test_serialization.py +0 -0
  58. {uipath_core-0.1.10 → uipath_core-0.2.1}/tests/tracing/test_span_nesting.py +0 -0
  59. {uipath_core-0.1.10 → uipath_core-0.2.1}/tests/tracing/test_span_registry.py +0 -0
  60. {uipath_core-0.1.10 → uipath_core-0.2.1}/tests/tracing/test_trace_manager.py +0 -0
  61. {uipath_core-0.1.10 → uipath_core-0.2.1}/tests/tracing/test_traced.py +0 -0
  62. {uipath_core-0.1.10 → uipath_core-0.2.1}/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.10
3
+ Version: 0.2.1
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.10"
3
+ version = "0.2.1"
4
4
  description = "UiPath Core abstractions"
5
5
  readme = { file = "README.md", content-type = "text/markdown" }
6
6
  requires-python = ">=3.11"
@@ -7,9 +7,11 @@ with OpenTelemetry tracing, including custom processors for UiPath execution tra
7
7
  from uipath.core.tracing.decorators import traced
8
8
  from uipath.core.tracing.span_utils import UiPathSpanUtils
9
9
  from uipath.core.tracing.trace_manager import UiPathTraceManager
10
+ from uipath.core.tracing.types import UiPathTraceSettings
10
11
 
11
12
  __all__ = [
12
13
  "traced",
13
14
  "UiPathSpanUtils",
14
15
  "UiPathTraceManager",
16
+ "UiPathTraceSettings",
15
17
  ]
@@ -0,0 +1,76 @@
1
+ """Custom span processors for UiPath execution tracing."""
2
+
3
+ from typing import cast
4
+
5
+ from opentelemetry import context as context_api
6
+ from opentelemetry import trace
7
+ from opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor
8
+ from opentelemetry.sdk.trace.export import (
9
+ BatchSpanProcessor,
10
+ SimpleSpanProcessor,
11
+ SpanExporter,
12
+ )
13
+
14
+ from uipath.core.tracing.types import UiPathTraceSettings
15
+
16
+
17
+ class UiPathExecutionTraceProcessorMixin:
18
+ """Mixin that propagates execution.id and optionally filters spans."""
19
+
20
+ _settings: UiPathTraceSettings | None = None
21
+
22
+ def on_start(self, span: Span, parent_context: context_api.Context | None = None):
23
+ """Called when a span is started."""
24
+ parent_span: Span | None
25
+ if parent_context:
26
+ parent_span = cast(Span, trace.get_current_span(parent_context))
27
+ else:
28
+ parent_span = cast(Span, trace.get_current_span())
29
+
30
+ if parent_span and parent_span.is_recording() and parent_span.attributes:
31
+ execution_id = parent_span.attributes.get("execution.id")
32
+ if execution_id:
33
+ span.set_attribute("execution.id", execution_id)
34
+
35
+ def on_end(self, span: ReadableSpan):
36
+ """Called when a span ends. Filters before delegating to parent."""
37
+ span_filter = self._settings.span_filter if self._settings else None
38
+ if span_filter is None or span_filter(span):
39
+ parent = cast(SpanProcessor, super())
40
+ parent.on_end(span)
41
+
42
+
43
+ class UiPathExecutionBatchTraceProcessor(
44
+ UiPathExecutionTraceProcessorMixin, BatchSpanProcessor
45
+ ):
46
+ """Batch span processor that propagates execution.id and optionally filters."""
47
+
48
+ def __init__(
49
+ self,
50
+ span_exporter: SpanExporter,
51
+ settings: UiPathTraceSettings | None = None,
52
+ ):
53
+ """Initialize the batch trace processor."""
54
+ super().__init__(span_exporter)
55
+ self._settings = settings
56
+
57
+
58
+ class UiPathExecutionSimpleTraceProcessor(
59
+ UiPathExecutionTraceProcessorMixin, SimpleSpanProcessor
60
+ ):
61
+ """Simple span processor that propagates execution.id and optionally filters."""
62
+
63
+ def __init__(
64
+ self,
65
+ span_exporter: SpanExporter,
66
+ settings: UiPathTraceSettings | None = None,
67
+ ):
68
+ """Initialize the simple trace processor."""
69
+ super().__init__(span_exporter)
70
+ self._settings = settings
71
+
72
+
73
+ __all__ = [
74
+ "UiPathExecutionBatchTraceProcessor",
75
+ "UiPathExecutionSimpleTraceProcessor",
76
+ ]
@@ -15,6 +15,7 @@ from uipath.core.tracing.processors import (
15
15
  UiPathExecutionBatchTraceProcessor,
16
16
  UiPathExecutionSimpleTraceProcessor,
17
17
  )
18
+ from uipath.core.tracing.types import UiPathTraceSettings
18
19
 
19
20
 
20
21
  class UiPathTraceManager:
@@ -40,13 +41,22 @@ class UiPathTraceManager:
40
41
  self,
41
42
  span_exporter: SpanExporter,
42
43
  batch: bool = True,
44
+ settings: UiPathTraceSettings | None = None,
43
45
  ) -> UiPathTraceManager:
44
- """Add a span processor to the tracer provider."""
46
+ """Add a span exporter to the tracer provider.
47
+
48
+ Args:
49
+ span_exporter: The exporter to add.
50
+ batch: Whether to use batch processing (default: True).
51
+ settings: Optional trace settings for filtering, etc.
52
+ """
45
53
  span_processor: SpanProcessor
46
54
  if batch:
47
- span_processor = UiPathExecutionBatchTraceProcessor(span_exporter)
55
+ span_processor = UiPathExecutionBatchTraceProcessor(span_exporter, settings)
48
56
  else:
49
- span_processor = UiPathExecutionSimpleTraceProcessor(span_exporter)
57
+ span_processor = UiPathExecutionSimpleTraceProcessor(
58
+ span_exporter, settings
59
+ )
50
60
  self.tracer_span_processors.append(span_processor)
51
61
  self.tracer_provider.add_span_processor(span_processor)
52
62
  return self
@@ -0,0 +1,21 @@
1
+ """Tracing types for UiPath SDK."""
2
+
3
+ from typing import Callable
4
+
5
+ from opentelemetry.sdk.trace import ReadableSpan
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class UiPathTraceSettings(BaseModel):
10
+ """Trace settings for UiPath SDK."""
11
+
12
+ model_config = {"arbitrary_types_allowed": True} # Needed for Callable
13
+
14
+ span_filter: Callable[[ReadableSpan], bool] | None = Field(
15
+ default=None,
16
+ description=(
17
+ "Optional filter to decide whether a span should be exported. "
18
+ "Called when a span ends with a ReadableSpan argument. "
19
+ "Return True to export, False to skip."
20
+ ),
21
+ )
@@ -0,0 +1,245 @@
1
+ """Tests for span filtering in trace manager and processors."""
2
+
3
+ from opentelemetry import trace
4
+
5
+ from uipath.core.tracing.trace_manager import UiPathTraceManager
6
+ from uipath.core.tracing.types import UiPathTraceSettings
7
+
8
+
9
+ class TestSpanFiltering:
10
+ """Tests for span filtering functionality."""
11
+
12
+ def test_no_filter_exports_all_spans(self):
13
+ """Test that without a filter, all spans are exported."""
14
+ trace_manager = UiPathTraceManager()
15
+
16
+ tracer = trace.get_tracer("test")
17
+ with trace_manager.start_execution_span("root", "exec-1"):
18
+ with tracer.start_as_current_span("child-1"):
19
+ pass
20
+ with tracer.start_as_current_span("child-2"):
21
+ pass
22
+
23
+ spans = trace_manager.get_execution_spans("exec-1")
24
+ assert len(spans) == 3
25
+ span_names = {s.name for s in spans}
26
+ assert span_names == {"root", "child-1", "child-2"}
27
+
28
+ def test_filter_drops_non_matching_spans(self):
29
+ """Test that filter drops spans that don't match the predicate."""
30
+ from unittest.mock import MagicMock
31
+
32
+ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
33
+
34
+ mock_exporter = MagicMock(spec=SpanExporter)
35
+ mock_exporter.export.return_value = SpanExportResult.SUCCESS
36
+
37
+ settings = UiPathTraceSettings(
38
+ span_filter=lambda span: span.attributes is not None
39
+ and span.attributes.get("keep") is True
40
+ )
41
+ trace_manager = UiPathTraceManager()
42
+ trace_manager.add_span_exporter(mock_exporter, batch=False, settings=settings)
43
+
44
+ tracer = trace.get_tracer("test")
45
+ with tracer.start_as_current_span("kept", attributes={"keep": True}):
46
+ pass
47
+ with tracer.start_as_current_span("dropped", attributes={"keep": False}):
48
+ pass
49
+ with tracer.start_as_current_span("also-dropped"):
50
+ pass
51
+
52
+ trace_manager.flush_spans()
53
+
54
+ exported_spans = []
55
+ for call in mock_exporter.export.call_args_list:
56
+ exported_spans.extend(call[0][0])
57
+
58
+ exported_names = {s.name for s in exported_spans}
59
+ assert "kept" in exported_names
60
+ assert "dropped" not in exported_names
61
+ assert "also-dropped" not in exported_names
62
+
63
+ def test_filter_by_span_name(self):
64
+ """Test filtering spans by name pattern."""
65
+ from unittest.mock import MagicMock
66
+
67
+ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
68
+
69
+ mock_exporter = MagicMock(spec=SpanExporter)
70
+ mock_exporter.export.return_value = SpanExportResult.SUCCESS
71
+
72
+ settings = UiPathTraceSettings(
73
+ span_filter=lambda span: span.name.startswith("uipath.")
74
+ )
75
+ trace_manager = UiPathTraceManager()
76
+ trace_manager.add_span_exporter(mock_exporter, batch=False, settings=settings)
77
+
78
+ tracer = trace.get_tracer("test")
79
+ with tracer.start_as_current_span("uipath.action"):
80
+ pass
81
+ with tracer.start_as_current_span("uipath.tool"):
82
+ pass
83
+ with tracer.start_as_current_span("http.request"):
84
+ pass
85
+
86
+ trace_manager.flush_spans()
87
+
88
+ exported_spans = []
89
+ for call in mock_exporter.export.call_args_list:
90
+ exported_spans.extend(call[0][0])
91
+
92
+ exported_names = {s.name for s in exported_spans}
93
+ assert "uipath.action" in exported_names
94
+ assert "uipath.tool" in exported_names
95
+ assert "http.request" not in exported_names
96
+
97
+ def test_filter_custom_instrumentation_attribute(self):
98
+ """Test filtering by custom instrumentation attribute (low-code scenario)."""
99
+ from unittest.mock import MagicMock
100
+
101
+ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
102
+
103
+ mock_exporter = MagicMock(spec=SpanExporter)
104
+ mock_exporter.export.return_value = SpanExportResult.SUCCESS
105
+
106
+ settings = UiPathTraceSettings(
107
+ span_filter=lambda span: bool(
108
+ span.attributes and span.attributes.get("uipath.custom_instrumentation")
109
+ )
110
+ )
111
+ trace_manager = UiPathTraceManager()
112
+ trace_manager.add_span_exporter(mock_exporter, batch=False, settings=settings)
113
+
114
+ tracer = trace.get_tracer("test")
115
+ with tracer.start_as_current_span(
116
+ "custom-span",
117
+ attributes={"uipath.custom_instrumentation": True},
118
+ ):
119
+ pass
120
+ with tracer.start_as_current_span(
121
+ "auto-instrumented",
122
+ attributes={"http.method": "GET"},
123
+ ):
124
+ pass
125
+
126
+ trace_manager.flush_spans()
127
+
128
+ exported_spans = []
129
+ for call in mock_exporter.export.call_args_list:
130
+ exported_spans.extend(call[0][0])
131
+
132
+ exported_names = {s.name for s in exported_spans}
133
+ assert "custom-span" in exported_names
134
+ assert "auto-instrumented" not in exported_names
135
+
136
+ def test_none_filter_same_as_no_filter(self):
137
+ """Test that explicit None filter behaves same as no filter."""
138
+ from unittest.mock import MagicMock
139
+
140
+ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
141
+
142
+ mock_exporter = MagicMock(spec=SpanExporter)
143
+ mock_exporter.export.return_value = SpanExportResult.SUCCESS
144
+
145
+ settings = UiPathTraceSettings(span_filter=None)
146
+ trace_manager = UiPathTraceManager()
147
+ trace_manager.add_span_exporter(mock_exporter, batch=False, settings=settings)
148
+
149
+ tracer = trace.get_tracer("test")
150
+ with tracer.start_as_current_span("span-1"):
151
+ pass
152
+ with tracer.start_as_current_span("span-2"):
153
+ pass
154
+
155
+ trace_manager.flush_spans()
156
+
157
+ exported_spans = []
158
+ for call in mock_exporter.export.call_args_list:
159
+ exported_spans.extend(call[0][0])
160
+
161
+ assert len(exported_spans) == 2
162
+
163
+ def test_filter_with_empty_attributes(self):
164
+ """Test that filter handles spans with no attributes gracefully."""
165
+ from unittest.mock import MagicMock
166
+
167
+ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
168
+
169
+ mock_exporter = MagicMock(spec=SpanExporter)
170
+ mock_exporter.export.return_value = SpanExportResult.SUCCESS
171
+
172
+ settings = UiPathTraceSettings(
173
+ span_filter=lambda span: (
174
+ span.attributes is not None and span.attributes.get("required") is True
175
+ )
176
+ )
177
+ trace_manager = UiPathTraceManager()
178
+ trace_manager.add_span_exporter(mock_exporter, batch=False, settings=settings)
179
+
180
+ tracer = trace.get_tracer("test")
181
+ with tracer.start_as_current_span("no-attrs"):
182
+ pass
183
+ with tracer.start_as_current_span(
184
+ "has-required", attributes={"required": True}
185
+ ):
186
+ pass
187
+
188
+ trace_manager.flush_spans()
189
+
190
+ exported_spans = []
191
+ for call in mock_exporter.export.call_args_list:
192
+ exported_spans.extend(call[0][0])
193
+
194
+ exported_names = {s.name for s in exported_spans}
195
+ assert "has-required" in exported_names
196
+ assert "no-attrs" not in exported_names
197
+
198
+ def test_different_filters_per_exporter(self):
199
+ """Test that different exporters can have different filters."""
200
+ from unittest.mock import MagicMock
201
+
202
+ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
203
+
204
+ mock_exporter_a = MagicMock(spec=SpanExporter)
205
+ mock_exporter_a.export.return_value = SpanExportResult.SUCCESS
206
+
207
+ mock_exporter_b = MagicMock(spec=SpanExporter)
208
+ mock_exporter_b.export.return_value = SpanExportResult.SUCCESS
209
+
210
+ settings_a = UiPathTraceSettings(
211
+ span_filter=lambda span: span.attributes is not None
212
+ and span.attributes.get("dest") == "a"
213
+ )
214
+ settings_b = UiPathTraceSettings(
215
+ span_filter=lambda span: span.attributes is not None
216
+ and span.attributes.get("dest") == "b"
217
+ )
218
+
219
+ trace_manager = UiPathTraceManager()
220
+ trace_manager.add_span_exporter(
221
+ mock_exporter_a, batch=False, settings=settings_a
222
+ )
223
+ trace_manager.add_span_exporter(
224
+ mock_exporter_b, batch=False, settings=settings_b
225
+ )
226
+
227
+ tracer = trace.get_tracer("test")
228
+ with tracer.start_as_current_span("to-a", attributes={"dest": "a"}):
229
+ pass
230
+ with tracer.start_as_current_span("to-b", attributes={"dest": "b"}):
231
+ pass
232
+ with tracer.start_as_current_span("to-neither", attributes={"dest": "c"}):
233
+ pass
234
+
235
+ trace_manager.flush_spans()
236
+
237
+ exported_a = []
238
+ for call in mock_exporter_a.export.call_args_list:
239
+ exported_a.extend(call[0][0])
240
+ assert {s.name for s in exported_a} == {"to-a"}
241
+
242
+ exported_b = []
243
+ for call in mock_exporter_b.export.call_args_list:
244
+ exported_b.extend(call[0][0])
245
+ assert {s.name for s in exported_b} == {"to-b"}
@@ -430,20 +430,20 @@ wheels = [
430
430
 
431
431
  [[package]]
432
432
  name = "opentelemetry-api"
433
- version = "1.39.0"
433
+ version = "1.39.1"
434
434
  source = { registry = "https://pypi.org/simple" }
435
435
  dependencies = [
436
436
  { name = "importlib-metadata" },
437
437
  { name = "typing-extensions" },
438
438
  ]
439
- sdist = { url = "https://files.pythonhosted.org/packages/c0/0b/e5428c009d4d9af0515b0a8371a8aaae695371af291f45e702f7969dce6b/opentelemetry_api-1.39.0.tar.gz", hash = "sha256:6130644268c5ac6bdffaf660ce878f10906b3e789f7e2daa5e169b047a2933b9", size = 65763, upload-time = "2025-12-03T13:19:56.378Z" }
439
+ sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" }
440
440
  wheels = [
441
- { url = "https://files.pythonhosted.org/packages/05/85/d831a9bc0a9e0e1a304ff3d12c1489a5fbc9bf6690a15dcbdae372bbca45/opentelemetry_api-1.39.0-py3-none-any.whl", hash = "sha256:3c3b3ca5c5687b1b5b37e5c5027ff68eacea8675241b29f13110a8ffbb8f0459", size = 66357, upload-time = "2025-12-03T13:19:33.043Z" },
441
+ { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" },
442
442
  ]
443
443
 
444
444
  [[package]]
445
445
  name = "opentelemetry-instrumentation"
446
- version = "0.60b0"
446
+ version = "0.60b1"
447
447
  source = { registry = "https://pypi.org/simple" }
448
448
  dependencies = [
449
449
  { name = "opentelemetry-api" },
@@ -451,36 +451,36 @@ dependencies = [
451
451
  { name = "packaging" },
452
452
  { name = "wrapt" },
453
453
  ]
454
- sdist = { url = "https://files.pythonhosted.org/packages/55/3c/bd53dbb42eff93d18e3047c7be11224aa9966ce98ac4cc5bfb860a32c95a/opentelemetry_instrumentation-0.60b0.tar.gz", hash = "sha256:4e9fec930f283a2677a2217754b40aaf9ef76edae40499c165bc7f1d15366a74", size = 31707, upload-time = "2025-12-03T13:22:00.352Z" }
454
+ sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" }
455
455
  wheels = [
456
- { url = "https://files.pythonhosted.org/packages/5c/7b/5b5b9f8cfe727a28553acf9cd287b1d7f706f5c0a00d6e482df55b169483/opentelemetry_instrumentation-0.60b0-py3-none-any.whl", hash = "sha256:aaafa1483543a402819f1bdfb06af721c87d60dd109501f9997332862a35c76a", size = 33096, upload-time = "2025-12-03T13:20:51.785Z" },
456
+ { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" },
457
457
  ]
458
458
 
459
459
  [[package]]
460
460
  name = "opentelemetry-sdk"
461
- version = "1.39.0"
461
+ version = "1.39.1"
462
462
  source = { registry = "https://pypi.org/simple" }
463
463
  dependencies = [
464
464
  { name = "opentelemetry-api" },
465
465
  { name = "opentelemetry-semantic-conventions" },
466
466
  { name = "typing-extensions" },
467
467
  ]
468
- sdist = { url = "https://files.pythonhosted.org/packages/51/e3/7cd989003e7cde72e0becfe830abff0df55c69d237ee7961a541e0167833/opentelemetry_sdk-1.39.0.tar.gz", hash = "sha256:c22204f12a0529e07aa4d985f1bca9d6b0e7b29fe7f03e923548ae52e0e15dde", size = 171322, upload-time = "2025-12-03T13:20:09.651Z" }
468
+ sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" }
469
469
  wheels = [
470
- { url = "https://files.pythonhosted.org/packages/a4/b4/2adc8bc83eb1055ecb592708efb6f0c520cc2eb68970b02b0f6ecda149cf/opentelemetry_sdk-1.39.0-py3-none-any.whl", hash = "sha256:90cfb07600dfc0d2de26120cebc0c8f27e69bf77cd80ef96645232372709a514", size = 132413, upload-time = "2025-12-03T13:19:51.364Z" },
470
+ { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" },
471
471
  ]
472
472
 
473
473
  [[package]]
474
474
  name = "opentelemetry-semantic-conventions"
475
- version = "0.60b0"
475
+ version = "0.60b1"
476
476
  source = { registry = "https://pypi.org/simple" }
477
477
  dependencies = [
478
478
  { name = "opentelemetry-api" },
479
479
  { name = "typing-extensions" },
480
480
  ]
481
- sdist = { url = "https://files.pythonhosted.org/packages/71/0e/176a7844fe4e3cb5de604212094dffaed4e18b32f1c56b5258bcbcba85c2/opentelemetry_semantic_conventions-0.60b0.tar.gz", hash = "sha256:227d7aa73cbb8a2e418029d6b6465553aa01cf7e78ec9d0bc3255c7b3ac5bf8f", size = 137935, upload-time = "2025-12-03T13:20:12.395Z" }
481
+ sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" }
482
482
  wheels = [
483
- { url = "https://files.pythonhosted.org/packages/d0/56/af0306666f91bae47db14d620775604688361f0f76a872e0005277311131/opentelemetry_semantic_conventions-0.60b0-py3-none-any.whl", hash = "sha256:069530852691136018087b52688857d97bba61cd641d0f8628d2d92788c4f78a", size = 219981, upload-time = "2025-12-03T13:19:53.585Z" },
483
+ { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" },
484
484
  ]
485
485
 
486
486
  [[package]]
@@ -991,7 +991,7 @@ wheels = [
991
991
 
992
992
  [[package]]
993
993
  name = "uipath-core"
994
- version = "0.1.10"
994
+ version = "0.2.1"
995
995
  source = { editable = "." }
996
996
  dependencies = [
997
997
  { name = "opentelemetry-instrumentation" },
@@ -1,46 +0,0 @@
1
- """Custom span processors for UiPath execution tracing."""
2
-
3
- from typing import Optional, cast
4
-
5
- from opentelemetry import context as context_api
6
- from opentelemetry import trace
7
- from opentelemetry.sdk.trace import Span
8
- from opentelemetry.sdk.trace.export import (
9
- BatchSpanProcessor,
10
- SimpleSpanProcessor,
11
- )
12
-
13
-
14
- class UiPathExecutionTraceProcessorMixin:
15
- def on_start(
16
- self, span: Span, parent_context: Optional[context_api.Context] = None
17
- ):
18
- """Called when a span is started."""
19
- parent_span: Optional[Span]
20
- if parent_context:
21
- parent_span = cast(Span, trace.get_current_span(parent_context))
22
- else:
23
- parent_span = cast(Span, trace.get_current_span())
24
-
25
- if parent_span and parent_span.is_recording() and parent_span.attributes:
26
- execution_id = parent_span.attributes.get("execution.id")
27
- if execution_id:
28
- span.set_attribute("execution.id", execution_id)
29
-
30
-
31
- class UiPathExecutionBatchTraceProcessor(
32
- UiPathExecutionTraceProcessorMixin, BatchSpanProcessor
33
- ):
34
- """Batch span processor that propagates execution.id."""
35
-
36
-
37
- class UiPathExecutionSimpleTraceProcessor(
38
- UiPathExecutionTraceProcessorMixin, SimpleSpanProcessor
39
- ):
40
- """Simple span processor that propagates execution.id."""
41
-
42
-
43
- __all__ = [
44
- "UiPathExecutionBatchTraceProcessor",
45
- "UiPathExecutionSimpleTraceProcessor",
46
- ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes