uipath 2.1.74__py3-none-any.whl → 2.1.76__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,241 @@
1
+ """Console progress reporter for evaluation runs with line-by-line output."""
2
+
3
+ import logging
4
+ from typing import Any, Dict
5
+
6
+ from rich.console import Console
7
+ from rich.rule import Rule
8
+ from rich.table import Table
9
+
10
+ from uipath._events._event_bus import EventBus
11
+ from uipath._events._events import (
12
+ EvalRunCreatedEvent,
13
+ EvalRunUpdatedEvent,
14
+ EvalSetRunCreatedEvent,
15
+ EvalSetRunUpdatedEvent,
16
+ EvaluationEvents,
17
+ )
18
+ from uipath.eval.evaluators import BaseEvaluator
19
+ from uipath.eval.models import ScoreType
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class ConsoleProgressReporter:
25
+ """Handles displaying evaluation progress to the console with line-by-line output."""
26
+
27
+ def __init__(self):
28
+ self.console = Console()
29
+ self.evaluators: Dict[str, BaseEvaluator[Any]] = {}
30
+ self.display_started = False
31
+ self.eval_results_by_name: Dict[str, list[Any]] = {}
32
+
33
+ def _convert_score_to_numeric(self, eval_result) -> float:
34
+ """Convert evaluation result score to numeric value."""
35
+ score_value = eval_result.result.score
36
+ if eval_result.result.score_type == ScoreType.BOOLEAN:
37
+ score_value = 100 if score_value else 0
38
+ return score_value
39
+
40
+ def _get_evaluator_name(self, evaluator_id: str) -> str:
41
+ """Get evaluator name from ID, with fallback."""
42
+ return self.evaluators.get(
43
+ evaluator_id,
44
+ type(
45
+ "obj",
46
+ (object,),
47
+ {"name": f"Evaluator {evaluator_id[:8]}"},
48
+ )(),
49
+ ).name
50
+
51
+ def _display_successful_evaluation(self, eval_name: str, eval_results) -> None:
52
+ """Display results for a successful evaluation."""
53
+ from rich.text import Text
54
+
55
+ if eval_results:
56
+ result = Text()
57
+ result.append("▌", style="bold green")
58
+ result.append(" ", style="")
59
+ result.append(eval_name, style="bold green")
60
+ self.console.print(result)
61
+ table = Table(show_header=False, box=None, padding=(0, 2, 0, 2))
62
+
63
+ for eval_result in eval_results:
64
+ evaluator_name = self._get_evaluator_name(eval_result.evaluator_id)
65
+ score_value = self._convert_score_to_numeric(eval_result)
66
+ table.add_row(
67
+ f"{evaluator_name}", f"[bold cyan]{score_value:.1f}[/bold cyan]"
68
+ )
69
+
70
+ self.console.print(table)
71
+ else:
72
+ result = Text()
73
+ result.append(" ✓ ", style="bold green")
74
+ result.append(eval_name, style="bold white")
75
+ result.append(" - No evaluators", style="dim")
76
+ self.console.print(result)
77
+
78
+ def _extract_error_message(self, payload: EvalRunUpdatedEvent) -> str:
79
+ return str(payload.exception_details.exception) or "Execution failed" # type: ignore
80
+
81
+ def _display_failed_evaluation(self, eval_name: str) -> None:
82
+ """Display results for a failed evaluation."""
83
+ from rich.text import Text
84
+
85
+ result = Text()
86
+ result.append(" ✗ ", style="bold red")
87
+ result.append(eval_name, style="bold white")
88
+ self.console.print(result)
89
+
90
+ def start_display(self):
91
+ """Start the display."""
92
+ if not self.display_started:
93
+ self.console.print()
94
+ self.console.print("→ [bold]Running Evaluations[/bold]")
95
+ self.console.print()
96
+ self.display_started = True
97
+
98
+ async def handle_create_eval_set_run(self, payload: EvalSetRunCreatedEvent) -> None:
99
+ """Handle evaluation set run creation."""
100
+ try:
101
+ self.evaluators = {eval.id: eval for eval in payload.evaluators}
102
+ except Exception as e:
103
+ logger.error(f"Failed to handle create eval set run event: {e}")
104
+
105
+ async def handle_create_eval_run(self, payload: EvalRunCreatedEvent) -> None:
106
+ """Handle individual evaluation run creation."""
107
+ try:
108
+ if not self.display_started:
109
+ self.start_display()
110
+
111
+ self.console.print(f" ○ [dim]{payload.eval_item.name}[/dim] - Running...")
112
+ except Exception as e:
113
+ logger.error(f"Failed to handle create eval run event: {e}")
114
+
115
+ def _display_logs_panel(self, eval_name: str, logs, error_msg: str = "") -> None:
116
+ """Display execution logs panel with optional exception at the end."""
117
+ self.console.print(
118
+ Rule(
119
+ f"[dim italic]Execution Logs: {eval_name}[/dim italic]",
120
+ style="dim",
121
+ align="center",
122
+ )
123
+ )
124
+
125
+ if logs:
126
+ for record in logs:
127
+ self.console.print(f" [dim]{record.getMessage()}[/dim]")
128
+ elif not error_msg:
129
+ self.console.print(" [dim italic]No execution logs[/dim italic]")
130
+
131
+ if error_msg:
132
+ self.console.print(f" [red]{error_msg}[/red]")
133
+
134
+ self.console.print(Rule(style="dim"))
135
+
136
+ async def handle_update_eval_run(self, payload: EvalRunUpdatedEvent) -> None:
137
+ """Handle evaluation run updates."""
138
+ try:
139
+ if payload.success:
140
+ self.eval_results_by_name[payload.eval_item.name] = payload.eval_results
141
+ self._display_successful_evaluation(
142
+ payload.eval_item.name, payload.eval_results
143
+ )
144
+ self._display_logs_panel(payload.eval_item.name, payload.logs)
145
+ else:
146
+ error_msg = self._extract_error_message(payload)
147
+ self._display_failed_evaluation(payload.eval_item.name)
148
+
149
+ if payload.exception_details.runtime_exception: # type: ignore
150
+ self._display_logs_panel(
151
+ payload.eval_item.name, payload.logs, error_msg
152
+ )
153
+ else:
154
+ self.console.print(f" [red]{error_msg}[/red]")
155
+ self.console.print()
156
+ except Exception as e:
157
+ logger.error(f"Console reporter error: {e}")
158
+
159
+ async def handle_update_eval_set_run(self, payload: EvalSetRunUpdatedEvent) -> None:
160
+ """Handle evaluation set run completion."""
161
+ try:
162
+ self.final_results = payload.evaluator_scores
163
+ self.display_final_results()
164
+ except Exception as e:
165
+ logger.error(f"Console reporter error: {e}")
166
+
167
+ def display_final_results(self):
168
+ """Display final results summary."""
169
+ self.console.print()
170
+
171
+ if hasattr(self, "final_results") and self.final_results:
172
+ from rich.table import Table
173
+
174
+ # Group evaluators by ID to organize display
175
+ evaluator_ids = list(self.final_results.keys())
176
+
177
+ # Print title
178
+ self.console.print("[bold]Evaluation Results[/bold]")
179
+ self.console.print()
180
+
181
+ # Create single summary table
182
+ summary_table = Table(show_header=True, padding=(0, 2))
183
+ summary_table.add_column("Evaluation", style="cyan")
184
+
185
+ # Add column for each evaluator
186
+ for evaluator_id in evaluator_ids:
187
+ evaluator_name = self._get_evaluator_name(evaluator_id)
188
+ summary_table.add_column(evaluator_name, justify="right")
189
+
190
+ # Add row for each evaluation
191
+ for eval_name, eval_results in self.eval_results_by_name.items():
192
+ row_values = [eval_name]
193
+
194
+ # Get score for each evaluator
195
+ for evaluator_id in evaluator_ids:
196
+ score_found = False
197
+ for eval_result in eval_results:
198
+ if eval_result.evaluator_id == evaluator_id:
199
+ score_value = self._convert_score_to_numeric(eval_result)
200
+ row_values.append(f"{score_value:.1f}")
201
+ score_found = True
202
+ break
203
+
204
+ if not score_found:
205
+ row_values.append("-")
206
+
207
+ summary_table.add_row(*row_values)
208
+
209
+ # Add separator row before average
210
+ summary_table.add_section()
211
+
212
+ # Add average row
213
+ avg_row_values = ["[bold]Average[/bold]"]
214
+ for evaluator_id in evaluator_ids:
215
+ avg_score = self.final_results[evaluator_id]
216
+ avg_row_values.append(f"[bold]{avg_score:.1f}[/bold]")
217
+
218
+ summary_table.add_row(*avg_row_values)
219
+
220
+ self.console.print(summary_table)
221
+ self.console.print()
222
+ else:
223
+ self.console.print(
224
+ "→ [bold green]All evaluations completed successfully![/bold green]"
225
+ )
226
+ self.console.print()
227
+
228
+ async def subscribe_to_eval_runtime_events(self, event_bus: EventBus) -> None:
229
+ """Subscribe to evaluation runtime events."""
230
+ event_bus.subscribe(
231
+ EvaluationEvents.CREATE_EVAL_SET_RUN, self.handle_create_eval_set_run
232
+ )
233
+ event_bus.subscribe(
234
+ EvaluationEvents.CREATE_EVAL_RUN, self.handle_create_eval_run
235
+ )
236
+ event_bus.subscribe(
237
+ EvaluationEvents.UPDATE_EVAL_RUN, self.handle_update_eval_run
238
+ )
239
+ event_bus.subscribe(
240
+ EvaluationEvents.UPDATE_EVAL_SET_RUN, self.handle_update_eval_set_run
241
+ )
@@ -0,0 +1,5 @@
1
+ class EvaluationRuntimeException(Exception):
2
+ def __init__(self, spans, logs, root_exception):
3
+ self.spans = spans
4
+ self.logs = logs
5
+ self.root_exception = root_exception
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  from typing import List, Optional
2
3
 
3
4
  from opentelemetry.sdk.trace import ReadableSpan
@@ -15,6 +16,7 @@ class UiPathEvalRunExecutionOutput(BaseModel):
15
16
 
16
17
  execution_time: float
17
18
  spans: list[ReadableSpan]
19
+ logs: list[logging.LogRecord]
18
20
  result: UiPathRuntimeResult
19
21
 
20
22
 
@@ -7,6 +7,7 @@ import os
7
7
  from typing import Any, Dict, List
8
8
 
9
9
  from opentelemetry import trace
10
+ from rich.console import Console
10
11
 
11
12
  from uipath import UiPath
12
13
  from uipath._cli._evals._models._evaluation_set import EvaluationItem, EvaluationStatus
@@ -68,6 +69,7 @@ class StudioWebProgressReporter:
68
69
 
69
70
  self._client = uipath.api_client
70
71
  self._console = console_logger
72
+ self._rich_console = Console()
71
73
  self._project_id = os.getenv("UIPATH_PROJECT_ID", None)
72
74
  if not self._project_id:
73
75
  logger.warning(
@@ -79,6 +81,10 @@ class StudioWebProgressReporter:
79
81
  self.evaluator_scores: Dict[str, List[float]] = {}
80
82
  self.eval_run_ids: Dict[str, str] = {}
81
83
 
84
+ def _format_error_message(self, error: Exception, context: str) -> None:
85
+ """Helper method to format and display error messages consistently."""
86
+ self._rich_console.print(f" • \u26a0 [dim]{context}: {error}[/dim]")
87
+
82
88
  @gracefully_handle_errors
83
89
  async def create_eval_set_run(
84
90
  self,
@@ -182,7 +188,7 @@ class StudioWebProgressReporter:
182
188
  logger.debug(f"Created eval set run with ID: {eval_set_run_id}")
183
189
 
184
190
  except Exception as e:
185
- logger.error(f"Failed to handle create eval set run event: {e}")
191
+ self._format_error_message(e, "StudioWeb create eval set run error")
186
192
 
187
193
  async def handle_create_eval_run(self, payload: EvalRunCreatedEvent) -> None:
188
194
  try:
@@ -197,7 +203,7 @@ class StudioWebProgressReporter:
197
203
  logger.warning("Cannot create eval run: eval_set_run_id not available")
198
204
 
199
205
  except Exception as e:
200
- logger.error(f"Failed to handle create eval run event: {e}")
206
+ self._format_error_message(e, "StudioWeb create eval run error")
201
207
 
202
208
  async def handle_update_eval_run(self, payload: EvalRunUpdatedEvent) -> None:
203
209
  try:
@@ -238,7 +244,7 @@ class StudioWebProgressReporter:
238
244
  logger.debug(f"Updated eval run with ID: {eval_run_id}")
239
245
 
240
246
  except Exception as e:
241
- logger.error(f"Failed to handle update eval run event: {e}")
247
+ self._format_error_message(e, "StudioWeb reporting error")
242
248
 
243
249
  async def handle_update_eval_set_run(self, payload: EvalSetRunUpdatedEvent) -> None:
244
250
  try:
@@ -254,7 +260,7 @@ class StudioWebProgressReporter:
254
260
  )
255
261
 
256
262
  except Exception as e:
257
- logger.error(f"Failed to handle update eval set run event: {e}")
263
+ self._format_error_message(e, "StudioWeb update eval set run error")
258
264
 
259
265
  async def subscribe_to_eval_runtime_events(self, event_bus: EventBus) -> None:
260
266
  event_bus.subscribe(
@@ -270,7 +276,7 @@ class StudioWebProgressReporter:
270
276
  EvaluationEvents.UPDATE_EVAL_SET_RUN, self.handle_update_eval_set_run
271
277
  )
272
278
 
273
- logger.info("StudioWeb progress reporter subscribed to evaluation events")
279
+ logger.debug("StudioWeb progress reporter subscribed to evaluation events")
274
280
 
275
281
  def _extract_agent_snapshot(self, entrypoint: str) -> StudioWebAgentSnapshot:
276
282
  try:
@@ -1,4 +1,6 @@
1
1
  import json
2
+ import logging
3
+ import uuid
2
4
  from collections import defaultdict
3
5
  from pathlib import Path
4
6
  from time import time
@@ -9,6 +11,7 @@ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
9
11
 
10
12
  from ..._events._event_bus import EventBus
11
13
  from ..._events._events import (
14
+ EvalItemExceptionDetails,
12
15
  EvalRunCreatedEvent,
13
16
  EvalRunUpdatedEvent,
14
17
  EvalSetRunCreatedEvent,
@@ -25,9 +28,11 @@ from .._runtime._contracts import (
25
28
  UiPathRuntimeResult,
26
29
  UiPathRuntimeStatus,
27
30
  )
31
+ from .._runtime._logging import ExecutionLogHandler
28
32
  from .._utils._eval_set import EvalHelpers
29
33
  from ._evaluator_factory import EvaluatorFactory
30
34
  from ._models._evaluation_set import EvaluationItem, EvaluationSet
35
+ from ._models._exceptions import EvaluationRuntimeException
31
36
  from ._models._output import (
32
37
  EvaluationResultDto,
33
38
  EvaluationRunResult,
@@ -72,6 +77,33 @@ class ExecutionSpanExporter(SpanExporter):
72
77
  self.clear()
73
78
 
74
79
 
80
+ class ExecutionLogsExporter:
81
+ """Custom exporter that stores multiple execution log handlers."""
82
+
83
+ def __init__(self):
84
+ self._log_handlers: dict[str, ExecutionLogHandler] = {}
85
+
86
+ def register(self, execution_id: str, handler: ExecutionLogHandler) -> None:
87
+ self._log_handlers[execution_id] = handler
88
+
89
+ def get_logs(self, execution_id: str) -> list[logging.LogRecord]:
90
+ """Clear stored spans for one or all executions."""
91
+ log_handler = self._log_handlers.get(execution_id)
92
+ return log_handler.buffer if log_handler else []
93
+
94
+ def clear(self, execution_id: Optional[str] = None) -> None:
95
+ """Clear stored spans for one or all executions."""
96
+ if execution_id:
97
+ self._log_handlers.pop(execution_id, None)
98
+ else:
99
+ self._log_handlers.clear()
100
+
101
+ def flush_logs(self, execution_id: str, target_handler: logging.Handler) -> None:
102
+ log_handler = self._log_handlers.get(execution_id)
103
+ if log_handler:
104
+ log_handler.flush_execution_logs(target_handler)
105
+
106
+
75
107
  class UiPathEvalContext(UiPathRuntimeContext):
76
108
  """Context used for evaluation runs."""
77
109
 
@@ -96,6 +128,8 @@ class UiPathEvalRuntime(UiPathBaseRuntime, Generic[T, C]):
96
128
  self.event_bus: EventBus = event_bus
97
129
  self.span_exporter: ExecutionSpanExporter = ExecutionSpanExporter()
98
130
  self.factory.add_span_exporter(self.span_exporter)
131
+ self.logs_exporter: ExecutionLogsExporter = ExecutionLogsExporter()
132
+ self.execution_id = str(uuid.uuid4())
99
133
 
100
134
  @classmethod
101
135
  def from_eval_context(
@@ -110,9 +144,6 @@ class UiPathEvalRuntime(UiPathBaseRuntime, Generic[T, C]):
110
144
  if self.context.eval_set is None:
111
145
  raise ValueError("eval_set must be provided for evaluation runs")
112
146
 
113
- if not self.context.execution_id:
114
- raise ValueError("execution_id must be provided for evaluation runs")
115
-
116
147
  event_bus = self.event_bus
117
148
 
118
149
  evaluation_set = EvalHelpers.load_eval_set(
@@ -126,7 +157,7 @@ class UiPathEvalRuntime(UiPathBaseRuntime, Generic[T, C]):
126
157
  await event_bus.publish(
127
158
  EvaluationEvents.CREATE_EVAL_SET_RUN,
128
159
  EvalSetRunCreatedEvent(
129
- execution_id=self.context.execution_id,
160
+ execution_id=self.execution_id,
130
161
  entrypoint=self.context.entrypoint or "",
131
162
  eval_set_id=evaluation_set.id,
132
163
  no_of_evals=len(evaluation_set.evaluations),
@@ -142,7 +173,7 @@ class UiPathEvalRuntime(UiPathBaseRuntime, Generic[T, C]):
142
173
  await event_bus.publish(
143
174
  EvaluationEvents.CREATE_EVAL_RUN,
144
175
  EvalRunCreatedEvent(
145
- execution_id=self.context.execution_id,
176
+ execution_id=self.execution_id,
146
177
  eval_item=eval_item,
147
178
  ),
148
179
  )
@@ -153,60 +184,96 @@ class UiPathEvalRuntime(UiPathBaseRuntime, Generic[T, C]):
153
184
 
154
185
  results.evaluation_set_results.append(evaluation_run_results)
155
186
 
156
- agent_execution_output = await self.execute_runtime(eval_item)
157
- evaluation_item_results: list[EvalItemResult] = []
158
-
159
- for evaluator in evaluators:
160
- evaluation_result = await self.run_evaluator(
161
- evaluator=evaluator,
162
- execution_output=agent_execution_output,
163
- eval_item=eval_item,
164
- )
187
+ try:
188
+ agent_execution_output = await self.execute_runtime(eval_item)
189
+ evaluation_item_results: list[EvalItemResult] = []
190
+
191
+ for evaluator in evaluators:
192
+ evaluation_result = await self.run_evaluator(
193
+ evaluator=evaluator,
194
+ execution_output=agent_execution_output,
195
+ eval_item=eval_item,
196
+ )
165
197
 
166
- dto_result = EvaluationResultDto.from_evaluation_result(
167
- evaluation_result
168
- )
169
- evaluator_counts[evaluator.id] += 1
170
- count = evaluator_counts[evaluator.id]
171
- evaluator_averages[evaluator.id] += (
172
- dto_result.score - evaluator_averages[evaluator.id]
173
- ) / count
174
-
175
- evaluation_run_results.evaluation_run_results.append(
176
- EvaluationRunResultDto(
177
- evaluator_name=evaluator.name,
178
- result=dto_result,
198
+ dto_result = EvaluationResultDto.from_evaluation_result(
199
+ evaluation_result
179
200
  )
180
- )
181
- evaluation_item_results.append(
182
- EvalItemResult(
183
- evaluator_id=evaluator.id,
184
- result=evaluation_result,
201
+ evaluator_counts[evaluator.id] += 1
202
+ count = evaluator_counts[evaluator.id]
203
+ evaluator_averages[evaluator.id] += (
204
+ dto_result.score - evaluator_averages[evaluator.id]
205
+ ) / count
206
+
207
+ evaluation_run_results.evaluation_run_results.append(
208
+ EvaluationRunResultDto(
209
+ evaluator_name=evaluator.name,
210
+ result=dto_result,
211
+ )
185
212
  )
213
+ evaluation_item_results.append(
214
+ EvalItemResult(
215
+ evaluator_id=evaluator.id,
216
+ result=evaluation_result,
217
+ )
218
+ )
219
+
220
+ evaluation_run_results.compute_average_score()
221
+
222
+ await event_bus.publish(
223
+ EvaluationEvents.UPDATE_EVAL_RUN,
224
+ EvalRunUpdatedEvent(
225
+ execution_id=self.execution_id,
226
+ eval_item=eval_item,
227
+ eval_results=evaluation_item_results,
228
+ success=not agent_execution_output.result.error,
229
+ agent_output=agent_execution_output.result.output,
230
+ agent_execution_time=agent_execution_output.execution_time,
231
+ spans=agent_execution_output.spans,
232
+ logs=agent_execution_output.logs,
233
+ ),
234
+ wait_for_completion=False,
186
235
  )
236
+ except Exception as e:
237
+ exception_details = EvalItemExceptionDetails(exception=e)
187
238
 
188
- evaluation_run_results.compute_average_score()
239
+ for evaluator in evaluators:
240
+ evaluator_counts[evaluator.id] += 1
241
+ count = evaluator_counts[evaluator.id]
242
+ evaluator_averages[evaluator.id] += (
243
+ 0.0 - evaluator_averages[evaluator.id]
244
+ ) / count
189
245
 
190
- await event_bus.publish(
191
- EvaluationEvents.UPDATE_EVAL_RUN,
192
- EvalRunUpdatedEvent(
193
- execution_id=self.context.execution_id,
246
+ eval_run_updated_event = EvalRunUpdatedEvent(
247
+ execution_id=self.execution_id,
194
248
  eval_item=eval_item,
195
- eval_results=evaluation_item_results,
196
- success=not agent_execution_output.result.error,
197
- agent_output=agent_execution_output.result.output,
198
- agent_execution_time=agent_execution_output.execution_time,
199
- spans=agent_execution_output.spans,
200
- ),
201
- wait_for_completion=False,
202
- )
249
+ eval_results=[],
250
+ success=False,
251
+ agent_output={},
252
+ agent_execution_time=0.0,
253
+ exception_details=exception_details,
254
+ spans=[],
255
+ logs=[],
256
+ )
257
+ if isinstance(e, EvaluationRuntimeException):
258
+ eval_run_updated_event.spans = e.spans
259
+ eval_run_updated_event.logs = e.logs
260
+ eval_run_updated_event.exception_details.exception = ( # type: ignore
261
+ e.root_exception
262
+ )
263
+ eval_run_updated_event.exception_details.runtime_exception = True # type: ignore
264
+
265
+ await event_bus.publish(
266
+ EvaluationEvents.UPDATE_EVAL_RUN,
267
+ eval_run_updated_event,
268
+ wait_for_completion=False,
269
+ )
203
270
 
204
271
  results.compute_average_score()
205
272
 
206
273
  await event_bus.publish(
207
274
  EvaluationEvents.UPDATE_EVAL_SET_RUN,
208
275
  EvalSetRunUpdatedEvent(
209
- execution_id=self.context.execution_id,
276
+ execution_id=self.execution_id,
210
277
  evaluator_scores=evaluator_averages,
211
278
  ),
212
279
  wait_for_completion=False,
@@ -218,14 +285,30 @@ class UiPathEvalRuntime(UiPathBaseRuntime, Generic[T, C]):
218
285
  )
219
286
  return self.context.result
220
287
 
288
+ def _get_and_clear_execution_data(
289
+ self, execution_id: str
290
+ ) -> tuple[List[ReadableSpan], list[logging.LogRecord]]:
291
+ spans = self.span_exporter.get_spans(execution_id)
292
+ self.span_exporter.clear(execution_id)
293
+
294
+ logs = self.logs_exporter.get_logs(execution_id)
295
+ self.logs_exporter.clear(execution_id)
296
+
297
+ return spans, logs
298
+
221
299
  async def execute_runtime(
222
300
  self, eval_item: EvaluationItem
223
301
  ) -> UiPathEvalRunExecutionOutput:
302
+ eval_item_id = eval_item.id
224
303
  runtime_context: C = self.factory.new_context(
225
- execution_id=eval_item.id,
304
+ execution_id=eval_item_id,
226
305
  input_json=eval_item.inputs,
227
306
  is_eval_run=True,
307
+ log_handler=self._setup_execution_logging(eval_item_id),
228
308
  )
309
+ if runtime_context.execution_id is None:
310
+ raise ValueError("execution_id must be set for eval runs")
311
+
229
312
  attributes = {
230
313
  "evalId": eval_item.id,
231
314
  "span_type": "eval",
@@ -234,18 +317,22 @@ class UiPathEvalRuntime(UiPathBaseRuntime, Generic[T, C]):
234
317
  attributes["execution.id"] = runtime_context.execution_id
235
318
 
236
319
  start_time = time()
237
-
238
- result = await self.factory.execute_in_root_span(
239
- runtime_context, root_span=eval_item.name, attributes=attributes
240
- )
320
+ try:
321
+ result = await self.factory.execute_in_root_span(
322
+ runtime_context, root_span=eval_item.name, attributes=attributes
323
+ )
324
+ except Exception as e:
325
+ spans, logs = self._get_and_clear_execution_data(
326
+ runtime_context.execution_id
327
+ )
328
+ raise EvaluationRuntimeException(
329
+ spans=spans,
330
+ logs=logs,
331
+ root_exception=e,
332
+ ) from e
241
333
 
242
334
  end_time = time()
243
-
244
- if runtime_context.execution_id is None:
245
- raise ValueError("execution_id must be set for eval runs")
246
-
247
- spans = self.span_exporter.get_spans(runtime_context.execution_id)
248
- self.span_exporter.clear(runtime_context.execution_id)
335
+ spans, logs = self._get_and_clear_execution_data(runtime_context.execution_id)
249
336
 
250
337
  if result is None:
251
338
  raise ValueError("Execution result cannot be None for eval runs")
@@ -253,9 +340,15 @@ class UiPathEvalRuntime(UiPathBaseRuntime, Generic[T, C]):
253
340
  return UiPathEvalRunExecutionOutput(
254
341
  execution_time=end_time - start_time,
255
342
  spans=spans,
343
+ logs=logs,
256
344
  result=result,
257
345
  )
258
346
 
347
+ def _setup_execution_logging(self, eval_item_id: str) -> ExecutionLogHandler:
348
+ execution_log_handler = ExecutionLogHandler(eval_item_id)
349
+ self.logs_exporter.register(eval_item_id, execution_log_handler)
350
+ return execution_log_handler
351
+
259
352
  async def run_evaluator(
260
353
  self,
261
354
  evaluator: BaseEvaluator[Any],
@@ -519,7 +519,8 @@ class UiPathBaseRuntime(ABC):
519
519
 
520
520
  await self.validate()
521
521
 
522
- # Intercept all stdout/stderr/logs and write them to a file (runtime/evals), stdout (debug)
522
+ # Intercept all stdout/stderr/logs
523
+ # write to file (runtime) or stdout (debug)
523
524
  self.logs_interceptor = LogsInterceptor(
524
525
  min_level=self.context.logs_min_level,
525
526
  dir=self.context.runtime_dir,
@@ -649,7 +650,7 @@ class UiPathBaseRuntime(ABC):
649
650
  raise
650
651
  finally:
651
652
  # Restore original logging
652
- if self.logs_interceptor:
653
+ if hasattr(self, "logs_interceptor"):
653
654
  self.logs_interceptor.teardown()
654
655
 
655
656
  await self.cleanup()
@@ -698,6 +699,7 @@ class UiPathRuntimeFactory(Generic[T, C]):
698
699
  self.context_generator = context_generator
699
700
  self.tracer_provider: TracerProvider = TracerProvider()
700
701
  self.tracer_span_processors: List[SpanProcessor] = []
702
+ self.logs_exporter: Optional[Any] = None
701
703
  trace.set_tracer_provider(self.tracer_provider)
702
704
 
703
705
  def add_span_exporter(
@@ -10,6 +10,35 @@ current_execution_id: ContextVar[Optional[str]] = ContextVar(
10
10
  )
11
11
 
12
12
 
13
+ class ExecutionLogHandler(logging.Handler):
14
+ """Handler for an execution unit."""
15
+
16
+ def __init__(self, execution_id: str):
17
+ """Initialize the buffered handler."""
18
+ super().__init__()
19
+ self.execution_id: str = execution_id
20
+ self.buffer: list[logging.LogRecord] = []
21
+ self.setFormatter(logging.Formatter("[%(asctime)s][%(levelname)s] %(message)s"))
22
+
23
+ def emit(self, record: logging.LogRecord):
24
+ """Store log record in buffer grouped by execution_id."""
25
+ self.buffer.append(record)
26
+
27
+ def flush_execution_logs(self, target_handler: logging.Handler) -> None:
28
+ """Flush buffered logs to a target handler.
29
+
30
+ Args:
31
+ target_handler: The handler to write the logs to
32
+ """
33
+ for record in self.buffer:
34
+ target_handler.handle(record)
35
+ target_handler.flush()
36
+
37
+ def clear_execution(self) -> None:
38
+ """Clear buffered logs without writing them."""
39
+ self.buffer.clear()
40
+
41
+
13
42
  class PersistentLogsHandler(logging.FileHandler):
14
43
  """A simple log handler that always writes to a single file without rotation."""
15
44
 
uipath/_cli/cli_eval.py CHANGED
@@ -2,11 +2,11 @@
2
2
  import ast
3
3
  import asyncio
4
4
  import os
5
- import uuid
6
5
  from typing import List, Optional
7
6
 
8
7
  import click
9
8
 
9
+ from uipath._cli._evals._console_progress_reporter import ConsoleProgressReporter
10
10
  from uipath._cli._evals._progress_reporter import StudioWebProgressReporter
11
11
  from uipath._cli._evals._runtime import (
12
12
  UiPathEvalContext,
@@ -17,6 +17,7 @@ from uipath._cli._runtime._contracts import (
17
17
  UiPathRuntimeFactory,
18
18
  )
19
19
  from uipath._cli._runtime._runtime import UiPathScriptRuntime
20
+ from uipath._cli._utils._constants import UIPATH_PROJECT_ID
20
21
  from uipath._cli._utils._folders import get_personal_workspace_key_async
21
22
  from uipath._cli.middlewares import Middlewares
22
23
  from uipath._events._event_bus import EventBus
@@ -39,6 +40,22 @@ class LiteralOption(click.Option):
39
40
  raise click.BadParameter(value) from e
40
41
 
41
42
 
43
+ def setup_reporting_prereq(no_report: bool) -> bool:
44
+ if no_report:
45
+ return False
46
+
47
+ if not os.getenv(UIPATH_PROJECT_ID, False):
48
+ console.warning(
49
+ "UIPATH_PROJECT_ID environment variable not set. Results will no be reported to Studio Web."
50
+ )
51
+ return False
52
+ if not os.getenv("UIPATH_FOLDER_KEY"):
53
+ os.environ["UIPATH_FOLDER_KEY"] = asyncio.run(
54
+ get_personal_workspace_key_async()
55
+ )
56
+ return True
57
+
58
+
42
59
  @click.command()
43
60
  @click.argument("entrypoint", required=False)
44
61
  @click.argument("eval_set", required=False)
@@ -79,10 +96,7 @@ def eval(
79
96
  workers: Number of parallel workers for running evaluations
80
97
  no_report: Do not report the evaluation results
81
98
  """
82
- if not no_report and not os.getenv("UIPATH_FOLDER_KEY"):
83
- os.environ["UIPATH_FOLDER_KEY"] = asyncio.run(
84
- get_personal_workspace_key_async()
85
- )
99
+ should_register_progress_reporter = setup_reporting_prereq(no_report)
86
100
 
87
101
  result = Middlewares.next(
88
102
  "eval",
@@ -92,6 +106,7 @@ def eval(
92
106
  no_report=no_report,
93
107
  workers=workers,
94
108
  execution_output_file=output_file,
109
+ register_progress_reporter=should_register_progress_reporter,
95
110
  )
96
111
 
97
112
  if result.error_message:
@@ -100,7 +115,7 @@ def eval(
100
115
  if result.should_continue:
101
116
  event_bus = EventBus()
102
117
 
103
- if not no_report:
118
+ if should_register_progress_reporter:
104
119
  progress_reporter = StudioWebProgressReporter(LlmOpsHttpExporter())
105
120
  asyncio.run(progress_reporter.subscribe_to_eval_runtime_events(event_bus))
106
121
 
@@ -114,7 +129,6 @@ def eval(
114
129
  eval_context = UiPathEvalContext.with_defaults(
115
130
  execution_output_file=output_file,
116
131
  entrypoint=runtime_entrypoint,
117
- execution_id=str(uuid.uuid4()),
118
132
  )
119
133
 
120
134
  eval_context.no_report = no_report
@@ -122,6 +136,9 @@ def eval(
122
136
  eval_context.eval_set = eval_set or EvalHelpers.auto_discover_eval_set()
123
137
  eval_context.eval_ids = eval_ids
124
138
 
139
+ console_reporter = ConsoleProgressReporter()
140
+ asyncio.run(console_reporter.subscribe_to_eval_runtime_events(event_bus))
141
+
125
142
  try:
126
143
  runtime_factory = UiPathRuntimeFactory(
127
144
  UiPathScriptRuntime,
@@ -143,11 +160,9 @@ def eval(
143
160
  asyncio.run(execute())
144
161
  except Exception as e:
145
162
  console.error(
146
- f"Error: Unexpected error occurred - {str(e)}", include_traceback=True
163
+ f"Error occurred: {e or 'Execution failed'}", include_traceback=True
147
164
  )
148
165
 
149
- console.success("Evaluation completed successfully")
150
-
151
166
 
152
167
  if __name__ == "__main__":
153
168
  eval()
@@ -121,20 +121,20 @@ class Middlewares:
121
121
  ]
122
122
 
123
123
  if middlewares:
124
- logger.info(f"Found {len(middlewares)} middleware plugins")
124
+ logger.debug(f"Found {len(middlewares)} middleware plugins")
125
125
 
126
126
  for entry_point in middlewares:
127
127
  try:
128
128
  register_func = entry_point.load()
129
129
  register_func()
130
- logger.info(f"Loaded middleware plugin: {entry_point.name}")
130
+ logger.debug(f"Loaded middleware plugin: {entry_point.name}")
131
131
  except Exception as e:
132
132
  console.error(
133
133
  f"Failed to load middleware plugin {entry_point.name}: {str(e)}",
134
134
  include_traceback=True,
135
135
  )
136
136
  else:
137
- logger.info("No middleware plugins found")
137
+ logger.debug("No middleware plugins found")
138
138
 
139
139
  except Exception as e:
140
140
  logger.error(f"No middleware plugins loaded: {str(e)}")
uipath/_events/_events.py CHANGED
@@ -1,8 +1,9 @@
1
1
  import enum
2
- from typing import Any, List, Union
2
+ import logging
3
+ from typing import Any, List, Optional, Union
3
4
 
4
5
  from opentelemetry.sdk.trace import ReadableSpan
5
- from pydantic import BaseModel, ConfigDict
6
+ from pydantic import BaseModel, ConfigDict, model_validator
6
7
 
7
8
  from uipath._cli._evals._models._evaluation_set import EvaluationItem
8
9
  from uipath.eval.models import EvalItemResult
@@ -28,6 +29,13 @@ class EvalRunCreatedEvent(BaseModel):
28
29
  eval_item: EvaluationItem
29
30
 
30
31
 
32
+ class EvalItemExceptionDetails(BaseModel):
33
+ model_config = ConfigDict(arbitrary_types_allowed=True)
34
+
35
+ runtime_exception: bool = False
36
+ exception: Exception
37
+
38
+
31
39
  class EvalRunUpdatedEvent(BaseModel):
32
40
  model_config = ConfigDict(arbitrary_types_allowed=True)
33
41
 
@@ -38,6 +46,14 @@ class EvalRunUpdatedEvent(BaseModel):
38
46
  agent_output: Any
39
47
  agent_execution_time: float
40
48
  spans: List[ReadableSpan]
49
+ logs: List[logging.LogRecord]
50
+ exception_details: Optional[EvalItemExceptionDetails] = None
51
+
52
+ @model_validator(mode="after")
53
+ def validate_exception_details(self):
54
+ if not self.success and self.exception_details is None:
55
+ raise ValueError("exception_details must be provided when success is False")
56
+ return self
41
57
 
42
58
 
43
59
  class EvalSetRunUpdatedEvent(BaseModel):
@@ -1,14 +1,17 @@
1
1
  import json
2
2
  import logging
3
- from typing import Any, Dict
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ from httpx import Response
4
6
 
5
7
  from .._config import Config
6
8
  from .._execution_context import ExecutionContext
7
- from .._utils import Endpoint, RequestSpec, infer_bindings
9
+ from .._utils import Endpoint, RequestSpec, header_folder, infer_bindings
8
10
  from ..models import Connection, ConnectionToken, EventArguments
9
11
  from ..models.connections import ConnectionTokenType
10
12
  from ..tracing._traced import traced
11
13
  from ._base_service import BaseService
14
+ from .folder_service import FolderService
12
15
 
13
16
  logger: logging.Logger = logging.getLogger("uipath")
14
17
 
@@ -20,9 +23,16 @@ class ConnectionsService(BaseService):
20
23
  and secure token management.
21
24
  """
22
25
 
23
- def __init__(self, config: Config, execution_context: ExecutionContext) -> None:
26
+ def __init__(
27
+ self,
28
+ config: Config,
29
+ execution_context: ExecutionContext,
30
+ folders_service: FolderService,
31
+ ) -> None:
24
32
  super().__init__(config=config, execution_context=execution_context)
33
+ self._folders_service = folders_service
25
34
 
35
+ @infer_bindings(resource_type="connection", name="key")
26
36
  @traced(
27
37
  name="connections_retrieve",
28
38
  run_type="uipath",
@@ -45,6 +55,117 @@ class ConnectionsService(BaseService):
45
55
  response = self.request(spec.method, url=spec.endpoint)
46
56
  return Connection.model_validate(response.json())
47
57
 
58
+ @traced(name="connections_list", run_type="uipath")
59
+ def list(
60
+ self,
61
+ *,
62
+ name: Optional[str] = None,
63
+ folder_path: Optional[str] = None,
64
+ folder_key: Optional[str] = None,
65
+ connector_key: Optional[str] = None,
66
+ skip: Optional[int] = None,
67
+ top: Optional[int] = None,
68
+ ) -> List[Connection]:
69
+ """Lists all connections with optional filtering.
70
+
71
+ Args:
72
+ name: Optional connection name to filter (supports partial matching)
73
+ folder_path: Optional folder path for filtering connections
74
+ folder_key: Optional folder key (mutually exclusive with folder_path)
75
+ connector_key: Optional connector key to filter by specific connector type
76
+ skip: Number of records to skip (for pagination)
77
+ top: Maximum number of records to return
78
+
79
+ Returns:
80
+ List[Connection]: List of connection instances
81
+
82
+ Raises:
83
+ ValueError: If both folder_path and folder_key are provided together, or if
84
+ folder_path is provided but cannot be resolved to a folder_key
85
+
86
+ Examples:
87
+ >>> # List all connections
88
+ >>> connections = sdk.connections.list()
89
+
90
+ >>> # Find connections by name
91
+ >>> salesforce_conns = sdk.connections.list(name="Salesforce")
92
+
93
+ >>> # List all Slack connections in Finance folder
94
+ >>> connections = sdk.connections.list(
95
+ ... folder_path="Finance",
96
+ ... connector_key="uipath-slack"
97
+ ... )
98
+ """
99
+ spec = self._list_spec(
100
+ name=name,
101
+ folder_path=folder_path,
102
+ folder_key=folder_key,
103
+ connector_key=connector_key,
104
+ skip=skip,
105
+ top=top,
106
+ )
107
+ response = self.request(
108
+ spec.method, url=spec.endpoint, params=spec.params, headers=spec.headers
109
+ )
110
+
111
+ return self._parse_and_validate_list_response(response)
112
+
113
+ @traced(name="connections_list", run_type="uipath")
114
+ async def list_async(
115
+ self,
116
+ *,
117
+ name: Optional[str] = None,
118
+ folder_path: Optional[str] = None,
119
+ folder_key: Optional[str] = None,
120
+ connector_key: Optional[str] = None,
121
+ skip: Optional[int] = None,
122
+ top: Optional[int] = None,
123
+ ) -> List[Connection]:
124
+ """Asynchronously lists all connections with optional filtering.
125
+
126
+ Args:
127
+ name: Optional connection name to filter (supports partial matching)
128
+ folder_path: Optional folder path for filtering connections
129
+ folder_key: Optional folder key (mutually exclusive with folder_path)
130
+ connector_key: Optional connector key to filter by specific connector type
131
+ skip: Number of records to skip (for pagination)
132
+ top: Maximum number of records to return
133
+
134
+ Returns:
135
+ List[Connection]: List of connection instances
136
+
137
+ Raises:
138
+ ValueError: If both folder_path and folder_key are provided together, or if
139
+ folder_path is provided but cannot be resolved to a folder_key
140
+
141
+ Examples:
142
+ >>> # List all connections
143
+ >>> connections = await sdk.connections.list_async()
144
+
145
+ >>> # Find connections by name
146
+ >>> salesforce_conns = await sdk.connections.list_async(name="Salesforce")
147
+
148
+ >>> # List all Slack connections in Finance folder
149
+ >>> connections = await sdk.connections.list_async(
150
+ ... folder_path="Finance",
151
+ ... connector_key="uipath-slack"
152
+ ... )
153
+ """
154
+ spec = self._list_spec(
155
+ name=name,
156
+ folder_path=folder_path,
157
+ folder_key=folder_key,
158
+ connector_key=connector_key,
159
+ skip=skip,
160
+ top=top,
161
+ )
162
+ response = await self.request_async(
163
+ spec.method, url=spec.endpoint, params=spec.params, headers=spec.headers
164
+ )
165
+
166
+ return self._parse_and_validate_list_response(response)
167
+
168
+ @infer_bindings(resource_type="connection", name="key")
48
169
  @traced(
49
170
  name="connections_retrieve",
50
171
  run_type="uipath",
@@ -213,3 +334,97 @@ class ConnectionsService(BaseService):
213
334
  endpoint=Endpoint(f"/connections_/api/v1/Connections/{key}/token"),
214
335
  params={"tokenType": token_type.value},
215
336
  )
337
+
338
+ def _parse_and_validate_list_response(self, response: Response) -> List[Connection]:
339
+ """Parse and validate the list response from the API.
340
+
341
+ Handles both OData response format (with 'value' field) and raw list responses.
342
+
343
+ Args:
344
+ response: The HTTP response from the API
345
+
346
+ Returns:
347
+ List of validated Connection instances
348
+ """
349
+ data = response.json()
350
+
351
+ # Handle both OData responses (dict with 'value') and raw list responses
352
+ if isinstance(data, dict):
353
+ connections_data = data.get("value", [])
354
+ elif isinstance(data, list):
355
+ connections_data = data
356
+ else:
357
+ connections_data = []
358
+
359
+ return [Connection.model_validate(conn) for conn in connections_data]
360
+
361
+ def _list_spec(
362
+ self,
363
+ name: Optional[str] = None,
364
+ folder_path: Optional[str] = None,
365
+ folder_key: Optional[str] = None,
366
+ connector_key: Optional[str] = None,
367
+ skip: Optional[int] = None,
368
+ top: Optional[int] = None,
369
+ ) -> RequestSpec:
370
+ """Build the request specification for listing connections.
371
+
372
+ Args:
373
+ name: Optional connection name to filter (supports partial matching)
374
+ folder_path: Optional folder path for filtering connections
375
+ folder_key: Optional folder key (mutually exclusive with folder_path)
376
+ connector_key: Optional connector key to filter by specific connector type
377
+ skip: Number of records to skip (for pagination)
378
+ top: Maximum number of records to return
379
+
380
+ Returns:
381
+ RequestSpec with endpoint, params, and headers configured
382
+
383
+ Raises:
384
+ ValueError: If both folder_path and folder_key are provided together, or if
385
+ folder_path is provided but cannot be resolved to a folder_key
386
+ """
387
+ # Validate mutual exclusivity of folder_path and folder_key
388
+ if folder_path is not None and folder_key is not None:
389
+ raise ValueError(
390
+ "folder_path and folder_key are mutually exclusive and cannot be provided together"
391
+ )
392
+
393
+ # Resolve folder_path to folder_key if needed
394
+ resolved_folder_key = folder_key
395
+ if not resolved_folder_key and folder_path:
396
+ resolved_folder_key = self._folders_service.retrieve_key(
397
+ folder_path=folder_path
398
+ )
399
+ if not resolved_folder_key:
400
+ raise ValueError(f"Folder with path '{folder_path}' not found")
401
+
402
+ # Build OData filters
403
+ filters = []
404
+ if name:
405
+ # Escape single quotes in name for OData
406
+ escaped_name = name.replace("'", "''")
407
+ filters.append(f"contains(Name, '{escaped_name}')")
408
+ if connector_key:
409
+ filters.append(f"connector/key eq '{connector_key}'")
410
+
411
+ params = {}
412
+ if filters:
413
+ params["$filter"] = " and ".join(filters)
414
+ if skip is not None:
415
+ params["$skip"] = str(skip)
416
+ if top is not None:
417
+ params["$top"] = str(top)
418
+
419
+ # Always expand connector and folder for complete information
420
+ params["$expand"] = "connector,folder"
421
+
422
+ # Use header_folder which handles validation
423
+ headers = header_folder(resolved_folder_key, None)
424
+
425
+ return RequestSpec(
426
+ method="GET",
427
+ endpoint=Endpoint("/connections_/api/v1/Connections"),
428
+ params=params,
429
+ headers=headers,
430
+ )
uipath/_uipath.py CHANGED
@@ -60,6 +60,7 @@ class UiPath:
60
60
  self._folders_service: Optional[FolderService] = None
61
61
  self._buckets_service: Optional[BucketsService] = None
62
62
  self._attachments_service: Optional[AttachmentsService] = None
63
+ self._connections_service: Optional[ConnectionsService] = None
63
64
 
64
65
  setup_logging(debug)
65
66
  self._execution_context = ExecutionContext()
@@ -98,7 +99,15 @@ class UiPath:
98
99
 
99
100
  @property
100
101
  def connections(self) -> ConnectionsService:
101
- return ConnectionsService(self._config, self._execution_context)
102
+ if not self._connections_service:
103
+ if not self._folders_service:
104
+ self._folders_service = FolderService(
105
+ self._config, self._execution_context
106
+ )
107
+ self._connections_service = ConnectionsService(
108
+ self._config, self._execution_context, self._folders_service
109
+ )
110
+ return self._connections_service
102
111
 
103
112
  @property
104
113
  def context_grounding(self) -> ContextGroundingService:
@@ -28,8 +28,10 @@ def infer_bindings(
28
28
  all_args.get(name), # type: ignore
29
29
  all_args.get(folder_path, None),
30
30
  ) as (name_overwrite_or_default, folder_path_overwrite_or_default):
31
- all_args[name] = name_overwrite_or_default
32
- all_args[folder_path] = folder_path_overwrite_or_default
31
+ if name in sig.parameters:
32
+ all_args[name] = name_overwrite_or_default
33
+ if folder_path in sig.parameters:
34
+ all_args[folder_path] = folder_path_overwrite_or_default
33
35
 
34
36
  return func(**all_args)
35
37
 
@@ -2,7 +2,7 @@ import json
2
2
  import os
3
3
  from functools import wraps
4
4
  from importlib.metadata import version
5
- from logging import INFO, LogRecord, getLogger
5
+ from logging import WARNING, LogRecord, getLogger
6
6
  from typing import Any, Callable, Dict, Optional, Union
7
7
 
8
8
  from azure.monitor.opentelemetry import configure_azure_monitor
@@ -102,7 +102,7 @@ class _TelemetryClient:
102
102
  )
103
103
 
104
104
  _logger.addHandler(_AzureMonitorOpenTelemetryEventHandler())
105
- _logger.setLevel(INFO)
105
+ _logger.setLevel(WARNING)
106
106
 
107
107
  _TelemetryClient._initialized = True
108
108
  except Exception:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath
3
- Version: 2.1.74
3
+ Version: 2.1.76
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
@@ -2,14 +2,14 @@ uipath/__init__.py,sha256=IaeKItOOQXMa95avueJ3dAq-XcRHyZVNjcCGwlSB000,634
2
2
  uipath/_config.py,sha256=pi3qxPzDTxMEstj_XkGOgKJqD6RTHHv7vYv8sS_-d5Q,92
3
3
  uipath/_execution_context.py,sha256=Qo8VMUFgtiL-40KsZrvul5bGv1CRERle_fCw1ORCggY,2374
4
4
  uipath/_folder_context.py,sha256=D-bgxdwpwJP4b_QdVKcPODYh15kMDrOar2xNonmMSm4,1861
5
- uipath/_uipath.py,sha256=u01pAYrVYfwgAWGeSyD-3Eg33uNHFxhQaH1KAg9NSTA,4616
5
+ uipath/_uipath.py,sha256=xvBwczfjrvrB96ZK7wo0IgDcPDQWuEqu4dioH9CKd4k,5021
6
6
  uipath/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  uipath/_cli/README.md,sha256=GLtCfbeIKZKNnGTCsfSVqRQ27V1btT1i2bSAyW_xZl4,474
8
8
  uipath/_cli/__init__.py,sha256=2RUgXYd8uJaYjA67xWb0w4IZuBmZoY8G1ccNmEQk9oM,2343
9
9
  uipath/_cli/cli_auth.py,sha256=ZEA0Fwoo77Ez9ctpRAIq7sbAwj8F4OouAbMp1g1OvjM,2601
10
10
  uipath/_cli/cli_deploy.py,sha256=KPCmQ0c_NYD5JofSDao5r6QYxHshVCRxlWDVnQvlp5w,645
11
11
  uipath/_cli/cli_dev.py,sha256=nEfpjw1PZ72O6jmufYWVrueVwihFxDPOeJakdvNHdOA,2146
12
- uipath/_cli/cli_eval.py,sha256=DK7FBLrNiBVEZgNBarlgjO065hsmQYLas-1e7nfIjLk,4816
12
+ uipath/_cli/cli_eval.py,sha256=Tg9mEf0T4qkp_YpcqJVxmPp69JkjHF68teX4CM061F0,5444
13
13
  uipath/_cli/cli_init.py,sha256=Ac3-9tIH3rpikIX1ehWTo7InW5tjVNoz_w6fjvgLK4w,7052
14
14
  uipath/_cli/cli_invoke.py,sha256=m-te-EjhDpk_fhFDkt-yQFzmjEHGo5lQDGEQWxSXisQ,4395
15
15
  uipath/_cli/cli_new.py,sha256=9378NYUBc9j-qKVXV7oja-jahfJhXBg8zKVyaon7ctY,2102
@@ -18,7 +18,7 @@ uipath/_cli/cli_publish.py,sha256=DgyfcZjvfV05Ldy0Pk5y_Le_nT9JduEE_x-VpIc_Kq0,64
18
18
  uipath/_cli/cli_pull.py,sha256=PZ2hkfsfN-ElNa3FHjNetTux8XH03tDY5kWWqydQ2OY,6832
19
19
  uipath/_cli/cli_push.py,sha256=-j-gDIbT8GyU2SybLQqFl5L8KI9nu3CDijVtltDgX20,3132
20
20
  uipath/_cli/cli_run.py,sha256=1FKv20EjxrrP1I5rNSnL_HzbWtOAIMjB3M--4RPA_Yo,3709
21
- uipath/_cli/middlewares.py,sha256=GvMhDnx1BmA7rIe12s6Uqv1JdqNZhvraU0a91oqGag4,4976
21
+ uipath/_cli/middlewares.py,sha256=0D9a-wphyetnH9T97F08o7-1OKWF1lMweFHHAR0xiOw,4979
22
22
  uipath/_cli/spinner.py,sha256=bS-U_HA5yne11ejUERu7CQoXmWdabUD2bm62EfEdV8M,1107
23
23
  uipath/_cli/_auth/_auth_server.py,sha256=22km0F1NFNXgyLbvtAx3ssiQlVGHroLdtDCWEqiCiMg,7106
24
24
  uipath/_cli/_auth/_auth_service.py,sha256=Thtp2wXZQAHqossPPXuP6sAEe4Px9xThhZutMECRrdU,6386
@@ -44,13 +44,15 @@ uipath/_cli/_dev/_terminal/_styles/terminal.tcss,sha256=ktVpKwXIXw2VZp8KIZD6fO9i
44
44
  uipath/_cli/_dev/_terminal/_utils/_chat.py,sha256=YUZxYVdmEManwHDuZsczJT1dWIYE1dVBgABlurwMFcE,8493
45
45
  uipath/_cli/_dev/_terminal/_utils/_exporter.py,sha256=oI6D_eMwrh_2aqDYUh4GrJg8VLGrLYhDahR-_o0uJns,4144
46
46
  uipath/_cli/_dev/_terminal/_utils/_logger.py,sha256=_ipTl_oAiMF9I7keGt2AAFAMz40DNLVMVkoiq-07UAU,2943
47
+ uipath/_cli/_evals/_console_progress_reporter.py,sha256=HgB6pdMyoS6YVwuI3EpM2LBcH3U69nrdaTyNgPG8ssg,9304
47
48
  uipath/_cli/_evals/_evaluator_factory.py,sha256=Gycv94VtGOpMir_Gba-UoiAyrSRfbSfe8_pTfjzcA9Q,3875
48
- uipath/_cli/_evals/_progress_reporter.py,sha256=hpSt0CXpIoFJGsbqZkqmwyGO_TBNesbWKlvDJUEDxd8,16455
49
- uipath/_cli/_evals/_runtime.py,sha256=fo-wtUxZ9bvo1ftt_yu7o2oUBvrMqVNKCjaGH556-Vo,11506
49
+ uipath/_cli/_evals/_progress_reporter.py,sha256=kX7rNSa-QCLXIzK-vb9Jjf-XLEtucdeiQPgPlSkpp2U,16778
50
+ uipath/_cli/_evals/_runtime.py,sha256=dz3mpZCLxwnLEdkwLo6W7qzBuVAklx6LMWtd4OMRk9w,15489
50
51
  uipath/_cli/_evals/_models/_evaluation_set.py,sha256=XgPNLWciE4FgCYzZXV2kRYHzdtbc33FWSQmZQqVSdMk,4747
51
52
  uipath/_cli/_evals/_models/_evaluator.py,sha256=fuC3UOYwPD4d_wdynHeLSCzbu82golNAnnPnxC8Y4rk,3315
52
53
  uipath/_cli/_evals/_models/_evaluator_base_params.py,sha256=lTYKOV66tcjW85KHTyOdtF1p1VDaBNemrMAvH8bFIFc,382
53
- uipath/_cli/_evals/_models/_output.py,sha256=LjwMBGI78sDFa2Dl8b9ReXJmjig57pdLWpuiwChrRLo,3096
54
+ uipath/_cli/_evals/_models/_exceptions.py,sha256=-oXLTDa4ab9Boa34ZxuUrCezf8ajIGrIEUVwZnmBASE,195
55
+ uipath/_cli/_evals/_models/_output.py,sha256=DmwFXh1YdLiMXyXmyoZr_4hgrrv3oiHbrrtIWMqGfsg,3145
54
56
  uipath/_cli/_evals/_models/_sw_reporting.py,sha256=tSBLQFAdOIun8eP0vsqt56K6bmCZz_uMaWI3hskg_24,536
55
57
  uipath/_cli/_evals/_models/_trajectory_span.py,sha256=8ukM8sB9rvzBMHfC_gnexAC3xlp4uMDevKZrRzcgrm4,3637
56
58
  uipath/_cli/_evals/mocks/__init__.py,sha256=2WXwAy_oZw5bKp6L0HB13QygCJeftOB_Bget0AI6Gik,32
@@ -60,10 +62,10 @@ uipath/_cli/_evals/mocks/mocker_factory.py,sha256=V5QKSTtQxztTo4-fK1TyAaXw2Z3mHf
60
62
  uipath/_cli/_evals/mocks/mockito_mocker.py,sha256=LtYT6lJM9vc3qtbSZJcUeCzDn4zarkBVj7In_EX7kYY,2087
61
63
  uipath/_cli/_evals/mocks/mocks.py,sha256=WqjWtHqKQXAsO1Wwom3Zcr1T09GQygwBWVp-EsxdW8o,4443
62
64
  uipath/_cli/_push/sw_file_handler.py,sha256=iE8Sk1Z-9hxmLFFj3j-k4kTK6TzNFP6hUCmxTudG6JQ,18251
63
- uipath/_cli/_runtime/_contracts.py,sha256=D57cq5V5CZ9p13n_vRDHRcwyJYQUcJLlAMbAOzIiBNI,28932
65
+ uipath/_cli/_runtime/_contracts.py,sha256=E8Is7EQfAu7_hCbeZI68gmTxSxo4X7_U4vcSl7D3Syg,28988
64
66
  uipath/_cli/_runtime/_escalation.py,sha256=x3vI98qsfRA-fL_tNkRVTFXioM5Gv2w0GFcXJJ5eQtg,7981
65
67
  uipath/_cli/_runtime/_hitl.py,sha256=VKbM021nVg1HEDnTfucSLJ0LsDn83CKyUtVzofS2qTU,11369
66
- uipath/_cli/_runtime/_logging.py,sha256=CIJNZH4S8SL980VI3XV9NIFdc97T-KJRT8QE7X1uWYw,12865
68
+ uipath/_cli/_runtime/_logging.py,sha256=jwBfsy0Hi4zkfPH-v9dQ7m5dcJeuE0j_OxdpI-DhHaw,13854
67
69
  uipath/_cli/_runtime/_runtime.py,sha256=gby9-avNNlEATEfSXtY8FfJ8nREsSCGA4wMgDlSXTDE,2297
68
70
  uipath/_cli/_runtime/_script_executor.py,sha256=PjbmEbyCMofGH2F85b8RFsxdV3Tqw0kVqdWOOk2ZLlI,9687
69
71
  uipath/_cli/_templates/.psmdcp.template,sha256=C7pBJPt98ovEljcBvGtEUGoWjjQhu9jls1bpYjeLOKA,611
@@ -86,7 +88,7 @@ uipath/_cli/_utils/_tracing.py,sha256=2igb03j3EHjF_A406UhtCKkPfudVfFPjUq5tXUEG4o
86
88
  uipath/_cli/_utils/_uv_helpers.py,sha256=6SvoLnZPoKIxW0sjMvD1-ENV_HOXDYzH34GjBqwT138,3450
87
89
  uipath/_events/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
90
  uipath/_events/_event_bus.py,sha256=4-VzstyX69cr7wT1EY7ywp-Ndyz2CyemD3Wk_-QmRpo,5496
89
- uipath/_events/_events.py,sha256=FhSqbEW4lcBp_QWcDsMtF5XtUtQ8S3rts6uEtMDmGbA,1252
91
+ uipath/_events/_events.py,sha256=EzDfzpVm-EIH27Onad5mo8Go6-WB3S6_-6zZQ7qV58w,1811
90
92
  uipath/_resources/AGENTS.md,sha256=YWhWuX9XIbyVhVT3PnPc4Of3_q6bsNJcuzYu3N8f_Ug,25850
91
93
  uipath/_services/__init__.py,sha256=eYZElMfYDQTQU6MMjIke5J-GGT9pzLD5QfbwLiTQkEE,1037
92
94
  uipath/_services/_base_service.py,sha256=x9-9jhPzn9Z16KRdFHhJNvV-FZHvTniMsDfxlS4Cutk,5782
@@ -95,7 +97,7 @@ uipath/_services/api_client.py,sha256=kGm04ijk9AOEQd2BMxvQg-2QoB8dmyoDwFFDPyutAG
95
97
  uipath/_services/assets_service.py,sha256=pG0Io--SeiRRQmfUWPQPl1vq3csZlQgx30LBNKRmmF8,12145
96
98
  uipath/_services/attachments_service.py,sha256=NPQYK7CGjfBaNT_1S5vEAfODmOChTbQZforllFM2ofU,26678
97
99
  uipath/_services/buckets_service.py,sha256=5s8tuivd7GUZYj774DDUYTa0axxlUuesc4EBY1V5sdk,18496
98
- uipath/_services/connections_service.py,sha256=Gt8zPY4oA7cMYAU2LI3lBieoBpV81BOGelnzDWJl_V4,7931
100
+ uipath/_services/connections_service.py,sha256=EbM4ywaJsvafPEhL5bLZDWLMOUK1UyDVLRc4vZgu3Fo,15905
99
101
  uipath/_services/context_grounding_service.py,sha256=Pjx-QQQEiSKD-hY6ityj3QUSALN3fIcKLLHr_NZ0d_g,37117
100
102
  uipath/_services/documents_service.py,sha256=UnFS8EpOZ_Ng2TZk3OiJJ3iNANvFs7QxuoG_v-lQj6c,24815
101
103
  uipath/_services/entities_service.py,sha256=QKCLE6wRgq3HZraF-M2mljy-8il4vsNHrQhUgkewVVk,14028
@@ -106,7 +108,7 @@ uipath/_services/processes_service.py,sha256=O_uHgQ1rnwiV5quG0OQqabAnE6Rf6cWrMEN
106
108
  uipath/_services/queues_service.py,sha256=VaG3dWL2QK6AJBOLoW2NQTpkPfZjsqsYPl9-kfXPFzA,13534
107
109
  uipath/_utils/__init__.py,sha256=VdcpnENJIa0R6Y26NoxY64-wUVyvb4pKfTh1wXDQeMk,526
108
110
  uipath/_utils/_endpoint.py,sha256=yYHwqbQuJIevpaTkdfYJS9CrtlFeEyfb5JQK5osTCog,2489
109
- uipath/_utils/_infer_bindings.py,sha256=yTl3JBgoLwtQV-RCUkDY4qZne9FtE3HHIiYgCAKAOKw,1727
111
+ uipath/_utils/_infer_bindings.py,sha256=eCxfUjd37fOFZ6vOfKl2BhWVUt7iSSJ-VQ0-nDTBcaA,1836
110
112
  uipath/_utils/_logs.py,sha256=adfX_0UAn3YBeKJ8DQDeZs94rJyHGQO00uDfkaTpNWQ,510
111
113
  uipath/_utils/_read_overwrites.py,sha256=OQgG9ycPpFnLub5ELQdX9V2Fyh6F9_zDR3xoYagJaMI,5287
112
114
  uipath/_utils/_request_override.py,sha256=fIVHzgHVXITUlWcp8osNBwIafM1qm4_ejx0ng5UzfJ4,573
@@ -158,15 +160,15 @@ uipath/models/processes.py,sha256=bV31xTyF0hRWZmwy3bWj5L8dBD9wttWxfJjwzhjETmk,19
158
160
  uipath/models/queues.py,sha256=gnbeEyYlHtdqdxBalio0lw8mq-78YBG9MPMSkv1BWOg,6934
159
161
  uipath/telemetry/__init__.py,sha256=Wna32UFzZR66D-RzTKlPWlvji9i2HJb82NhHjCCXRjY,61
160
162
  uipath/telemetry/_constants.py,sha256=uRDuEZayBYtBA0tMx-2AS_D-oiVA7oKgp9zid9jNats,763
161
- uipath/telemetry/_track.py,sha256=G_Pyq8n8iMvoCWhUpWedlptXUSuUSbQBBzGxsh4DW9c,4654
163
+ uipath/telemetry/_track.py,sha256=N7EMUJi8cnpZqlVL-B746MGvH1hk3urqWbV0T67UDB8,4660
162
164
  uipath/tracing/__init__.py,sha256=GKRINyWdHVrDsI-8mrZDLdf0oey6GHGlNZTOADK-kgc,224
163
165
  uipath/tracing/_otel_exporters.py,sha256=c0GKU_oUrAwrOrqbyu64c55z1TR6xk01d3y5fLUN1lU,3215
164
166
  uipath/tracing/_traced.py,sha256=yBIY05PCCrYyx50EIHZnwJaKNdHPNx-YTR1sHQl0a98,19901
165
167
  uipath/tracing/_utils.py,sha256=qd7N56tg6VXQ9pREh61esBgUWLNA0ssKsE0QlwrRWFM,11974
166
168
  uipath/utils/__init__.py,sha256=VD-KXFpF_oWexFg6zyiWMkxl2HM4hYJMIUDZ1UEtGx0,105
167
169
  uipath/utils/_endpoints_manager.py,sha256=iRTl5Q0XAm_YgcnMcJOXtj-8052sr6jpWuPNz6CgT0Q,8408
168
- uipath-2.1.74.dist-info/METADATA,sha256=YI-VaC3UPoQGy3mDnKYd4in3JnlExWwD9QzMJdO-H6s,6593
169
- uipath-2.1.74.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
170
- uipath-2.1.74.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
171
- uipath-2.1.74.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
172
- uipath-2.1.74.dist-info/RECORD,,
170
+ uipath-2.1.76.dist-info/METADATA,sha256=zT48rJq2Cbsdva0uAbrjk7eNsLGxBiq2YTjp327fJg8,6593
171
+ uipath-2.1.76.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
172
+ uipath-2.1.76.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
173
+ uipath-2.1.76.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
174
+ uipath-2.1.76.dist-info/RECORD,,