uipath 2.1.20__py3-none-any.whl → 2.1.22__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.
@@ -107,6 +107,10 @@ class EvaluatorFactory:
107
107
  model = data.get("model", "")
108
108
  if not model:
109
109
  raise ValueError("LLM evaluator must include 'model' field")
110
+ if model == "same-as-agent":
111
+ raise ValueError(
112
+ "'same-as-agent' model option is not supported by coded agents evaluations. Please select a specific model for the evaluator."
113
+ )
110
114
 
111
115
  return LlmAsAJudgeEvaluator.from_params(
112
116
  base_params,
@@ -36,6 +36,16 @@ class EvaluationSet(BaseModel):
36
36
  createdAt: str
37
37
  updatedAt: str
38
38
 
39
+ def extract_selected_evals(self, eval_ids) -> None:
40
+ selected_evals: list[EvaluationItem] = []
41
+ for evaluation in self.evaluations:
42
+ if evaluation.id in eval_ids:
43
+ selected_evals.append(evaluation)
44
+ eval_ids.remove(evaluation.id)
45
+ if len(eval_ids) > 0:
46
+ raise ValueError("Unknown evaluation ids: {}".format(eval_ids))
47
+ self.evaluations = selected_evals
48
+
39
49
 
40
50
  class EvaluationStatus(IntEnum):
41
51
  PENDING = 0
@@ -33,6 +33,7 @@ class EvaluationService:
33
33
  self,
34
34
  entrypoint: Optional[str] = None,
35
35
  eval_set_path: Optional[str | Path] = None,
36
+ eval_ids: Optional[List[str]] = None,
36
37
  workers: int = 8,
37
38
  report_progress: bool = True,
38
39
  ):
@@ -47,10 +48,10 @@ class EvaluationService:
47
48
  self.entrypoint, self.eval_set_path = self._resolve_paths(
48
49
  entrypoint, eval_set_path
49
50
  )
50
- self.eval_set = self._load_eval_set()
51
+ self._eval_set = self._load_eval_set(eval_ids)
51
52
  self._evaluators = self._load_evaluators()
52
- self.num_workers = workers
53
- self.results_lock = asyncio.Lock()
53
+ self._num_workers = workers
54
+ self._results_lock = asyncio.Lock()
54
55
  self._progress_manager: Optional[EvaluationProgressManager] = None
55
56
  self._report_progress = report_progress
56
57
  self._progress_reporter: Optional[ProgressReporter] = None
@@ -169,9 +170,9 @@ class EvaluationService:
169
170
  if self._report_progress:
170
171
  agent_snapshot = self._extract_agent_snapshot()
171
172
  self._progress_reporter = ProgressReporter(
172
- eval_set_id=self.eval_set.id,
173
+ eval_set_id=self._eval_set.id,
173
174
  agent_snapshot=agent_snapshot,
174
- no_of_evals=len(self.eval_set.evaluations),
175
+ no_of_evals=len(self._eval_set.evaluations),
175
176
  evaluators=self._evaluators,
176
177
  )
177
178
 
@@ -215,12 +216,12 @@ class EvaluationService:
215
216
 
216
217
  # Create results file
217
218
  timestamp = datetime.now(timezone.utc).strftime("%M-%H-%d-%m-%Y")
218
- eval_set_name = self.eval_set.name
219
+ eval_set_name = self._eval_set.name
219
220
  self.result_file = results_dir / f"eval-{eval_set_name}-{timestamp}.json"
220
221
 
221
222
  initial_results = EvaluationSetResult(
222
- eval_set_id=self.eval_set.id,
223
- eval_set_name=self.eval_set.name,
223
+ eval_set_id=self._eval_set.id,
224
+ eval_set_name=self._eval_set.name,
224
225
  results=[],
225
226
  average_score=0.0,
226
227
  )
@@ -228,7 +229,7 @@ class EvaluationService:
228
229
  with open(self.result_file, "w", encoding="utf-8") as f:
229
230
  f.write(initial_results.model_dump_json(indent=2))
230
231
 
231
- def _load_eval_set(self) -> EvaluationSet:
232
+ def _load_eval_set(self, eval_ids: Optional[List[str]] = None) -> EvaluationSet:
232
233
  """Load the evaluation set from file.
233
234
 
234
235
  Returns:
@@ -236,13 +237,16 @@ class EvaluationService:
236
237
  """
237
238
  with open(self.eval_set_path, "r", encoding="utf-8") as f:
238
239
  data = json.load(f)
239
- return EvaluationSet(**data)
240
+ eval_set = EvaluationSet(**data)
241
+ if eval_ids:
242
+ eval_set.extract_selected_evals(eval_ids)
243
+ return eval_set
240
244
 
241
245
  def _load_evaluators(self) -> List[EvaluatorBase]:
242
246
  """Load evaluators referenced by the evaluation set."""
243
247
  evaluators = []
244
248
  evaluators_dir = self.eval_set_path.parent.parent / "evaluators"
245
- evaluator_refs = set(self.eval_set.evaluatorRefs)
249
+ evaluator_refs = set(self._eval_set.evaluatorRefs)
246
250
  found_evaluator_ids = set()
247
251
 
248
252
  # Load evaluators from JSON files
@@ -252,14 +256,9 @@ class EvaluationService:
252
256
  evaluator_id = data.get("id")
253
257
 
254
258
  if evaluator_id in evaluator_refs:
255
- try:
256
- evaluator = EvaluatorFactory.create_evaluator(data)
257
- evaluators.append(evaluator)
258
- found_evaluator_ids.add(evaluator_id)
259
- except Exception as e:
260
- console.warning(
261
- f"Failed to create evaluator {evaluator_id}: {str(e)}"
262
- )
259
+ evaluator = EvaluatorFactory.create_evaluator(data)
260
+ evaluators.append(evaluator)
261
+ found_evaluator_ids.add(evaluator_id)
263
262
 
264
263
  # Check if all referenced evaluators were found
265
264
  missing_evaluators = evaluator_refs - found_evaluator_ids
@@ -276,7 +275,7 @@ class EvaluationService:
276
275
  Args:
277
276
  results: List of evaluation results to write
278
277
  """
279
- async with self.results_lock:
278
+ async with self._results_lock:
280
279
  # Read current results
281
280
  with open(self.result_file, "r", encoding="utf-8") as f:
282
281
  current_results = EvaluationSetResult.model_validate_json(f.read())
@@ -473,11 +472,11 @@ class EvaluationService:
473
472
  Args:
474
473
  task_queue: The asyncio queue to add tasks to
475
474
  """
476
- for eval_item in self.eval_set.evaluations:
475
+ for eval_item in self._eval_set.evaluations:
477
476
  await task_queue.put(eval_item.model_dump())
478
477
 
479
478
  # Add sentinel values to signal workers to stop
480
- for _ in range(self.num_workers):
479
+ for _ in range(self._num_workers):
481
480
  await task_queue.put(None)
482
481
 
483
482
  async def _consumer_task(
@@ -517,7 +516,7 @@ class EvaluationService:
517
516
  async def run_evaluation(self) -> None:
518
517
  """Run the evaluation set using multiple worker tasks."""
519
518
  console.info(
520
- f"Starting evaluating {click.style(self.eval_set.name, fg='cyan')} evaluation set..."
519
+ f"Starting evaluating {click.style(self._eval_set.name, fg='cyan')} evaluation set..."
521
520
  )
522
521
 
523
522
  if self._report_progress and self._progress_reporter:
@@ -526,7 +525,7 @@ class EvaluationService:
526
525
  # Prepare items for progress tracker
527
526
  progress_items = [
528
527
  {"id": eval_item.id, "name": eval_item.name}
529
- for eval_item in self.eval_set.evaluations
528
+ for eval_item in self._eval_set.evaluations
530
529
  ]
531
530
 
532
531
  with console.evaluation_progress(progress_items) as progress_manager:
@@ -539,7 +538,7 @@ class EvaluationService:
539
538
  producer = asyncio.create_task(self._producer_task(task_queue))
540
539
 
541
540
  consumers = []
542
- for worker_id in range(self.num_workers):
541
+ for worker_id in range(self._num_workers):
543
542
  consumer = asyncio.create_task(
544
543
  self._consumer_task(
545
544
  task_queue, worker_id, results_queue, sw_progress_reporter_queue
@@ -133,12 +133,14 @@ class SwFileHandler:
133
133
  self,
134
134
  local_files: list[FileInfo],
135
135
  source_code_files: Dict[str, ProjectFile],
136
+ root_files: Dict[str, ProjectFile],
136
137
  ) -> None:
137
138
  """Process all file uploads to the source_code folder.
138
139
 
139
140
  Args:
140
141
  local_files: List of files to upload
141
142
  source_code_files: Dictionary of existing remote files
143
+ root_files: Dictionary of existing root-level files
142
144
 
143
145
  Returns:
144
146
  Set of processed file names
@@ -186,13 +188,25 @@ class SwFileHandler:
186
188
  )
187
189
  )
188
190
  self.console.info(
189
- f"Uploading {click.style(local_file.file_name, fg='cyan')}"
191
+ f"Uploading {click.style(local_file.relative_path, fg='cyan')}"
190
192
  )
191
193
 
192
194
  # identify and add deleted files
193
195
  structural_migration.deleted_resources.extend(
194
196
  self._collect_deleted_files(source_code_files, processed_source_files)
195
197
  )
198
+
199
+ with open(os.path.join(self.directory, "uipath.json"), "r") as f:
200
+ uipath_config = json.load(f)
201
+
202
+ await self._prepare_agent_json_migration(
203
+ structural_migration, root_files, uipath_config
204
+ )
205
+
206
+ await self._prepare_entrypoints_json_migration(
207
+ structural_migration, root_files, uipath_config
208
+ )
209
+
196
210
  await self._studio_client.perform_structural_migration_async(
197
211
  structural_migration
198
212
  )
@@ -303,40 +317,65 @@ class SwFileHandler:
303
317
 
304
318
  return True
305
319
 
306
- async def _update_agent_json(
320
+ async def _prepare_entrypoints_json_migration(
307
321
  self,
308
- agent_json_file: Optional[ProjectFile] = None,
322
+ structural_migration: StructuralMigration,
323
+ root_files: Dict[str, ProjectFile],
324
+ uipath_config: Dict[str, Any],
309
325
  ) -> None:
310
- """Update agent.json file with metadata from uipath.json.
326
+ """Prepare entry-points.json to be included in the same structural migration."""
327
+ existing = root_files.get("entry-points.json")
328
+ if existing:
329
+ try:
330
+ entry_points_json = (
331
+ await self._studio_client.download_file_async(existing.id)
332
+ ).json()
333
+ entry_points_json["entryPoints"] = uipath_config["entryPoints"]
311
334
 
312
- This function:
313
- 1. Downloads existing agent.json if it exists
314
- 2. Updates metadata based on uipath.json content
315
- 3. Increments code version
316
- 4. Updates author from JWT or pyproject.toml
317
- 5. Uploads updated agent.json
335
+ except Exception:
336
+ self.console.warning(
337
+ "Could not parse existing entry-points.json file, using default version"
338
+ )
339
+ structural_migration.modified_resources.append(
340
+ ModifiedResource(
341
+ id=existing.id,
342
+ content_string=json.dumps(entry_points_json),
343
+ )
344
+ )
345
+ self.console.info(
346
+ f"Updating {click.style('entry-points.json', fg='yellow')}"
347
+ )
318
348
 
319
- Args:
320
- agent_json_file: Optional existing agent.json file
349
+ else:
350
+ self.console.warning(
351
+ "'entry-points.json' file does not exist in Studio Web project, initializing using default version"
352
+ )
353
+ entry_points_json = {
354
+ "$schema": "https://cloud.uipath.com/draft/2024-12/entry-point",
355
+ "$id": "entry-points.json",
356
+ "entryPoints": uipath_config["entryPoints"],
357
+ }
358
+ structural_migration.added_resources.append(
359
+ AddedResource(
360
+ file_name="entry-points.json",
361
+ content_string=json.dumps(entry_points_json),
362
+ )
363
+ )
364
+ self.console.info(
365
+ f"Uploading {click.style('entry-points.json', fg='cyan')}"
366
+ )
321
367
 
322
- Raises:
323
- httpx.HTTPError: If API requests fail
324
- FileNotFoundError: If required files are missing
325
- json.JSONDecodeError: If JSON parsing fails
326
- """
368
+ async def _prepare_agent_json_migration(
369
+ self,
370
+ structural_migration: StructuralMigration,
371
+ root_files: Dict[str, ProjectFile],
372
+ uipath_config: Dict[str, Any],
373
+ ) -> None:
374
+ """Prepare agent.json to be included in the same structural migration."""
327
375
 
328
376
  def get_author_from_token_or_toml() -> str:
329
377
  import jwt
330
378
 
331
- """Extract preferred_username from JWT token or fall back to pyproject.toml author.
332
-
333
- Args:
334
- directory: Project directory containing pyproject.toml
335
-
336
- Returns:
337
- str: Author name from JWT preferred_username or pyproject.toml authors field
338
- """
339
- # Try to get author from JWT token first
340
379
  token = os.getenv("UIPATH_ACCESS_TOKEN")
341
380
  if token:
342
381
  try:
@@ -350,19 +389,15 @@ class SwFileHandler:
350
389
  # If JWT decoding fails, fall back to toml
351
390
  pass
352
391
 
353
- toml_data = read_toml_project(os.path.join(directory, "pyproject.toml"))
354
-
392
+ toml_data = read_toml_project(
393
+ os.path.join(self.directory, "pyproject.toml")
394
+ )
355
395
  return toml_data.get("authors", "").strip()
356
396
 
357
- # Read uipath.json
358
- directory = os.getcwd()
359
- with open(os.path.join(directory, "uipath.json"), "r") as f:
360
- uipath_config = json.load(f)
361
-
362
397
  try:
363
398
  entrypoints = [
364
- {"input": entry_point["input"], "output": entry_point["output"]}
365
- for entry_point in uipath_config["entryPoints"]
399
+ {"input": entrypoint["input"], "output": entrypoint["output"]}
400
+ for entrypoint in uipath_config["entryPoints"]
366
401
  ]
367
402
  except (FileNotFoundError, KeyError) as e:
368
403
  self.console.error(
@@ -388,14 +423,12 @@ class SwFileHandler:
388
423
  ),
389
424
  }
390
425
 
391
- if agent_json_file:
392
- # Download existing agent.json
393
- existing_agent_json = (
394
- await self._studio_client.download_file_async(agent_json_file.id)
395
- ).json()
396
-
426
+ existing = root_files.get("agent.json")
427
+ if existing:
397
428
  try:
398
- # Get current version and increment patch version
429
+ existing_agent_json = (
430
+ await self._studio_client.download_file_async(existing.id)
431
+ ).json()
399
432
  version_parts = existing_agent_json["metadata"]["codeVersion"].split(
400
433
  "."
401
434
  )
@@ -403,20 +436,32 @@ class SwFileHandler:
403
436
  version_parts[-1] = str(int(version_parts[-1]) + 1)
404
437
  agent_json["metadata"]["codeVersion"] = ".".join(version_parts)
405
438
  else:
406
- # If version format is invalid, start from initial version + 1
407
439
  agent_json["metadata"]["codeVersion"] = (
408
440
  AGENT_INITIAL_CODE_VERSION[:-1] + "1"
409
441
  )
410
- except (json.JSONDecodeError, KeyError, ValueError):
442
+ except Exception:
411
443
  self.console.warning(
412
- "Could not parse existing agent.json, using default version"
444
+ "Could not parse existing agent.json file, using default version"
413
445
  )
414
- file, action = await self._studio_client.upload_file_async(
415
- file_content=json.dumps(agent_json),
416
- file_name="agent.json",
417
- remote_file=agent_json_file,
418
- )
419
- self.console.success(f"{action} {click.style('agent.json', fg='cyan')}")
446
+
447
+ structural_migration.modified_resources.append(
448
+ ModifiedResource(
449
+ id=existing.id,
450
+ content_string=json.dumps(agent_json),
451
+ )
452
+ )
453
+ self.console.info(f"Updating {click.style('agent.json', fg='yellow')}")
454
+ else:
455
+ self.console.warning(
456
+ "'agent.json' file does not exist in Studio Web project, initializing using default version"
457
+ )
458
+ structural_migration.added_resources.append(
459
+ AddedResource(
460
+ file_name="agent.json",
461
+ content_string=json.dumps(agent_json),
462
+ )
463
+ )
464
+ self.console.info(f"Uploading {click.style('agent.json', fg='cyan')}")
420
465
 
421
466
  async def upload_source_files(self, config_data: dict[str, Any]) -> None:
422
467
  """Main method to upload source files to the UiPath project.
@@ -457,8 +502,4 @@ class SwFileHandler:
457
502
  self.include_uv_lock,
458
503
  directories_to_ignore=["evals"],
459
504
  )
460
- await self._process_file_uploads(files, source_code_files)
461
-
462
- await self._update_agent_json(
463
- root_files.get("agent.json", None),
464
- )
505
+ await self._process_file_uploads(files, source_code_files, root_files)
@@ -175,13 +175,20 @@ def get_folder_by_name(
175
175
 
176
176
 
177
177
  class AddedResource(BaseModel):
178
- content_file_path: str
178
+ """Represents a new file to be added during a structural migration."""
179
+
180
+ content_file_path: Optional[str] = None
179
181
  parent_path: Optional[str] = None
182
+ file_name: Optional[str] = None
183
+ content_string: Optional[str] = None
180
184
 
181
185
 
182
186
  class ModifiedResource(BaseModel):
187
+ """Represents a file update during a structural migration."""
188
+
183
189
  id: str
184
- content_file_path: str
190
+ content_file_path: Optional[str] = None
191
+ content_string: Optional[str] = None
185
192
 
186
193
 
187
194
  class StructuralMigration(BaseModel):
@@ -336,6 +343,53 @@ class StudioClient:
336
343
  headers=headers or {},
337
344
  )
338
345
 
346
+ def _resolve_content_and_filename(
347
+ self,
348
+ *,
349
+ content_string: Optional[str],
350
+ content_file_path: Optional[str],
351
+ file_name: Optional[str] = None,
352
+ modified: bool = False,
353
+ ) -> tuple[bytes, Optional[str]]:
354
+ """Resolve multipart content bytes and filename for a resource.
355
+
356
+ Args:
357
+ content_string: Inline content as a string.
358
+ content_file_path: Path to a local file to read if inline content is not provided.
359
+ file_name: Explicit filename to use when adding a new resource.
360
+
361
+ Returns:
362
+ A tuple of (content_bytes, filename).
363
+
364
+ Raises:
365
+ FileNotFoundError: If a provided file path does not exist.
366
+ ValueError: If a filename cannot be determined.
367
+ """
368
+ content_bytes: bytes = b""
369
+ resolved_name: Optional[str] = None
370
+ if content_string is not None:
371
+ content_bytes = content_string.encode("utf-8")
372
+ elif content_file_path:
373
+ if os.path.exists(content_file_path):
374
+ with open(content_file_path, "rb") as f:
375
+ content_bytes = f.read()
376
+ else:
377
+ raise FileNotFoundError(f"File not found: {content_file_path}")
378
+
379
+ if file_name:
380
+ resolved_name = file_name
381
+ elif content_file_path:
382
+ resolved_name = os.path.basename(content_file_path)
383
+ elif not modified:
384
+ raise ValueError(
385
+ "Unable to determine filename for multipart upload. "
386
+ "When providing inline content (content_string), you must also provide file_name. "
387
+ "Alternatively, set content_file_path so the filename can be inferred. "
388
+ f"Received file_name={file_name!r}, content_file_path={content_file_path!r}."
389
+ )
390
+
391
+ return content_bytes, resolved_name
392
+
339
393
  @with_lock_retry
340
394
  async def perform_structural_migration_async(
341
395
  self,
@@ -360,44 +414,37 @@ class StudioClient:
360
414
  (None, deleted_resources_json),
361
415
  )
362
416
  )
363
-
364
417
  for i, added_resource in enumerate(structural_migration.added_resources):
365
- if os.path.exists(added_resource.content_file_path):
366
- with open(added_resource.content_file_path, "rb") as f:
367
- content = f.read()
368
-
369
- filename = os.path.basename(added_resource.content_file_path)
370
- files.append((f"AddedResources[{i}].Content", (filename, content)))
371
-
372
- if added_resource.parent_path:
373
- files.append(
374
- (
375
- f"AddedResources[{i}].ParentPath",
376
- (None, added_resource.parent_path),
377
- )
378
- )
379
- else:
380
- raise FileNotFoundError(
381
- f"File not found: {added_resource.content_file_path}"
382
- )
418
+ content_bytes, filename = self._resolve_content_and_filename(
419
+ content_string=added_resource.content_string,
420
+ content_file_path=added_resource.content_file_path,
421
+ file_name=added_resource.file_name,
422
+ )
383
423
 
384
- for i, modified_resource in enumerate(structural_migration.modified_resources):
385
- if os.path.exists(modified_resource.content_file_path):
386
- with open(modified_resource.content_file_path, "rb") as f:
387
- content = f.read()
424
+ files.append((f"AddedResources[{i}].Content", (filename, content_bytes)))
388
425
 
389
- filename = os.path.basename(modified_resource.content_file_path)
390
- files.append((f"ModifiedResources[{i}].Content", (filename, content)))
426
+ if added_resource.parent_path:
391
427
  files.append(
392
428
  (
393
- f"ModifiedResources[{i}].Id",
394
- (None, modified_resource.id),
429
+ f"AddedResources[{i}].ParentPath",
430
+ (None, added_resource.parent_path),
395
431
  )
396
432
  )
397
- else:
398
- raise FileNotFoundError(
399
- f"File not found: {modified_resource.content_file_path}"
433
+
434
+ for i, modified_resource in enumerate(structural_migration.modified_resources):
435
+ content_bytes, _ = self._resolve_content_and_filename(
436
+ content_string=modified_resource.content_string,
437
+ content_file_path=modified_resource.content_file_path,
438
+ modified=True,
439
+ )
440
+
441
+ files.append((f"ModifiedResources[{i}].Content", content_bytes))
442
+ files.append(
443
+ (
444
+ f"ModifiedResources[{i}].Id",
445
+ (None, modified_resource.id),
400
446
  )
447
+ )
401
448
 
402
449
  response = await self.uipath.api_client.request_async(
403
450
  "POST",
uipath/_cli/cli_eval.py CHANGED
@@ -1,7 +1,8 @@
1
1
  # type: ignore
2
+ import ast
2
3
  import asyncio
3
4
  import os
4
- from typing import Optional, Tuple
5
+ from typing import List, Optional, Tuple
5
6
 
6
7
  import click
7
8
  from dotenv import load_dotenv
@@ -15,9 +16,18 @@ console = ConsoleLogger()
15
16
  load_dotenv(override=True)
16
17
 
17
18
 
19
+ class LiteralOption(click.Option):
20
+ def type_cast_value(self, ctx, value):
21
+ try:
22
+ return ast.literal_eval(value)
23
+ except Exception as e:
24
+ raise click.BadParameter(value) from e
25
+
26
+
18
27
  def eval_agent(
19
28
  entrypoint: Optional[str] = None,
20
29
  eval_set: Optional[str] = None,
30
+ eval_ids: Optional[List[str]] = None,
21
31
  workers: int = 8,
22
32
  no_report: bool = False,
23
33
  **kwargs,
@@ -27,6 +37,7 @@ def eval_agent(
27
37
  Args:
28
38
  entrypoint: Path to the agent script to evaluate (optional, will auto-discover if not provided)
29
39
  eval_set: Path to the evaluation set JSON file (optional, will auto-discover if not provided)
40
+ eval_ids: Optional list of evaluation IDs
30
41
  workers: Number of parallel workers for running evaluations
31
42
  no_report: Do not report the evaluation results
32
43
  **kwargs: Additional arguments for future extensibility
@@ -41,8 +52,13 @@ def eval_agent(
41
52
  if workers < 1:
42
53
  return False, "Number of workers must be at least 1", None
43
54
 
55
+ print("EVAL SET")
56
+ print(eval_set)
57
+ if eval_set is not None and len(eval_set) == 0:
58
+ return False, "Evaluation set must not be empty", None
59
+
44
60
  service = EvaluationService(
45
- entrypoint, eval_set, workers, report_progress=not no_report
61
+ entrypoint, eval_set, eval_ids, workers, report_progress=not no_report
46
62
  )
47
63
  asyncio.run(service.run_evaluation())
48
64
 
@@ -55,6 +71,7 @@ def eval_agent(
55
71
  @click.command()
56
72
  @click.argument("entrypoint", required=False)
57
73
  @click.argument("eval_set", required=False)
74
+ @click.option("--eval-ids", cls=LiteralOption, default="[]")
58
75
  @click.option(
59
76
  "--no-report",
60
77
  is_flag=True,
@@ -69,20 +86,28 @@ def eval_agent(
69
86
  )
70
87
  @track(when=lambda *_a, **_kw: os.getenv(ENV_JOB_ID) is None)
71
88
  def eval(
72
- entrypoint: Optional[str], eval_set: Optional[str], no_report: bool, workers: int
89
+ entrypoint: Optional[str],
90
+ eval_set: Optional[str],
91
+ eval_ids: List[str],
92
+ no_report: bool,
93
+ workers: int,
73
94
  ) -> None:
74
95
  """Run an evaluation set against the agent.
75
96
 
76
97
  Args:
77
98
  entrypoint: Path to the agent script to evaluate (optional, will auto-discover if not specified)
78
99
  eval_set: Path to the evaluation set JSON file (optional, will auto-discover if not specified)
100
+ eval_ids: Optional list of evaluation IDs
79
101
  workers: Number of parallel workers for running evaluations
80
102
  no_report: Do not report the evaluation results
81
103
  """
82
104
  success, error_message, info_message = eval_agent(
83
- entrypoint=entrypoint, eval_set=eval_set, workers=workers, no_report=no_report
105
+ entrypoint=entrypoint,
106
+ eval_set=eval_set,
107
+ eval_ids=eval_ids,
108
+ workers=workers,
109
+ no_report=no_report,
84
110
  )
85
-
86
111
  if error_message:
87
112
  console.error(error_message)
88
113
  click.get_current_context().exit(1)
uipath/_cli/cli_invoke.py CHANGED
@@ -70,7 +70,6 @@ def invoke(
70
70
  url = f"{base_url}/orchestrator_/odata/Jobs/UiPath.Server.Configuration.OData.StartJobs"
71
71
  _, personal_workspace_folder_id = get_personal_workspace_info(base_url, token)
72
72
  project_name, project_version = _read_project_details()
73
-
74
73
  if not personal_workspace_folder_id:
75
74
  console.error(
76
75
  "No personal workspace found for user. Please try reauthenticating."
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath
3
- Version: 2.1.20
3
+ Version: 2.1.22
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
@@ -8,9 +8,9 @@ uipath/_cli/README.md,sha256=GLtCfbeIKZKNnGTCsfSVqRQ27V1btT1i2bSAyW_xZl4,474
8
8
  uipath/_cli/__init__.py,sha256=oG0oTrb60qfIncJ0EcGsytBYxAVbepcBlOkqBKQlsJM,2104
9
9
  uipath/_cli/cli_auth.py,sha256=RUSBHfmqhBtITrx52FeXMlVCuNyo8vrjTdjEhmM1Khw,6734
10
10
  uipath/_cli/cli_deploy.py,sha256=KPCmQ0c_NYD5JofSDao5r6QYxHshVCRxlWDVnQvlp5w,645
11
- uipath/_cli/cli_eval.py,sha256=z0ER8pN5rJyINcSr1tM75HbSlmZXtx96YtqDvDI6zHk,2945
11
+ uipath/_cli/cli_eval.py,sha256=INkfaZKadShtFOrVfTNM7K2kjXV-cwIqsOfIEYqDSGc,3656
12
12
  uipath/_cli/cli_init.py,sha256=jksza6bHfh4z1nKyEJBEEZlkO37yZoCz_FJWq_RPhWI,6093
13
- uipath/_cli/cli_invoke.py,sha256=FurosrZNGlmANIrplKWhw3EQ1b46ph5Z2rPwVaYJgmc,4001
13
+ uipath/_cli/cli_invoke.py,sha256=4Oc6CM21Y24b_5I2MAqu-TffZL1aOKQwxfBmC6mPR8o,4000
14
14
  uipath/_cli/cli_new.py,sha256=9378NYUBc9j-qKVXV7oja-jahfJhXBg8zKVyaon7ctY,2102
15
15
  uipath/_cli/cli_pack.py,sha256=NmwZTfwZ2fURiHyiX1BM0juAtBOjPB1Jmcpu-rD7p-4,11025
16
16
  uipath/_cli/cli_publish.py,sha256=QT17JTClAyLve6ZjB-WvQaJ-j4DdmNneV_eDRyXjeeQ,6578
@@ -29,20 +29,20 @@ uipath/_cli/_auth/auth_config.json,sha256=UnAhdum8phjuZaZKE5KLp0IcPCbIltDEU1M_G8
29
29
  uipath/_cli/_auth/index.html,sha256=_Q2OtqPfapG_6vumbQYqtb2PfFe0smk7TlGERKEBvB4,22518
30
30
  uipath/_cli/_auth/localhost.crt,sha256=oGl9oLLOiouHubAt39B4zEfylFvKEtbtr_43SIliXJc,1226
31
31
  uipath/_cli/_auth/localhost.key,sha256=X31VYXD8scZtmGA837dGX5l6G-LXHLo5ItWJhZXaz3c,1679
32
- uipath/_cli/_evals/evaluation_service.py,sha256=VVxZxoCJoB2SUhej_c0DzC9AlnIlWMKnug7z5weNSoE,22077
32
+ uipath/_cli/_evals/evaluation_service.py,sha256=zqYRB-tZpTTFqMctjIpEli3joIlmrz3dCVZsxekxIps,22053
33
33
  uipath/_cli/_evals/progress_reporter.py,sha256=m1Dio1vG-04nFTFz5ijM_j1dhudlgOzQukmTkkg6wS4,11490
34
34
  uipath/_cli/_evals/_evaluators/__init__.py,sha256=jD7KNLjbsUpsESFXX11eW2MEPXDNuPp2-t-IPB-inlM,734
35
35
  uipath/_cli/_evals/_evaluators/_deterministic_evaluator_base.py,sha256=BTl0puBjp9iCsU3YFfYWqk4TOz4iE19O3q1-dK6qUOI,1723
36
36
  uipath/_cli/_evals/_evaluators/_evaluator_base.py,sha256=knHUwYFt0gMG1uJhq5TGEab6M_YevxX019yT3yYwZsw,3787
37
- uipath/_cli/_evals/_evaluators/_evaluator_factory.py,sha256=RJtCuFREZ8Ijlldpa0521poZLmcR7vTU3WyYOmhJOkc,4688
37
+ uipath/_cli/_evals/_evaluators/_evaluator_factory.py,sha256=cURShn17X6BW-_G3rknJXWtlgpeh5UdioLUV6oGCGAU,4912
38
38
  uipath/_cli/_evals/_evaluators/_exact_match_evaluator.py,sha256=lvEtAitrZy9myoZLMXLqlBWBPX06Msu67kuFMGSbikM,1319
39
39
  uipath/_cli/_evals/_evaluators/_json_similarity_evaluator.py,sha256=HpmkvuwU4Az3IIqFVLUmDvzkqb21pFMxY0sg2biZOMM,7093
40
40
  uipath/_cli/_evals/_evaluators/_llm_as_judge_evaluator.py,sha256=nSLZ29xWqALEI53ifr79JPXjyx0T4sr7p-4NygwgAio,6594
41
41
  uipath/_cli/_evals/_evaluators/_trajectory_evaluator.py,sha256=dnogQTOskpI4_cNF0Ge3hBceJJocvOgxBWAwaCWnzB0,1595
42
42
  uipath/_cli/_evals/_models/__init__.py,sha256=Ewjp3u2YeTH2MmzY9LWf7EIbAoIf_nW9fMYbj7pGlPs,420
43
- uipath/_cli/_evals/_models/_evaluation_set.py,sha256=UIapFwn_Ti9zHUIcL3xyHDcLZ4lq4sHJ3JXLvY5OYI0,1080
43
+ uipath/_cli/_evals/_models/_evaluation_set.py,sha256=tVHykSget-G3sOCs9bSchMYUTpFqzXVlYYbY8L9SI0c,1518
44
44
  uipath/_cli/_evals/_models/_evaluators.py,sha256=l57NEVyYmzSKuoIXuGkE94Br01hAMg35fiS2MlTkaQM,2115
45
- uipath/_cli/_push/sw_file_handler.py,sha256=tRE9n68xv0r20ulwOyALHtYwzbjGneiASwzNm8xtBN0,16372
45
+ uipath/_cli/_push/sw_file_handler.py,sha256=5zAt03gSlni7RnJmZJP4d4RVC33u8lZWj3W9xwx-CFM,18064
46
46
  uipath/_cli/_runtime/_contracts.py,sha256=WlpaiQAMWCo-JFHjee35Klf49A3GsKjOU1Mf2IpUGHY,16033
47
47
  uipath/_cli/_runtime/_escalation.py,sha256=x3vI98qsfRA-fL_tNkRVTFXioM5Gv2w0GFcXJJ5eQtg,7981
48
48
  uipath/_cli/_runtime/_hitl.py,sha256=aexwe0dIXvh6SlVS1jVnO_aGZc6e3gLsmGkCyha5AHo,11300
@@ -62,7 +62,7 @@ uipath/_cli/_utils/_input_args.py,sha256=3LGNqVpJItvof75VGm-ZNTUMUH9-c7-YgleM5b2
62
62
  uipath/_cli/_utils/_parse_ast.py,sha256=8Iohz58s6bYQ7rgWtOTjrEInLJ-ETikmOMZzZdIY2Co,20072
63
63
  uipath/_cli/_utils/_processes.py,sha256=q7DfEKHISDWf3pngci5za_z0Pbnf_shWiYEcTOTCiyk,1855
64
64
  uipath/_cli/_utils/_project_files.py,sha256=MMexSE6BapxitYBl1UUWy_AgkJMdGNFD1t2MkU0hwz4,13115
65
- uipath/_cli/_utils/_studio_project.py,sha256=iVcCm-aps-EQ7Pym_OWGOA48xeDigzcofUxXKy29x6U,13727
65
+ uipath/_cli/_utils/_studio_project.py,sha256=X4X9HbdbGgLQtJRE-h8rcALCrupWkrpqOfSnt9c-hlo,15541
66
66
  uipath/_cli/_utils/_tracing.py,sha256=2igb03j3EHjF_A406UhtCKkPfudVfFPjUq5tXUEG4oo,1541
67
67
  uipath/_cli/_utils/_uv_helpers.py,sha256=6SvoLnZPoKIxW0sjMvD1-ENV_HOXDYzH34GjBqwT138,3450
68
68
  uipath/_services/__init__.py,sha256=10xtw3ENC30yR9CCq_b94RMZ3YrUeyfHV33yWYUd8tU,896
@@ -115,8 +115,8 @@ uipath/tracing/_traced.py,sha256=qeVDrds2OUnpdUIA0RhtF0kg2dlAZhyC1RRkI-qivTM,185
115
115
  uipath/tracing/_utils.py,sha256=ZeensQexnw69jVcsVrGyED7mPlAU-L1agDGm6_1A3oc,10388
116
116
  uipath/utils/__init__.py,sha256=VD-KXFpF_oWexFg6zyiWMkxl2HM4hYJMIUDZ1UEtGx0,105
117
117
  uipath/utils/_endpoints_manager.py,sha256=iRTl5Q0XAm_YgcnMcJOXtj-8052sr6jpWuPNz6CgT0Q,8408
118
- uipath-2.1.20.dist-info/METADATA,sha256=CMJ4e0Xkauc6EZhmTQW6kVrQ3mnhWjZte2atCM4JyyE,6367
119
- uipath-2.1.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
120
- uipath-2.1.20.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
121
- uipath-2.1.20.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
122
- uipath-2.1.20.dist-info/RECORD,,
118
+ uipath-2.1.22.dist-info/METADATA,sha256=PBTmuugOkHNWxAFxeXSWRQwVtz81cv2zUMUU9JldFzI,6367
119
+ uipath-2.1.22.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
120
+ uipath-2.1.22.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
121
+ uipath-2.1.22.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
122
+ uipath-2.1.22.dist-info/RECORD,,