uipath 2.1.27__py3-none-any.whl → 2.1.29__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.
@@ -0,0 +1,119 @@
1
+ import logging
2
+ from datetime import datetime
3
+ from typing import Callable, Sequence
4
+
5
+ from opentelemetry import trace
6
+ from opentelemetry.sdk.trace import Event, ReadableSpan
7
+ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
8
+ from opentelemetry.trace import StatusCode
9
+
10
+ from .._models._messages import LogMessage, TraceMessage
11
+
12
+
13
+ class RunContextExporter(SpanExporter):
14
+ """Custom trace exporter that sends traces and logs to CLI UI."""
15
+
16
+ def __init__(
17
+ self,
18
+ on_trace: Callable[[TraceMessage], None],
19
+ on_log: Callable[[LogMessage], None],
20
+ ):
21
+ self.on_trace = on_trace
22
+ self.on_log = on_log
23
+ self.logger = logging.getLogger(__name__)
24
+
25
+ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
26
+ """Export spans to CLI UI."""
27
+ try:
28
+ for span in spans:
29
+ self._export_span(span)
30
+ return SpanExportResult.SUCCESS
31
+ except Exception as e:
32
+ self.logger.error(f"Failed to export spans: {e}")
33
+ return SpanExportResult.FAILURE
34
+
35
+ def _export_span(self, span: ReadableSpan):
36
+ """Export a single span to CLI UI."""
37
+ # Calculate duration
38
+ start_time = (
39
+ span.start_time / 1_000_000_000 if span.start_time is not None else 0
40
+ )
41
+ end_time = span.end_time / 1_000_000_000 if span.end_time is not None else None
42
+ duration_ms = (end_time - start_time) * 1000 if end_time else None
43
+
44
+ # Determine status
45
+ if span.status.status_code == StatusCode.ERROR:
46
+ status = "failed"
47
+ elif end_time:
48
+ status = "completed"
49
+ else:
50
+ status = "running"
51
+
52
+ # Extract span context information
53
+ span_context = span.get_span_context()
54
+
55
+ # Convert span IDs to string format (they're usually int64)
56
+ span_id = f"{span_context.span_id:016x}" # 16-char hex string
57
+ trace_id = f"{span_context.trace_id:032x}" # 32-char hex string
58
+
59
+ run_id = span.attributes.get("execution.id") if span.attributes else None
60
+ run_id_val = str(run_id) if run_id is not None else None
61
+
62
+ if run_id_val is None:
63
+ return
64
+
65
+ # Get parent span ID if available
66
+ parent_span_id = None
67
+ if hasattr(span, "parent") and span.parent:
68
+ parent_span_id = f"{span.parent.span_id:016x}"
69
+
70
+ # Create trace message with all required fields
71
+ trace_msg = TraceMessage(
72
+ run_id=run_id_val,
73
+ span_name=span.name,
74
+ span_id=span_id,
75
+ parent_span_id=parent_span_id,
76
+ trace_id=trace_id,
77
+ status=status,
78
+ duration_ms=duration_ms,
79
+ timestamp=datetime.fromtimestamp(start_time),
80
+ attributes=dict(span.attributes) if span.attributes else {},
81
+ )
82
+
83
+ # Send to UI
84
+ self.on_trace(trace_msg)
85
+
86
+ # Also send logs if there are events
87
+ if hasattr(span, "events") and span.events:
88
+ for event in span.events:
89
+ log_level = self._determine_log_level(event, span.status)
90
+ log_msg = LogMessage(
91
+ run_id=run_id_val,
92
+ level=log_level,
93
+ message=event.name,
94
+ timestamp=datetime.fromtimestamp(event.timestamp / 1_000_000_000),
95
+ )
96
+ self.on_log(log_msg)
97
+
98
+ def _determine_log_level(self, event: Event, span_status: trace.Status) -> str:
99
+ """Determine log level from span event."""
100
+ event_name = event.name.lower()
101
+
102
+ if span_status.status_code == StatusCode.ERROR:
103
+ return "ERROR"
104
+ elif "error" in event_name or "exception" in event_name:
105
+ return "ERROR"
106
+ elif "warn" in event_name:
107
+ return "WARNING"
108
+ elif "debug" in event_name:
109
+ return "DEBUG"
110
+ else:
111
+ return "INFO"
112
+
113
+ def shutdown(self) -> None:
114
+ """Shutdown the exporter."""
115
+ pass
116
+
117
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
118
+ """Force flush any pending spans."""
119
+ return True
@@ -0,0 +1,32 @@
1
+ import logging
2
+ from datetime import datetime
3
+ from typing import Callable
4
+
5
+ from .._models._messages import LogMessage
6
+
7
+
8
+ class RunContextLogHandler(logging.Handler):
9
+ """Custom log handler that sends logs to CLI UI."""
10
+
11
+ def __init__(
12
+ self,
13
+ run_id: str,
14
+ on_log: Callable[[LogMessage], None],
15
+ ):
16
+ super().__init__()
17
+ self.run_id = run_id
18
+ self.on_log = on_log
19
+
20
+ def emit(self, record: logging.LogRecord):
21
+ """Emit a log record to CLI UI."""
22
+ try:
23
+ log_msg = LogMessage(
24
+ run_id=self.run_id,
25
+ level=record.levelname,
26
+ message=self.format(record),
27
+ timestamp=datetime.fromtimestamp(record.created),
28
+ )
29
+ self.on_log(log_msg)
30
+ except Exception:
31
+ # Don't let logging errors crash the app
32
+ pass
@@ -395,10 +395,8 @@ class SwFileHandler:
395
395
  return toml_data.get("authors", "").strip()
396
396
 
397
397
  try:
398
- entrypoints = [
399
- {"input": entrypoint["input"], "output": entrypoint["output"]}
400
- for entrypoint in uipath_config["entryPoints"]
401
- ]
398
+ input_schema = uipath_config["entryPoints"][0]["input"]
399
+ output_schema = uipath_config["entryPoints"][0]["output"]
402
400
  except (FileNotFoundError, KeyError) as e:
403
401
  self.console.error(
404
402
  f"Unable to extract entrypoints from configuration file. Please run 'uipath init' : {str(e)}",
@@ -417,10 +415,12 @@ class SwFileHandler:
417
415
  "author": author,
418
416
  "pushDate": datetime.now(timezone.utc).isoformat(),
419
417
  },
420
- "entryPoints": entrypoints,
418
+ "inputSchema": input_schema,
419
+ "outputSchema": output_schema,
421
420
  "bindings": uipath_config.get(
422
421
  "bindings", {"version": "2.0", "resources": []}
423
422
  ),
423
+ "settings": {},
424
424
  }
425
425
 
426
426
  existing = root_files.get("agent.json")
@@ -8,10 +8,22 @@ import traceback
8
8
  from abc import ABC, abstractmethod
9
9
  from enum import Enum
10
10
  from functools import cached_property
11
- from typing import Any, Dict, Generic, Optional, Type, TypeVar, Union
12
-
11
+ from typing import Any, Callable, Dict, Generic, List, Optional, Type, TypeVar, Union
12
+
13
+ from opentelemetry import context as context_api
14
+ from opentelemetry import trace
15
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor # type: ignore
16
+ from opentelemetry.sdk.trace import Span, SpanProcessor, TracerProvider
17
+ from opentelemetry.sdk.trace.export import (
18
+ BatchSpanProcessor,
19
+ SimpleSpanProcessor,
20
+ SpanExporter,
21
+ )
22
+ from opentelemetry.trace import Tracer
13
23
  from pydantic import BaseModel, Field
14
24
 
25
+ from uipath.tracing import TracingManager
26
+
15
27
  from ._logging import LogsInterceptor
16
28
 
17
29
  logger = logging.getLogger(__name__)
@@ -144,6 +156,7 @@ class UiPathRuntimeContext(BaseModel):
144
156
  input: Optional[str] = None
145
157
  input_json: Optional[Any] = None
146
158
  job_id: Optional[str] = None
159
+ execution_id: Optional[str] = None
147
160
  trace_id: Optional[str] = None
148
161
  trace_context: Optional[UiPathTraceContext] = None
149
162
  tracing_enabled: Union[bool, str] = False
@@ -159,7 +172,7 @@ class UiPathRuntimeContext(BaseModel):
159
172
  execution_output_file: Optional[str] = None
160
173
  input_file: Optional[str] = None
161
174
  is_eval_run: bool = False
162
-
175
+ log_handler: Optional[logging.Handler] = None
163
176
  model_config = {"arbitrary_types_allowed": True}
164
177
 
165
178
  @classmethod
@@ -328,6 +341,7 @@ class UiPathBaseRuntime(ABC):
328
341
  file=self.context.logs_file,
329
342
  job_id=self.context.job_id,
330
343
  is_debug_run=self.is_debug_run(),
344
+ log_handler=self.context.log_handler,
331
345
  )
332
346
  self.logs_interceptor.setup()
333
347
 
@@ -487,6 +501,34 @@ class UiPathRuntimeFactory(Generic[T, C]):
487
501
 
488
502
  self.runtime_class = runtime_class
489
503
  self.context_class = context_class
504
+ self.tracer_provider: TracerProvider = TracerProvider()
505
+ self.tracer_span_processors: List[SpanProcessor] = []
506
+ trace.set_tracer_provider(self.tracer_provider)
507
+
508
+ def add_span_exporter(
509
+ self,
510
+ span_exporter: SpanExporter,
511
+ batch: bool = True,
512
+ ) -> "UiPathRuntimeFactory[T, C]":
513
+ """Add a span processor to the tracer provider."""
514
+ span_processor: SpanProcessor
515
+ if batch:
516
+ span_processor = UiPathExecutionBatchTraceProcessor(span_exporter)
517
+ else:
518
+ span_processor = UiPathExecutionSimpleTraceProcessor(span_exporter)
519
+ self.tracer_span_processors.append(span_processor)
520
+ self.tracer_provider.add_span_processor(span_processor)
521
+ return self
522
+
523
+ def add_instrumentor(
524
+ self,
525
+ instrumentor_class: Type[BaseInstrumentor],
526
+ get_current_span_func: Callable[[], Optional[Span]],
527
+ ) -> "UiPathRuntimeFactory[T, C]":
528
+ """Add and instrument immediately."""
529
+ instrumentor_class().instrument(tracer_provider=self.tracer_provider)
530
+ TracingManager.register_current_span_provider(get_current_span_func)
531
+ return self
490
532
 
491
533
  def new_context(self, **kwargs) -> C:
492
534
  """Create a new context instance."""
@@ -499,4 +541,52 @@ class UiPathRuntimeFactory(Generic[T, C]):
499
541
  async def execute(self, context: C) -> Optional[UiPathRuntimeResult]:
500
542
  """Execute runtime with context."""
501
543
  async with self.from_context(context) as runtime:
502
- return await runtime.execute()
544
+ result = await runtime.execute()
545
+ for span_processor in self.tracer_span_processors:
546
+ span_processor.force_flush()
547
+ return result
548
+
549
+ async def execute_in_root_span(
550
+ self, context: C, root_span: str = "root"
551
+ ) -> Optional[UiPathRuntimeResult]:
552
+ """Execute runtime with context."""
553
+ async with self.from_context(context) as runtime:
554
+ tracer: Tracer = trace.get_tracer("uipath-runtime")
555
+ with tracer.start_as_current_span(
556
+ root_span,
557
+ attributes={"execution.id": context.execution_id}
558
+ if context.execution_id
559
+ else {},
560
+ ):
561
+ result = await runtime.execute()
562
+ for span_processor in self.tracer_span_processors:
563
+ span_processor.force_flush()
564
+ return result
565
+
566
+
567
+ class UiPathExecutionTraceProcessorMixin:
568
+ def on_start(
569
+ self, span: Span, parent_context: Optional[context_api.Context] = None
570
+ ):
571
+ """Called when a span is started."""
572
+ if parent_context:
573
+ parent_span = trace.get_current_span(parent_context)
574
+ else:
575
+ parent_span = trace.get_current_span()
576
+
577
+ if parent_span and parent_span.is_recording():
578
+ run_id = parent_span.attributes.get("execution.id") # type: ignore[attr-defined]
579
+ if run_id:
580
+ span.set_attribute("execution.id", run_id)
581
+
582
+
583
+ class UiPathExecutionBatchTraceProcessor(
584
+ UiPathExecutionTraceProcessorMixin, BatchSpanProcessor
585
+ ):
586
+ """Batch span processor that propagates execution.id."""
587
+
588
+
589
+ class UiPathExecutionSimpleTraceProcessor(
590
+ UiPathExecutionTraceProcessorMixin, SimpleSpanProcessor
591
+ ):
592
+ """Simple span processor that propagates execution.id."""
@@ -30,6 +30,7 @@ class LogsInterceptor:
30
30
  file: Optional[str] = "execution.log",
31
31
  job_id: Optional[str] = None,
32
32
  is_debug_run: bool = False,
33
+ log_handler: Optional[logging.Handler] = None,
33
34
  ):
34
35
  """Initialize the log interceptor.
35
36
 
@@ -39,6 +40,7 @@ class LogsInterceptor:
39
40
  file (str): The log file name.
40
41
  job_id (str, optional): If provided, logs go to file; otherwise, to stdout.
41
42
  is_debug_run (bool, optional): If True, log the output to stdout/stderr.
43
+ log_handler (logging.Handler, optional): Custom log handler to use.
42
44
  """
43
45
  min_level = min_level or "INFO"
44
46
  self.job_id = job_id
@@ -57,21 +59,26 @@ class LogsInterceptor:
57
59
  self.original_stdout = cast(TextIO, sys.stdout)
58
60
  self.original_stderr = cast(TextIO, sys.stderr)
59
61
 
60
- self.log_handler: Union[PersistentLogsHandler, logging.StreamHandler[TextIO]]
62
+ self.log_handler: Union[
63
+ PersistentLogsHandler, logging.StreamHandler[TextIO], logging.Handler
64
+ ]
61
65
 
62
- # Create either file handler (runtime) or stdout handler (debug)
63
- if is_debug_run:
64
- # Use stdout handler when not running as a job or eval
65
- self.log_handler = logging.StreamHandler(sys.stdout)
66
- formatter = logging.Formatter("%(message)s")
67
- self.log_handler.setFormatter(formatter)
66
+ if log_handler:
67
+ self.log_handler = log_handler
68
68
  else:
69
- # Ensure directory exists for file logging
70
- dir = dir or "__uipath"
71
- file = file or "execution.log"
72
- os.makedirs(dir, exist_ok=True)
73
- log_file = os.path.join(dir, file)
74
- self.log_handler = PersistentLogsHandler(file=log_file)
69
+ # Create either file handler (runtime) or stdout handler (debug)
70
+ if is_debug_run:
71
+ # Use stdout handler when not running as a job or eval
72
+ self.log_handler = logging.StreamHandler(sys.stdout)
73
+ formatter = logging.Formatter("%(message)s")
74
+ self.log_handler.setFormatter(formatter)
75
+ else:
76
+ # Ensure directory exists for file logging
77
+ dir = dir or "__uipath"
78
+ file = file or "execution.log"
79
+ os.makedirs(dir, exist_ok=True)
80
+ log_file = os.path.join(dir, file)
81
+ self.log_handler = PersistentLogsHandler(file=log_file)
75
82
 
76
83
  self.log_handler.setLevel(self.numeric_min_level)
77
84
  self.logger = logging.getLogger("runtime")
@@ -8,13 +8,8 @@ import os
8
8
  from dataclasses import asdict, is_dataclass
9
9
  from typing import Any, Dict, Optional, Type, TypeVar, cast, get_type_hints
10
10
 
11
- from opentelemetry import trace
12
- from opentelemetry.sdk.trace import TracerProvider
13
- from opentelemetry.sdk.trace.export import BatchSpanProcessor
14
11
  from pydantic import BaseModel
15
12
 
16
- from uipath.tracing import LlmOpsHttpExporter
17
-
18
13
  from ._contracts import (
19
14
  UiPathBaseRuntime,
20
15
  UiPathErrorCategory,
@@ -43,11 +38,6 @@ class UiPathRuntime(UiPathBaseRuntime):
43
38
  await self.validate()
44
39
 
45
40
  try:
46
- trace.set_tracer_provider(TracerProvider())
47
- trace.get_tracer_provider().add_span_processor( # type: ignore
48
- BatchSpanProcessor(LlmOpsHttpExporter())
49
- )
50
-
51
41
  if self.context.entrypoint is None:
52
42
  return None
53
43
 
@@ -74,8 +64,6 @@ class UiPathRuntime(UiPathBaseRuntime):
74
64
  f"Error: {str(e)}",
75
65
  UiPathErrorCategory.SYSTEM,
76
66
  ) from e
77
- finally:
78
- trace.get_tracer_provider().shutdown() # type: ignore
79
67
 
80
68
  async def validate(self) -> None:
81
69
  """Validate runtime inputs."""
@@ -223,7 +211,7 @@ class UiPathRuntime(UiPathBaseRuntime):
223
211
  # Handle Pydantic models
224
212
  try:
225
213
  if inspect.isclass(cls) and issubclass(cls, BaseModel):
226
- return cast(T, cls.model_validate(data))
214
+ return cls.model_validate(data)
227
215
  except TypeError:
228
216
  # issubclass can raise TypeError if cls is not a class
229
217
  pass
@@ -247,7 +235,7 @@ class UiPathRuntime(UiPathBaseRuntime):
247
235
  value = self._convert_to_class(value, typed_field)
248
236
  converted_data[field_name] = value
249
237
 
250
- return cast(T, cls(**converted_data))
238
+ return cls(**converted_data)
251
239
 
252
240
  # Handle regular classes
253
241
  else:
uipath/_cli/cli_dev.py ADDED
@@ -0,0 +1,44 @@
1
+ import asyncio
2
+ from typing import Optional
3
+
4
+ import click
5
+
6
+ from ..telemetry import track
7
+ from ._dev._terminal import UiPathDevTerminal
8
+ from ._runtime._contracts import UiPathRuntimeContext, UiPathRuntimeFactory
9
+ from ._runtime._runtime import UiPathRuntime
10
+ from ._utils._console import ConsoleLogger
11
+ from .middlewares import Middlewares
12
+
13
+ console = ConsoleLogger()
14
+
15
+
16
+ @click.command()
17
+ @click.argument("interface", default="terminal")
18
+ @track
19
+ def dev(interface: Optional[str]) -> None:
20
+ """Launch interactive debugging interface."""
21
+ console.info("🚀 Starting UiPath Dev Terminal...")
22
+ console.info("Use 'q' to quit, 'n' for new run, 'r' to execute")
23
+
24
+ result = Middlewares.next(
25
+ "dev",
26
+ interface,
27
+ )
28
+
29
+ if result.should_continue is False:
30
+ return
31
+
32
+ try:
33
+ if interface == "terminal":
34
+ runtime_factory = UiPathRuntimeFactory(UiPathRuntime, UiPathRuntimeContext)
35
+ app = UiPathDevTerminal(runtime_factory)
36
+ asyncio.run(app.run_async())
37
+ else:
38
+ console.error(f"Unknown interface: {interface}")
39
+ except KeyboardInterrupt:
40
+ console.info("Debug session interrupted by user")
41
+ except Exception as e:
42
+ console.error(
43
+ f"Error running debug interface: {str(e)}", include_traceback=True
44
+ )
uipath/_cli/cli_run.py CHANGED
@@ -10,6 +10,7 @@ import click
10
10
  from dotenv import load_dotenv
11
11
 
12
12
  from uipath._cli._utils._debug import setup_debugging
13
+ from uipath.tracing import LlmOpsHttpExporter
13
14
 
14
15
  from .._utils.constants import (
15
16
  ENV_JOB_ID,
@@ -18,6 +19,7 @@ from ..telemetry import track
18
19
  from ._runtime._contracts import (
19
20
  UiPathRuntimeContext,
20
21
  UiPathRuntimeError,
22
+ UiPathRuntimeFactory,
21
23
  UiPathTraceContext,
22
24
  )
23
25
  from ._runtime._runtime import UiPathRuntime
@@ -61,39 +63,42 @@ Usage: `uipath run <entrypoint_path> <input_arguments> [-f <input_json_file_path
61
63
  )
62
64
 
63
65
  try:
66
+ context = UiPathRuntimeContext.from_config(
67
+ env.get("UIPATH_CONFIG_PATH", "uipath.json"), **kwargs
68
+ )
69
+ context.entrypoint = entrypoint
70
+ context.input = input
71
+ context.resume = resume
72
+ context.job_id = env.get("UIPATH_JOB_KEY")
73
+ context.trace_id = env.get("UIPATH_TRACE_ID")
74
+ context.input_file = kwargs.get("input_file", None)
75
+ context.execution_output_file = kwargs.get("execution_output_file", None)
76
+ context.is_eval_run = kwargs.get("is_eval_run", False)
77
+ context.logs_min_level = env.get("LOG_LEVEL", "INFO")
78
+ context.tracing_enabled = env.get("UIPATH_TRACING_ENABLED", True)
79
+ context.trace_context = UiPathTraceContext(
80
+ trace_id=env.get("UIPATH_TRACE_ID"),
81
+ parent_span_id=env.get("UIPATH_PARENT_SPAN_ID"),
82
+ root_span_id=env.get("UIPATH_ROOT_SPAN_ID"),
83
+ enabled=env.get("UIPATH_TRACING_ENABLED", True),
84
+ job_id=env.get("UIPATH_JOB_KEY"),
85
+ org_id=env.get("UIPATH_ORGANIZATION_ID"),
86
+ tenant_id=env.get("UIPATH_TENANT_ID"),
87
+ process_key=env.get("UIPATH_PROCESS_UUID"),
88
+ folder_key=env.get("UIPATH_FOLDER_KEY"),
89
+ reference_id=env.get("UIPATH_JOB_KEY") or str(uuid4()),
90
+ )
91
+
92
+ runtime_factory = UiPathRuntimeFactory(UiPathRuntime, UiPathRuntimeContext)
93
+
94
+ if context.job_id:
95
+ runtime_factory.add_span_exporter(LlmOpsHttpExporter())
64
96
 
65
97
  async def execute():
66
- context = UiPathRuntimeContext.from_config(
67
- env.get("UIPATH_CONFIG_PATH", "uipath.json"), **kwargs
68
- )
69
- context.entrypoint = entrypoint
70
- context.input = input
71
- context.resume = resume
72
- context.job_id = env.get("UIPATH_JOB_KEY")
73
- context.trace_id = env.get("UIPATH_TRACE_ID")
74
- context.input_file = kwargs.get("input_file", None)
75
- context.execution_output_file = kwargs.get("execution_output_file", None)
76
- context.is_eval_run = kwargs.get("is_eval_run", False)
77
- context.tracing_enabled = env.get("UIPATH_TRACING_ENABLED", True)
78
- context.trace_context = UiPathTraceContext(
79
- trace_id=env.get("UIPATH_TRACE_ID"),
80
- parent_span_id=env.get("UIPATH_PARENT_SPAN_ID"),
81
- root_span_id=env.get("UIPATH_ROOT_SPAN_ID"),
82
- enabled=env.get("UIPATH_TRACING_ENABLED", True),
83
- job_id=env.get("UIPATH_JOB_KEY"),
84
- org_id=env.get("UIPATH_ORGANIZATION_ID"),
85
- tenant_id=env.get("UIPATH_TENANT_ID"),
86
- process_key=env.get("UIPATH_PROCESS_UUID"),
87
- folder_key=env.get("UIPATH_FOLDER_KEY"),
88
- reference_id=env.get("UIPATH_JOB_KEY") or str(uuid4()),
89
- )
90
- context.logs_min_level = env.get("LOG_LEVEL", "INFO")
91
- async with UiPathRuntime.from_context(context) as runtime:
92
- await runtime.execute()
98
+ await runtime_factory.execute(context)
93
99
 
94
100
  asyncio.run(execute())
95
101
 
96
- # Return success
97
102
  return MiddlewareResult(should_continue=False)
98
103
 
99
104
  except UiPathRuntimeError as e:
@@ -28,6 +28,7 @@ class Middlewares:
28
28
  "pack": [],
29
29
  "publish": [],
30
30
  "run": [],
31
+ "dev": [],
31
32
  }
32
33
  _plugins_loaded = False
33
34
 
@@ -58,7 +58,7 @@ def _get_project_key() -> str:
58
58
 
59
59
  class _AzureMonitorOpenTelemetryEventHandler(LoggingHandler):
60
60
  @staticmethod
61
- def _get_attributes(record: LogRecord) -> Attributes:
61
+ def _get_attributes(record: LogRecord) -> Attributes: # type: ignore[override]
62
62
  attributes = dict(LoggingHandler._get_attributes(record) or {})
63
63
  attributes[_APP_INSIGHTS_EVENT_MARKER_ATTRIBUTE] = True
64
64
  attributes[_CLOUD_TENANT_ID] = os.getenv(ENV_TENANT_ID, _UNKNOWN)
@@ -75,7 +75,7 @@ class _AzureMonitorOpenTelemetryEventHandler(LoggingHandler):
75
75
  if _CODE_LINENO in attributes:
76
76
  del attributes[_CODE_LINENO]
77
77
 
78
- return attributes
78
+ return attributes # type: ignore[return-value]
79
79
 
80
80
 
81
81
  class _TelemetryClient:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath
3
- Version: 2.1.27
3
+ Version: 2.1.29
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
@@ -17,12 +17,14 @@ Requires-Python: >=3.10
17
17
  Requires-Dist: azure-monitor-opentelemetry>=1.6.8
18
18
  Requires-Dist: click>=8.1.8
19
19
  Requires-Dist: httpx>=0.28.1
20
+ Requires-Dist: opentelemetry-instrumentation>=0.52b1
20
21
  Requires-Dist: opentelemetry-sdk>=1.31.1
21
22
  Requires-Dist: pathlib>=1.0.1
22
23
  Requires-Dist: pydantic>=2.11.1
23
24
  Requires-Dist: python-dotenv>=1.0.1
24
25
  Requires-Dist: rich>=13.0.0
25
26
  Requires-Dist: tenacity>=9.0.0
27
+ Requires-Dist: textual>=5.3.0
26
28
  Requires-Dist: tomli>=2.2.1
27
29
  Requires-Dist: truststore>=0.10.1
28
30
  Description-Content-Type: text/markdown