uipath 2.1.71__py3-none-any.whl → 2.1.72__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.
@@ -73,13 +73,33 @@ class RunHistoryPanel(Container):
73
73
  self.refresh_list()
74
74
 
75
75
  def _refresh_running_items(self) -> None:
76
+ """Refresh display names for running items only."""
76
77
  if not any(run.status == "running" for run in self.runs):
77
- return None # No running items, skip update
78
+ return None
78
79
 
79
- run_list = self.query_one("#run-list", ListView)
80
+ try:
81
+ run_list = self.query_one("#run-list", ListView)
82
+ except Exception:
83
+ return None
84
+
85
+ # Take a snapshot of items to avoid mid-iteration changes
86
+ items_snapshot = list(run_list.children)
87
+
88
+ for item in items_snapshot:
89
+ if not hasattr(item, "run_id"):
90
+ continue
91
+
92
+ run = self.get_run_by_id(item.run_id)
93
+ if not run or run.status != "running":
94
+ continue
95
+
96
+ # Check if item still exists in the list (wasn't removed)
97
+ if item not in run_list.children:
98
+ continue
80
99
 
81
- for item in run_list.children:
82
- run = self.get_run_by_id(item.run_id) # type: ignore[attr-defined]
83
- if run and run.status == "running":
100
+ try:
84
101
  static = item.query_one(Static)
85
102
  static.update(run.display_name)
103
+ except Exception:
104
+ # Item structure changed or was removed
105
+ continue
@@ -1,5 +1,14 @@
1
1
  from typing import Any, Dict
2
2
 
3
+ from pydantic import TypeAdapter
4
+
5
+ from uipath._cli._evals._models._evaluator import (
6
+ EqualsEvaluatorParams,
7
+ Evaluator,
8
+ JsonSimilarityEvaluatorParams,
9
+ LLMEvaluatorParams,
10
+ TrajectoryEvaluatorParams,
11
+ )
3
12
  from uipath._cli._evals._models._evaluator_base_params import EvaluatorBaseParams
4
13
  from uipath.eval.evaluators import (
5
14
  BaseEvaluator,
@@ -8,7 +17,6 @@ from uipath.eval.evaluators import (
8
17
  LlmAsAJudgeEvaluator,
9
18
  TrajectoryEvaluator,
10
19
  )
11
- from uipath.eval.models.models import EvaluatorCategory, EvaluatorType
12
20
 
13
21
 
14
22
  class EvaluatorFactory:
@@ -35,110 +43,64 @@ class EvaluatorFactory:
35
43
  if not id:
36
44
  raise ValueError("Evaluator configuration must include 'id' field")
37
45
 
38
- category = EvaluatorCategory.from_int(data.get("category"))
39
- evaluator_type = EvaluatorType.from_int(data.get("type", EvaluatorType.Unknown))
40
- description = data.get("description", "")
41
- created_at = data.get("createdAt", "")
42
- updated_at = data.get("updatedAt", "")
43
- target_output_key = data.get("targetOutputKey", "")
44
-
45
- # Create base parameters
46
- base_params = EvaluatorBaseParams(
47
- id=id,
48
- category=category,
49
- evaluator_type=evaluator_type,
50
- name=name,
51
- description=description,
52
- created_at=created_at,
53
- updated_at=updated_at,
54
- target_output_key=target_output_key,
55
- )
56
-
57
- match category:
58
- case EvaluatorCategory.Deterministic:
59
- if evaluator_type == evaluator_type.Equals:
60
- return EvaluatorFactory._create_exact_match_evaluator(
61
- base_params, data
62
- )
63
- elif evaluator_type == evaluator_type.JsonSimilarity:
64
- return EvaluatorFactory._create_json_similarity_evaluator(
65
- base_params, data
66
- )
67
- else:
68
- raise ValueError(
69
- f"Unknown evaluator type {evaluator_type} for category {category}"
70
- )
71
- case EvaluatorCategory.LlmAsAJudge:
72
- return EvaluatorFactory._create_llm_as_judge_evaluator(
73
- base_params, data
74
- )
75
- case EvaluatorCategory.AgentScorer:
76
- raise NotImplementedError()
77
- case EvaluatorCategory.Trajectory:
78
- return EvaluatorFactory._create_trajectory_evaluator(base_params, data)
46
+ params: EvaluatorBaseParams = TypeAdapter(Evaluator).validate_python(data)
47
+
48
+ match params:
49
+ case EqualsEvaluatorParams():
50
+ return EvaluatorFactory._create_exact_match_evaluator(params)
51
+ case JsonSimilarityEvaluatorParams():
52
+ return EvaluatorFactory._create_json_similarity_evaluator(params)
53
+ case LLMEvaluatorParams():
54
+ return EvaluatorFactory._create_llm_as_judge_evaluator(params)
55
+ case TrajectoryEvaluatorParams():
56
+ return EvaluatorFactory._create_trajectory_evaluator(params)
79
57
  case _:
80
- raise ValueError(f"Unknown evaluator category: {category}")
58
+ raise ValueError(f"Unknown evaluator category: {params}")
81
59
 
82
60
  @staticmethod
83
61
  def _create_exact_match_evaluator(
84
- base_params: EvaluatorBaseParams, data: Dict[str, Any]
62
+ params: EqualsEvaluatorParams,
85
63
  ) -> ExactMatchEvaluator:
86
64
  """Create a deterministic evaluator."""
87
- return ExactMatchEvaluator(
88
- **base_params.model_dump(),
89
- )
65
+ return ExactMatchEvaluator(**params.model_dump())
90
66
 
91
67
  @staticmethod
92
68
  def _create_json_similarity_evaluator(
93
- base_params: EvaluatorBaseParams, data: Dict[str, Any]
69
+ params: JsonSimilarityEvaluatorParams,
94
70
  ) -> JsonSimilarityEvaluator:
95
71
  """Create a deterministic evaluator."""
96
- return JsonSimilarityEvaluator(
97
- **base_params.model_dump(),
98
- )
72
+ return JsonSimilarityEvaluator(**params.model_dump())
99
73
 
100
74
  @staticmethod
101
75
  def _create_llm_as_judge_evaluator(
102
- base_params: EvaluatorBaseParams, data: Dict[str, Any]
76
+ params: LLMEvaluatorParams,
103
77
  ) -> LlmAsAJudgeEvaluator:
104
78
  """Create an LLM-as-a-judge evaluator."""
105
- prompt = data.get("prompt", "")
106
- if not prompt:
79
+ if not params.prompt:
107
80
  raise ValueError("LLM evaluator must include 'prompt' field")
108
81
 
109
- model = data.get("model", "")
110
- if not model:
82
+ if not params.model:
111
83
  raise ValueError("LLM evaluator must include 'model' field")
112
- if model == "same-as-agent":
84
+ if params.model == "same-as-agent":
113
85
  raise ValueError(
114
86
  "'same-as-agent' model option is not supported by coded agents evaluations. Please select a specific model for the evaluator."
115
87
  )
116
88
 
117
- return LlmAsAJudgeEvaluator(
118
- **base_params.model_dump(),
119
- prompt=prompt,
120
- model=model,
121
- )
89
+ return LlmAsAJudgeEvaluator(**params.model_dump())
122
90
 
123
91
  @staticmethod
124
92
  def _create_trajectory_evaluator(
125
- base_params: EvaluatorBaseParams, data: Dict[str, Any]
93
+ params: TrajectoryEvaluatorParams,
126
94
  ) -> TrajectoryEvaluator:
127
95
  """Create a trajectory evaluator."""
128
- prompt = data.get("prompt", "")
129
- if not prompt:
96
+ if not params.prompt:
130
97
  raise ValueError("Trajectory evaluator must include 'prompt' field")
131
98
 
132
- model = data.get("model", "")
133
- if not model:
99
+ if not params.model:
134
100
  raise ValueError("LLM evaluator must include 'model' field")
135
- if model == "same-as-agent":
101
+ if params.model == "same-as-agent":
136
102
  raise ValueError(
137
103
  "'same-as-agent' model option is not supported by coded agents evaluations. Please select a specific model for the evaluator."
138
104
  )
139
105
 
140
- return TrajectoryEvaluator(
141
- **base_params.model_dump(),
142
- prompt=prompt,
143
- model=model,
144
- )
106
+ return TrajectoryEvaluator(**params.model_dump())
@@ -5,6 +5,10 @@ from pydantic import BaseModel, ConfigDict, Field
5
5
  from pydantic.alias_generators import to_camel
6
6
 
7
7
 
8
+ class EvaluationSimulationTool(BaseModel):
9
+ name: str = Field(..., alias="name")
10
+
11
+
8
12
  class EvaluationItem(BaseModel):
9
13
  """Individual evaluation item within an evaluation set."""
10
14
 
@@ -14,15 +18,19 @@ class EvaluationItem(BaseModel):
14
18
  name: str
15
19
  inputs: Dict[str, Any]
16
20
  expected_output: Dict[str, Any]
17
- expected_agent_behavior: str = ""
18
- simulation_instructions: str = ""
19
- simulate_input: bool = False
20
- input_generation_instructions: str = ""
21
- simulate_tools: bool = False
22
- tools_to_simulate: List[str] = Field(default_factory=list)
23
- eval_set_id: str
24
- created_at: str
25
- updated_at: str
21
+ expected_agent_behavior: str = Field(default="", alias="expectedAgentBehavior")
22
+ simulation_instructions: str = Field(default="", alias="simulationInstructions")
23
+ simulate_input: bool = Field(default=False, alias="simulateInput")
24
+ input_generation_instructions: str = Field(
25
+ default="", alias="inputGenerationInstructions"
26
+ )
27
+ simulate_tools: bool = Field(default=False, alias="simulateTools")
28
+ tools_to_simulate: List[EvaluationSimulationTool] = Field(
29
+ default_factory=list, alias="toolsToSimulate"
30
+ )
31
+ eval_set_id: str = Field(alias="evalSetId")
32
+ created_at: str = Field(alias="createdAt")
33
+ updated_at: str = Field(alias="updatedAt")
26
34
 
27
35
 
28
36
  class EvaluationSet(BaseModel):
@@ -31,15 +39,17 @@ class EvaluationSet(BaseModel):
31
39
  model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
32
40
 
33
41
  id: str
34
- file_name: str
42
+ file_name: str = Field(..., alias="fileName")
35
43
  evaluator_refs: List[str] = Field(default_factory=list)
36
44
  evaluations: List[EvaluationItem] = Field(default_factory=list)
37
45
  name: str
38
- batch_size: int = 10
39
- timeout_minutes: int = 20
40
- model_settings: List[Dict[str, Any]] = Field(default_factory=list)
41
- created_at: str
42
- updated_at: str
46
+ batch_size: int = Field(10, alias="batchSize")
47
+ timeout_minutes: int = Field(default=20, alias="timeoutMinutes")
48
+ model_settings: List[Dict[str, Any]] = Field(
49
+ default_factory=list, alias="modelSettings"
50
+ )
51
+ created_at: str = Field(alias="createdAt")
52
+ updated_at: str = Field(alias="updatedAt")
43
53
 
44
54
  def extract_selected_evals(self, eval_ids) -> None:
45
55
  selected_evals: list[EvaluationItem] = []
@@ -0,0 +1,106 @@
1
+ from typing import Annotated, Any, Literal, Union
2
+
3
+ from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag
4
+
5
+ from uipath.eval.models.models import EvaluatorCategory, EvaluatorType
6
+
7
+
8
+ class EvaluatorBaseParams(BaseModel):
9
+ """Parameters for initializing the base evaluator."""
10
+
11
+ id: str
12
+ name: str
13
+ description: str
14
+ evaluator_type: EvaluatorType = Field(..., alias="type")
15
+ created_at: str = Field(..., alias="createdAt")
16
+ updated_at: str = Field(..., alias="updatedAt")
17
+ target_output_key: str = Field(..., alias="targetOutputKey")
18
+ file_name: str = Field(..., alias="fileName")
19
+
20
+
21
+ class LLMEvaluatorParams(EvaluatorBaseParams):
22
+ category: Literal[EvaluatorCategory.LlmAsAJudge] = Field(..., alias="category")
23
+ prompt: str = Field(..., alias="prompt")
24
+ model: str = Field(..., alias="model")
25
+
26
+ model_config = ConfigDict(
27
+ validate_by_name=True, validate_by_alias=True, extra="allow"
28
+ )
29
+
30
+
31
+ class TrajectoryEvaluatorParams(EvaluatorBaseParams):
32
+ category: Literal[EvaluatorCategory.Trajectory] = Field(..., alias="category")
33
+ prompt: str = Field(..., alias="prompt")
34
+ model: str = Field(..., alias="model")
35
+
36
+ model_config = ConfigDict(
37
+ validate_by_name=True, validate_by_alias=True, extra="allow"
38
+ )
39
+
40
+
41
+ class EqualsEvaluatorParams(EvaluatorBaseParams):
42
+ model_config = ConfigDict(
43
+ validate_by_name=True, validate_by_alias=True, extra="allow"
44
+ )
45
+
46
+
47
+ class JsonSimilarityEvaluatorParams(EvaluatorBaseParams):
48
+ model_config = ConfigDict(
49
+ validate_by_name=True, validate_by_alias=True, extra="allow"
50
+ )
51
+
52
+
53
+ class UnknownEvaluatorParams(EvaluatorBaseParams):
54
+ model_config = ConfigDict(
55
+ validate_by_name=True, validate_by_alias=True, extra="allow"
56
+ )
57
+
58
+
59
+ def evaluator_discriminator(data: Any) -> str:
60
+ if isinstance(data, dict):
61
+ category = data.get("category")
62
+ evaluator_type = data.get("type")
63
+ match category:
64
+ case EvaluatorCategory.LlmAsAJudge:
65
+ return "LLMEvaluatorParams"
66
+ case EvaluatorCategory.Trajectory:
67
+ return "TrajectoryEvaluatorParams"
68
+ case EvaluatorCategory.Deterministic:
69
+ match evaluator_type:
70
+ case EvaluatorType.Equals:
71
+ return "EqualsEvaluatorParams"
72
+ case EvaluatorType.JsonSimilarity:
73
+ return "JsonSimilarityEvaluatorParams"
74
+ case _:
75
+ return "UnknownEvaluatorParams"
76
+ case _:
77
+ return "UnknownEvaluatorParams"
78
+ else:
79
+ return "UnknownEvaluatorParams"
80
+
81
+
82
+ Evaluator = Annotated[
83
+ Union[
84
+ Annotated[
85
+ LLMEvaluatorParams,
86
+ Tag("LLMEvaluatorParams"),
87
+ ],
88
+ Annotated[
89
+ TrajectoryEvaluatorParams,
90
+ Tag("TrajectoryEvaluatorParams"),
91
+ ],
92
+ Annotated[
93
+ EqualsEvaluatorParams,
94
+ Tag("EqualsEvaluatorParams"),
95
+ ],
96
+ Annotated[
97
+ JsonSimilarityEvaluatorParams,
98
+ Tag("JsonSimilarityEvaluatorParams"),
99
+ ],
100
+ Annotated[
101
+ UnknownEvaluatorParams,
102
+ Tag("UnknownEvaluatorParams"),
103
+ ],
104
+ ],
105
+ Field(discriminator=Discriminator(evaluator_discriminator)),
106
+ ]
@@ -525,6 +525,7 @@ class UiPathBaseRuntime(ABC):
525
525
  dir=self.context.runtime_dir,
526
526
  file=self.context.logs_file,
527
527
  job_id=self.context.job_id,
528
+ execution_id=self.context.execution_id,
528
529
  is_debug_run=self.is_debug_run(),
529
530
  log_handler=self.context.log_handler,
530
531
  )
@@ -1,8 +1,14 @@
1
1
  import logging
2
2
  import os
3
3
  import sys
4
+ from contextvars import ContextVar
4
5
  from typing import Optional, TextIO, Union, cast
5
6
 
7
+ # Context variable to track current execution_id
8
+ current_execution_id: ContextVar[Optional[str]] = ContextVar(
9
+ "current_execution_id", default=None
10
+ )
11
+
6
12
 
7
13
  class PersistentLogsHandler(logging.FileHandler):
8
14
  """A simple log handler that always writes to a single file without rotation."""
@@ -20,6 +26,30 @@ class PersistentLogsHandler(logging.FileHandler):
20
26
  self.setFormatter(self.formatter)
21
27
 
22
28
 
29
+ class ExecutionContextFilter(logging.Filter):
30
+ """Filter that only allows logs from a specific execution context."""
31
+
32
+ def __init__(self, execution_id: str):
33
+ super().__init__()
34
+ self.execution_id = execution_id
35
+
36
+ def filter(self, record: logging.LogRecord) -> bool:
37
+ """Allow logs that have matching execution_id attribute or context."""
38
+ # First check if record has execution_id attribute
39
+ record_execution_id = getattr(record, "execution_id", None)
40
+ if record_execution_id == self.execution_id:
41
+ return True
42
+
43
+ # Fall back to context variable
44
+ ctx_execution_id = current_execution_id.get()
45
+ if ctx_execution_id == self.execution_id:
46
+ # Inject execution_id into record for downstream handlers
47
+ record.execution_id = self.execution_id
48
+ return True
49
+
50
+ return False
51
+
52
+
23
53
  class LogsInterceptor:
24
54
  """Intercepts all logging and stdout/stderr, routing to either persistent log files or stdout based on whether it's running as a job or not."""
25
55
 
@@ -31,6 +61,7 @@ class LogsInterceptor:
31
61
  job_id: Optional[str] = None,
32
62
  is_debug_run: bool = False,
33
63
  log_handler: Optional[logging.Handler] = None,
64
+ execution_id: Optional[str] = None,
34
65
  ):
35
66
  """Initialize the log interceptor.
36
67
 
@@ -41,9 +72,11 @@ class LogsInterceptor:
41
72
  job_id (str, optional): If provided, logs go to file; otherwise, to stdout.
42
73
  is_debug_run (bool, optional): If True, log the output to stdout/stderr.
43
74
  log_handler (logging.Handler, optional): Custom log handler to use.
75
+ execution_id (str, optional): Unique identifier for this execution context.
44
76
  """
45
77
  min_level = min_level or "INFO"
46
78
  self.job_id = job_id
79
+ self.execution_id = execution_id
47
80
 
48
81
  # Convert to numeric level for consistent comparison
49
82
  self.numeric_min_level = getattr(logging, min_level.upper(), logging.INFO)
@@ -81,6 +114,12 @@ class LogsInterceptor:
81
114
  self.log_handler = PersistentLogsHandler(file=log_file)
82
115
 
83
116
  self.log_handler.setLevel(self.numeric_min_level)
117
+
118
+ # Add execution context filter if execution_id provided
119
+ if execution_id:
120
+ self.execution_filter = ExecutionContextFilter(execution_id)
121
+ self.log_handler.addFilter(self.execution_filter)
122
+
84
123
  self.logger = logging.getLogger("runtime")
85
124
  self.patched_loggers: set[str] = set()
86
125
 
@@ -95,22 +134,37 @@ class LogsInterceptor:
95
134
 
96
135
  def setup(self) -> None:
97
136
  """Configure logging to use our persistent handler."""
98
- # Use global disable to prevent all logging below our minimum level
99
- if self.numeric_min_level > logging.NOTSET:
137
+ # Set the context variable for this execution
138
+ if self.execution_id:
139
+ current_execution_id.set(self.execution_id)
140
+
141
+ # Only use global disable if we're not in a parallel execution context
142
+ if not self.execution_id and self.numeric_min_level > logging.NOTSET:
100
143
  logging.disable(self.numeric_min_level - 1)
101
144
 
102
145
  # Set root logger level
103
146
  self.root_logger.setLevel(self.numeric_min_level)
104
147
 
105
- # Remove ALL handlers from root logger and add only ours
106
- self._clean_all_handlers(self.root_logger)
148
+ if self.execution_id:
149
+ # Parallel execution mode: add our handler without removing others
150
+ if self.log_handler not in self.root_logger.handlers:
151
+ self.root_logger.addHandler(self.log_handler)
152
+
153
+ # Set up propagation for all existing loggers
154
+ for logger_name in logging.root.manager.loggerDict:
155
+ logger = logging.getLogger(logger_name)
156
+ # Keep propagation enabled so logs flow to all handlers
157
+ self.patched_loggers.add(logger_name)
158
+ else:
159
+ # Single execution mode: remove all handlers and add only ours
160
+ self._clean_all_handlers(self.root_logger)
107
161
 
108
- # Set up propagation for all existing loggers
109
- for logger_name in logging.root.manager.loggerDict:
110
- logger = logging.getLogger(logger_name)
111
- logger.propagate = False # Prevent double-logging
112
- self._clean_all_handlers(logger)
113
- self.patched_loggers.add(logger_name)
162
+ # Set up propagation for all existing loggers
163
+ for logger_name in logging.root.manager.loggerDict:
164
+ logger = logging.getLogger(logger_name)
165
+ logger.propagate = False # Prevent double-logging
166
+ self._clean_all_handlers(logger)
167
+ self.patched_loggers.add(logger_name)
114
168
 
115
169
  # Set up stdout/stderr redirection
116
170
  self._redirect_stdout_stderr()
@@ -130,7 +184,7 @@ class LogsInterceptor:
130
184
  self.level = level
131
185
  self.min_level = min_level
132
186
  self.buffer = ""
133
- self.sys_file = sys_file # Store reference to system stdout/stderr
187
+ self.sys_file = sys_file
134
188
 
135
189
  def write(self, message: str) -> None:
136
190
  self.buffer += message
@@ -138,7 +192,7 @@ class LogsInterceptor:
138
192
  line, self.buffer = self.buffer.split("\n", 1)
139
193
  # Only log if the message is not empty and the level is sufficient
140
194
  if line and self.level >= self.min_level:
141
- # Use _log to avoid potential recursive logging if logging methods are overridden
195
+ # The context variable is automatically available here
142
196
  self.logger._log(self.level, line, ())
143
197
 
144
198
  def flush(self) -> None:
@@ -160,14 +214,21 @@ class LogsInterceptor:
160
214
  def writable(self) -> bool:
161
215
  return True
162
216
 
163
- # Set up stdout and stderr loggers with propagate=False
217
+ # Set up stdout and stderr loggers
164
218
  stdout_logger = logging.getLogger("stdout")
165
- stdout_logger.propagate = False
166
- self._clean_all_handlers(stdout_logger)
167
-
168
219
  stderr_logger = logging.getLogger("stderr")
220
+
221
+ stdout_logger.propagate = False
169
222
  stderr_logger.propagate = False
170
- self._clean_all_handlers(stderr_logger)
223
+
224
+ if self.execution_id:
225
+ if self.log_handler not in stdout_logger.handlers:
226
+ stdout_logger.addHandler(self.log_handler)
227
+ if self.log_handler not in stderr_logger.handlers:
228
+ stderr_logger.addHandler(self.log_handler)
229
+ else:
230
+ self._clean_all_handlers(stdout_logger)
231
+ self._clean_all_handlers(stderr_logger)
171
232
 
172
233
  # Use the min_level in the LoggerWriter to filter messages
173
234
  sys.stdout = LoggerWriter(
@@ -179,21 +240,41 @@ class LogsInterceptor:
179
240
 
180
241
  def teardown(self) -> None:
181
242
  """Restore original logging configuration."""
182
- # Restore the original disable level
183
- logging.disable(self.original_disable_level)
184
-
185
- if self.log_handler in self.root_logger.handlers:
186
- self.root_logger.removeHandler(self.log_handler)
243
+ # Clear the context variable
244
+ if self.execution_id:
245
+ current_execution_id.set(None)
187
246
 
188
- for logger_name in self.patched_loggers:
189
- logger = logging.getLogger(logger_name)
190
- if self.log_handler in logger.handlers:
191
- logger.removeHandler(self.log_handler)
192
-
193
- self.root_logger.setLevel(self.original_level)
194
- for handler in self.original_handlers:
195
- if handler not in self.root_logger.handlers:
196
- self.root_logger.addHandler(handler)
247
+ # Restore the original disable level
248
+ if not self.execution_id:
249
+ logging.disable(self.original_disable_level)
250
+
251
+ # Remove our handler and filter
252
+ if self.execution_id:
253
+ if hasattr(self, "execution_filter"):
254
+ self.log_handler.removeFilter(self.execution_filter)
255
+ if self.log_handler in self.root_logger.handlers:
256
+ self.root_logger.removeHandler(self.log_handler)
257
+
258
+ # Remove from stdout/stderr loggers too
259
+ stdout_logger = logging.getLogger("stdout")
260
+ stderr_logger = logging.getLogger("stderr")
261
+ if self.log_handler in stdout_logger.handlers:
262
+ stdout_logger.removeHandler(self.log_handler)
263
+ if self.log_handler in stderr_logger.handlers:
264
+ stderr_logger.removeHandler(self.log_handler)
265
+ else:
266
+ if self.log_handler in self.root_logger.handlers:
267
+ self.root_logger.removeHandler(self.log_handler)
268
+
269
+ for logger_name in self.patched_loggers:
270
+ logger = logging.getLogger(logger_name)
271
+ if self.log_handler in logger.handlers:
272
+ logger.removeHandler(self.log_handler)
273
+
274
+ self.root_logger.setLevel(self.original_level)
275
+ for handler in self.original_handlers:
276
+ if handler not in self.root_logger.handlers:
277
+ self.root_logger.addHandler(handler)
197
278
 
198
279
  self.log_handler.close()
199
280
 
uipath/_cli/cli_pull.py CHANGED
@@ -112,7 +112,7 @@ async def download_folder_files(
112
112
  if local_hash != remote_hash:
113
113
  styled_path = click.style(str(file_path), fg="cyan")
114
114
  console.warning(f"File {styled_path}" + " differs from remote version.")
115
- response = click.prompt("Do you want to override it? (y/n)", type=str)
115
+ response = click.prompt("Do you want to overwrite it? (y/n)", type=str)
116
116
  if response.lower() == "y":
117
117
  with open(local_path, "w", encoding="utf-8", newline="\n") as f:
118
118
  f.write(remote_content)
uipath/agent/_utils.py CHANGED
@@ -31,6 +31,52 @@ async def load_agent_definition(project_id: str):
31
31
  await get_file(project_structure, PurePath("agent.json"), studio_client)
32
32
  ).json()
33
33
 
34
+ evaluators = []
35
+ try:
36
+ evaluators_path = resolve_path(
37
+ project_structure, PurePath("evals", "evaluators")
38
+ )
39
+ if isinstance(evaluators_path, ProjectFolder):
40
+ for file in evaluators_path.files:
41
+ evaluators.append(
42
+ (
43
+ await get_file(
44
+ evaluators_path, PurePath(file.name), studio_client
45
+ )
46
+ ).json()
47
+ )
48
+ else:
49
+ logger.warning(
50
+ "Unable to read evaluators from project. Defaulting to empty evaluators."
51
+ )
52
+ except Exception:
53
+ logger.warning(
54
+ "Unable to read evaluators from project. Defaulting to empty evaluators."
55
+ )
56
+
57
+ evaluation_sets = []
58
+ try:
59
+ evaluation_sets_path = resolve_path(
60
+ project_structure, PurePath("evals", "eval-sets")
61
+ )
62
+ if isinstance(evaluation_sets_path, ProjectFolder):
63
+ for file in evaluation_sets_path.files:
64
+ evaluation_sets.append(
65
+ (
66
+ await get_file(
67
+ evaluation_sets_path, PurePath(file.name), studio_client
68
+ )
69
+ ).json()
70
+ )
71
+ else:
72
+ logger.warning(
73
+ "Unable to read eval-sets from project. Defaulting to empty eval-sets."
74
+ )
75
+ except Exception:
76
+ logger.warning(
77
+ "Unable to read eval-sets from project. Defaulting to empty eval-sets."
78
+ )
79
+
34
80
  resolved_path = resolve_path(project_structure, PurePath("resources"))
35
81
  if isinstance(resolved_path, ProjectFolder):
36
82
  resource_folders = resolved_path.folders
@@ -50,6 +96,8 @@ async def load_agent_definition(project_id: str):
50
96
  "id": project_id,
51
97
  "name": project_structure.name,
52
98
  "resources": resources,
99
+ "evaluators": evaluators,
100
+ "evaluationSets": evaluation_sets,
53
101
  **agent,
54
102
  }
55
103
  return TypeAdapter(AgentDefinition).validate_python(agent_definition)
@@ -5,6 +5,8 @@ from typing import Annotated, Any, Dict, List, Literal, Optional, Union
5
5
 
6
6
  from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag
7
7
 
8
+ from uipath._cli._evals._models._evaluation_set import EvaluationSet
9
+ from uipath._cli._evals._models._evaluator import Evaluator
8
10
  from uipath.models import Connection
9
11
 
10
12
 
@@ -307,6 +309,14 @@ class BaseAgentDefinition(BaseModel):
307
309
  resources: List[AgentResourceConfig] = Field(
308
310
  ..., description="List of tools, context, and escalation resources"
309
311
  )
312
+ evaluation_sets: Optional[List[EvaluationSet]] = Field(
313
+ None,
314
+ alias="evaluationSets",
315
+ description="List of agent evaluation sets",
316
+ )
317
+ evaluators: Optional[List[Evaluator]] = Field(
318
+ None, description="List of agent evaluators"
319
+ )
310
320
 
311
321
  model_config = ConfigDict(
312
322
  validate_by_name=True, validate_by_alias=True, extra="allow"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath
3
- Version: 2.1.71
3
+ Version: 2.1.72
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
@@ -15,7 +15,7 @@ uipath/_cli/cli_invoke.py,sha256=m-te-EjhDpk_fhFDkt-yQFzmjEHGo5lQDGEQWxSXisQ,439
15
15
  uipath/_cli/cli_new.py,sha256=9378NYUBc9j-qKVXV7oja-jahfJhXBg8zKVyaon7ctY,2102
16
16
  uipath/_cli/cli_pack.py,sha256=NmwZTfwZ2fURiHyiX1BM0juAtBOjPB1Jmcpu-rD7p-4,11025
17
17
  uipath/_cli/cli_publish.py,sha256=DgyfcZjvfV05Ldy0Pk5y_Le_nT9JduEE_x-VpIc_Kq0,6471
18
- uipath/_cli/cli_pull.py,sha256=vwS0KMX6O2L6RaPy8tw_qzXe4dC7kf_G6nbLm0I62eI,6831
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
21
  uipath/_cli/middlewares.py,sha256=GvMhDnx1BmA7rIe12s6Uqv1JdqNZhvraU0a91oqGag4,4976
@@ -35,7 +35,7 @@ uipath/_cli/_auth/localhost.key,sha256=X31VYXD8scZtmGA837dGX5l6G-LXHLo5ItWJhZXaz
35
35
  uipath/_cli/_dev/_terminal/__init__.py,sha256=di_RiN9Mcp9wqyKRRqXag28vbSw8_78mCnQZNn9H-Ss,14027
36
36
  uipath/_cli/_dev/_terminal/_components/_chat.py,sha256=NLRoy49QScHiI-q0FGykkaU8ajv1d23fx7issSALcFA,4119
37
37
  uipath/_cli/_dev/_terminal/_components/_details.py,sha256=FbLYtJ56gqHV6CIrpzO_n9Sk_YNg4nzRKTSsbj-DBPQ,17257
38
- uipath/_cli/_dev/_terminal/_components/_history.py,sha256=dcT9tohEwpUaLGi7VWu5d-mDIF45UxFzN2Yvdf5N-eM,2691
38
+ uipath/_cli/_dev/_terminal/_components/_history.py,sha256=QX9GWiKpP1H6_wVOJkrIMb-8w2HKg8g-kcbwxiEQj2s,3237
39
39
  uipath/_cli/_dev/_terminal/_components/_json_input.py,sha256=MPkaeiA5KfkwJZKuNJ02hQksVtluZlmJv9nLRRAWYQI,592
40
40
  uipath/_cli/_dev/_terminal/_components/_new.py,sha256=paA8oRhP5mphpf3RHV0gx7_CYdN5e6158tv_XVQifdE,5219
41
41
  uipath/_cli/_dev/_terminal/_models/_execution.py,sha256=gPcxtwWR9eO929VaieOdI1e77clceKLoKA0FYayuCFQ,2869
@@ -44,19 +44,20 @@ 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/_evaluator_factory.py,sha256=OWfLxPOEcDn4qv5m3n7LBfIBKcdTPml2ZCLcsqSymlU,5329
47
+ uipath/_cli/_evals/_evaluator_factory.py,sha256=Gycv94VtGOpMir_Gba-UoiAyrSRfbSfe8_pTfjzcA9Q,3875
48
48
  uipath/_cli/_evals/_progress_reporter.py,sha256=hpSt0CXpIoFJGsbqZkqmwyGO_TBNesbWKlvDJUEDxd8,16455
49
49
  uipath/_cli/_evals/_runtime.py,sha256=WKcBT6DGzNRjgEOpmH0b7RoEbEsHMyAbcAMs8b_CAI0,11418
50
- uipath/_cli/_evals/_models/_evaluation_set.py,sha256=mwcTstHuyHd7ys_nLzgCNKBAsS4ns9UL2TF5Oq2Cc64,1758
50
+ uipath/_cli/_evals/_models/_evaluation_set.py,sha256=RRDaP0X4E8kueL0Io9yB4y8akx3gKZhoSIgTNhgoN9Y,2407
51
+ uipath/_cli/_evals/_models/_evaluator.py,sha256=fuC3UOYwPD4d_wdynHeLSCzbu82golNAnnPnxC8Y4rk,3315
51
52
  uipath/_cli/_evals/_models/_evaluator_base_params.py,sha256=lTYKOV66tcjW85KHTyOdtF1p1VDaBNemrMAvH8bFIFc,382
52
53
  uipath/_cli/_evals/_models/_output.py,sha256=LjwMBGI78sDFa2Dl8b9ReXJmjig57pdLWpuiwChrRLo,3096
53
54
  uipath/_cli/_evals/_models/_sw_reporting.py,sha256=tSBLQFAdOIun8eP0vsqt56K6bmCZz_uMaWI3hskg_24,536
54
55
  uipath/_cli/_evals/_models/_trajectory_span.py,sha256=8ukM8sB9rvzBMHfC_gnexAC3xlp4uMDevKZrRzcgrm4,3637
55
56
  uipath/_cli/_push/sw_file_handler.py,sha256=iE8Sk1Z-9hxmLFFj3j-k4kTK6TzNFP6hUCmxTudG6JQ,18251
56
- uipath/_cli/_runtime/_contracts.py,sha256=xIcKq0xRbenzmJkZQO8blKwZ3b72Ntm4YONSYwaI-kg,28880
57
+ uipath/_cli/_runtime/_contracts.py,sha256=D57cq5V5CZ9p13n_vRDHRcwyJYQUcJLlAMbAOzIiBNI,28932
57
58
  uipath/_cli/_runtime/_escalation.py,sha256=x3vI98qsfRA-fL_tNkRVTFXioM5Gv2w0GFcXJJ5eQtg,7981
58
59
  uipath/_cli/_runtime/_hitl.py,sha256=VKbM021nVg1HEDnTfucSLJ0LsDn83CKyUtVzofS2qTU,11369
59
- uipath/_cli/_runtime/_logging.py,sha256=MGklGKPjYKjs7J5Jy9eplA9zCDsdtEbkZdCbTwgut_4,8311
60
+ uipath/_cli/_runtime/_logging.py,sha256=iO0AG_tqUBp7aJZTK_ZgwV3fFvxbi9Rp1UOBn3F76lw,11684
60
61
  uipath/_cli/_runtime/_runtime.py,sha256=gby9-avNNlEATEfSXtY8FfJ8nREsSCGA4wMgDlSXTDE,2297
61
62
  uipath/_cli/_runtime/_script_executor.py,sha256=PjbmEbyCMofGH2F85b8RFsxdV3Tqw0kVqdWOOk2ZLlI,9687
62
63
  uipath/_cli/_templates/.psmdcp.template,sha256=C7pBJPt98ovEljcBvGtEUGoWjjQhu9jls1bpYjeLOKA,611
@@ -108,7 +109,7 @@ uipath/_utils/_ssl_context.py,sha256=xSYitos0eJc9cPHzNtHISX9PBvL6D2vas5G_GiBdLp8
108
109
  uipath/_utils/_url.py,sha256=-4eluSrIZCUlnQ3qU17WPJkgaC2KwF9W5NeqGnTNGGo,2512
109
110
  uipath/_utils/_user_agent.py,sha256=pVJkFYacGwaQBomfwWVAvBQgdBUo62e4n3-fLIajWUU,563
110
111
  uipath/_utils/constants.py,sha256=2xLT-1aW0aJS2USeZbK-7zRgyyi1bgV60L0rtQOUqOM,1721
111
- uipath/agent/_utils.py,sha256=Z5x7TprkEudrd_aC9YI5ObyT8O05_WkmBJTpqkQKOGQ,1641
112
+ uipath/agent/_utils.py,sha256=frpg3LYJofdGWS5w44PsBaNHiW9GRkLokvNnf2D4a54,3309
112
113
  uipath/agent/conversation/__init__.py,sha256=5hK-Iz131mnd9m6ANnpZZffxXZLVFDQ9GTg5z9ik1oQ,5265
113
114
  uipath/agent/conversation/async_stream.py,sha256=BA_8uU1DgE3VpU2KkJj0rkI3bAHLk_ZJKsajR0ipMpo,2055
114
115
  uipath/agent/conversation/citation.py,sha256=42dGv-wiYx3Lt7MPuPCFTkjAlSADFSzjyNXuZHdxqvo,2253
@@ -119,7 +120,7 @@ uipath/agent/conversation/exchange.py,sha256=nuk1tEMBHc_skrraT17d8U6AtyJ3h07ExGQ
119
120
  uipath/agent/conversation/message.py,sha256=1ZkEs146s79TrOAWCQwzBAEJvjAu4lQBpJ64tKXDgGE,2142
120
121
  uipath/agent/conversation/meta.py,sha256=3t0eS9UHoAPHre97QTUeVbjDhnMX4zj4-qG6ju0B8wY,315
121
122
  uipath/agent/conversation/tool.py,sha256=ol8XI8AVd-QNn5auXNBPcCzOkh9PPFtL7hTK3kqInkU,2191
122
- uipath/agent/models/agent.py,sha256=HVdv-VHV98ZI4afIxexPDSKEKzGlMJ7uSRLj7qLe6vA,12548
123
+ uipath/agent/models/agent.py,sha256=ie_N47K-txv9Q9kXeynI1FsO2l120JYmjPXNm-e6yMo,12952
123
124
  uipath/eval/_helpers/__init__.py,sha256=GSmZMryjuO3Wo_zdxZdrHCRRsgOxsVFYkYgJ15YNC3E,86
124
125
  uipath/eval/_helpers/helpers.py,sha256=iE2HHdMiAdAMLqxHkPKHpfecEtAuN5BTBqvKFTI8ciE,1315
125
126
  uipath/eval/evaluators/__init__.py,sha256=DJAAhgv0I5UfBod4sGnSiKerfrz1iMmk7GNFb71V8eI,494
@@ -158,8 +159,8 @@ uipath/tracing/_traced.py,sha256=yBIY05PCCrYyx50EIHZnwJaKNdHPNx-YTR1sHQl0a98,199
158
159
  uipath/tracing/_utils.py,sha256=qd7N56tg6VXQ9pREh61esBgUWLNA0ssKsE0QlwrRWFM,11974
159
160
  uipath/utils/__init__.py,sha256=VD-KXFpF_oWexFg6zyiWMkxl2HM4hYJMIUDZ1UEtGx0,105
160
161
  uipath/utils/_endpoints_manager.py,sha256=iRTl5Q0XAm_YgcnMcJOXtj-8052sr6jpWuPNz6CgT0Q,8408
161
- uipath-2.1.71.dist-info/METADATA,sha256=45oUACkDT5dlFX6IuVnvbE-5WRCYwQMRfRsdYauUjFU,6482
162
- uipath-2.1.71.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
163
- uipath-2.1.71.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
164
- uipath-2.1.71.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
165
- uipath-2.1.71.dist-info/RECORD,,
162
+ uipath-2.1.72.dist-info/METADATA,sha256=sIUVm86o9y3uorIq58WEe4g-58aQ_tD0mp_8TPcrhHc,6482
163
+ uipath-2.1.72.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
164
+ uipath-2.1.72.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
165
+ uipath-2.1.72.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
166
+ uipath-2.1.72.dist-info/RECORD,,