uipath 2.1.88__py3-none-any.whl → 2.1.89__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.

Potentially problematic release.


This version of uipath might be problematic. Click here for more details.

@@ -44,6 +44,15 @@ class LLMMockingStrategy(BaseMockingStrategy):
44
44
  )
45
45
 
46
46
 
47
+ class InputMockingStrategy(BaseModel):
48
+ prompt: str = Field(..., alias="prompt")
49
+ model: Optional[ModelSettings] = Field(None, alias="model")
50
+
51
+ model_config = ConfigDict(
52
+ validate_by_name=True, validate_by_alias=True, extra="allow"
53
+ )
54
+
55
+
47
56
  class MockingArgument(BaseModel):
48
57
  args: List[Any] = Field(default_factory=lambda: [], alias="args")
49
58
  kwargs: Dict[str, Any] = Field(default_factory=lambda: {}, alias="kwargs")
@@ -110,6 +119,10 @@ class EvaluationItem(BaseModel):
110
119
  default=None,
111
120
  alias="mockingStrategy",
112
121
  )
122
+ input_mocking_strategy: Optional[InputMockingStrategy] = Field(
123
+ default=None,
124
+ alias="inputMockingStrategy",
125
+ )
113
126
 
114
127
 
115
128
  class EvaluationSet(BaseModel):
@@ -11,6 +11,10 @@ from opentelemetry import context as context_api
11
11
  from opentelemetry.sdk.trace import ReadableSpan, Span
12
12
  from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
13
13
 
14
+ from uipath._cli._evals.mocks.input_mocker import (
15
+ generate_llm_input,
16
+ )
17
+
14
18
  from ..._events._event_bus import EventBus
15
19
  from ..._events._events import (
16
20
  EvalItemExceptionDetails,
@@ -318,6 +322,10 @@ class UiPathEvalRuntime(UiPathBaseRuntime, Generic[T, C]):
318
322
  evaluators: List[BaseEvaluator[Any]],
319
323
  event_bus: EventBus,
320
324
  ) -> EvaluationRunResult:
325
+ # Generate LLM-based input if input_mocking_strategy is defined
326
+ if eval_item.input_mocking_strategy:
327
+ eval_item = await self._generate_input_for_eval(eval_item)
328
+
321
329
  set_execution_context(eval_item, self.span_collector)
322
330
 
323
331
  await event_bus.publish(
@@ -417,6 +425,16 @@ class UiPathEvalRuntime(UiPathBaseRuntime, Generic[T, C]):
417
425
 
418
426
  return evaluation_run_results
419
427
 
428
+ async def _generate_input_for_eval(
429
+ self, eval_item: EvaluationItem
430
+ ) -> EvaluationItem:
431
+ """Use LLM to generate a mock input for an evaluation item."""
432
+ # TODO(bai): get the input schema from agent definition, once it is available there.
433
+ input_schema: dict[str, Any] = {}
434
+ generated_input = await generate_llm_input(eval_item, input_schema)
435
+ updated_eval_item = eval_item.model_copy(update={"inputs": generated_input})
436
+ return updated_eval_item
437
+
420
438
  def _get_and_clear_execution_data(
421
439
  self, execution_id: str
422
440
  ) -> tuple[List[ReadableSpan], list[logging.LogRecord]]:
@@ -0,0 +1,111 @@
1
+ """LLM Input Mocker implementation."""
2
+
3
+ import json
4
+ from datetime import datetime
5
+ from typing import Any, Dict
6
+
7
+ from uipath import UiPath
8
+ from uipath._cli._evals._models._evaluation_set import EvaluationItem
9
+ from uipath.tracing._traced import traced
10
+
11
+ from .mocker import UiPathInputMockingError
12
+
13
+
14
+ def get_input_mocking_prompt(
15
+ input_schema: str,
16
+ input_generation_instructions: str,
17
+ expected_behavior: str,
18
+ expected_output: str,
19
+ ) -> str:
20
+ """Generate the LLM input mocking prompt."""
21
+ current_datetime = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
22
+
23
+ return f"""You are simulating input for automated testing purposes of an Agent as part of a simulation run.
24
+ You will need to generate realistic input to a LLM agent which will call various tools to achieve a goal. This must be in the exact format of the INPUT_SCHEMA.
25
+ You may need to follow specific INPUT_GENERATION_INSTRUCTIONS. If no relevant instructions are provided pertaining to input generation, use the other provided information and your own judgement to generate input.
26
+ If the INPUT_GENERATION_INSTRUCTIONS are provided, you MUST follow them exactly. For example if the instructions say to generate a value for a field to be before a certain calendar date, you must generate a value that is before that date.
27
+
28
+ The current date and time is: {current_datetime}
29
+
30
+ #INPUT_SCHEMA: You MUST OUTPUT THIS EXACT JSON SCHEMA
31
+ {input_schema}
32
+ #END_INPUT_SCHEMA
33
+
34
+ #INPUT_GENERATION_INSTRUCTIONS
35
+ {input_generation_instructions}
36
+ #END_INPUT_GENERATION_INSTRUCTIONS
37
+
38
+ #EXPECTED_BEHAVIOR
39
+ {expected_behavior}
40
+ #END_EXPECTED_BEHAVIOR
41
+
42
+ #EXPECTED_OUTPUT
43
+ {expected_output}
44
+ #END_EXPECTED_OUTPUT
45
+
46
+ Based on the above information, provide a realistic input to the LLM agent. Your response should:
47
+ 1. Match the expected input format according to the INPUT_SCHEMA exactly
48
+ 2. Be consistent with the style and level of detail in the example inputs
49
+ 3. Consider the context of the the agent being tested
50
+ 4. Be realistic and representative of what a real user might say or ask
51
+
52
+ OUTPUT: ONLY the simulated agent input in the exact format of the INPUT_SCHEMA in valid JSON. Do not include any explanations, quotation marks, or markdown."""
53
+
54
+
55
+ @traced(name="__mocker__")
56
+ async def generate_llm_input(
57
+ evaluation_item: EvaluationItem,
58
+ input_schema: Dict[str, Any],
59
+ ) -> Dict[str, Any]:
60
+ """Generate synthetic input using an LLM based on the evaluation context."""
61
+ try:
62
+ llm = UiPath().llm
63
+
64
+ prompt = get_input_mocking_prompt(
65
+ input_schema=json.dumps(input_schema, indent=2),
66
+ input_generation_instructions=evaluation_item.input_mocking_strategy.prompt
67
+ if evaluation_item.input_mocking_strategy
68
+ else "",
69
+ expected_behavior=evaluation_item.expected_agent_behavior or "",
70
+ expected_output=json.dumps(evaluation_item.expected_output, indent=2)
71
+ if evaluation_item.expected_output
72
+ else "",
73
+ )
74
+
75
+ response_format = {
76
+ "type": "json_schema",
77
+ "json_schema": {
78
+ "name": "agent_input",
79
+ "strict": True,
80
+ "schema": input_schema,
81
+ },
82
+ }
83
+
84
+ model_parameters = (
85
+ evaluation_item.input_mocking_strategy.model
86
+ if evaluation_item.input_mocking_strategy
87
+ else None
88
+ )
89
+ completion_kwargs = (
90
+ model_parameters.model_dump(by_alias=False, exclude_none=True)
91
+ if model_parameters
92
+ else {}
93
+ )
94
+
95
+ response = await llm.chat_completions(
96
+ [{"role": "user", "content": prompt}],
97
+ response_format=response_format,
98
+ **completion_kwargs,
99
+ )
100
+
101
+ generated_input_str = response.choices[0].message.content
102
+
103
+ return json.loads(generated_input_str)
104
+ except json.JSONDecodeError as e:
105
+ raise UiPathInputMockingError(
106
+ f"Failed to parse LLM response as JSON: {str(e)}"
107
+ ) from e
108
+ except UiPathInputMockingError:
109
+ raise
110
+ except Exception as e:
111
+ raise UiPathInputMockingError(f"Failed to generate input: {str(e)}") from e
@@ -33,3 +33,9 @@ class UiPathMockResponseGenerationError(Exception):
33
33
  """Exception when a mocker is configured unable to generate a response."""
34
34
 
35
35
  pass
36
+
37
+
38
+ class UiPathInputMockingError(Exception):
39
+ """Exception when input mocking fails."""
40
+
41
+ pass
@@ -1,14 +1,12 @@
1
1
  """Studio Web File Handler for managing file operations in UiPath projects."""
2
2
 
3
3
  import json
4
+ import logging
4
5
  import os
5
6
  from datetime import datetime, timezone
6
7
  from typing import Any, Dict, Optional, Set
7
8
 
8
- import click
9
-
10
9
  from ...models.exceptions import EnrichedException
11
- from .._utils._console import ConsoleLogger
12
10
  from .._utils._constants import (
13
11
  AGENT_INITIAL_CODE_VERSION,
14
12
  AGENT_STORAGE_VERSION,
@@ -30,6 +28,8 @@ from .._utils._studio_project import (
30
28
  StudioClient,
31
29
  )
32
30
 
31
+ logger = logging.getLogger(__name__)
32
+
33
33
 
34
34
  class SwFileHandler:
35
35
  """Handler for Studio Web file operations.
@@ -58,7 +58,6 @@ class SwFileHandler:
58
58
  """
59
59
  self.directory = directory
60
60
  self.include_uv_lock = include_uv_lock
61
- self.console = ConsoleLogger()
62
61
  self._studio_client = StudioClient(project_id)
63
62
  self._project_structure: Optional[ProjectStructure] = None
64
63
 
@@ -156,9 +155,7 @@ class SwFileHandler:
156
155
 
157
156
  for local_file in local_files:
158
157
  if not os.path.exists(local_file.file_path):
159
- self.console.warning(
160
- f"File not found: {click.style(local_file.file_path, fg='cyan')}"
161
- )
158
+ logger.info(f"File not found: '{local_file.file_path}'")
162
159
  continue
163
160
 
164
161
  # Skip agent.json as it's handled separately
@@ -175,9 +172,7 @@ class SwFileHandler:
175
172
  id=remote_file.id, content_file_path=local_file.file_path
176
173
  )
177
174
  )
178
- self.console.info(
179
- f"Updating {click.style(local_file.file_name, fg='yellow')}"
180
- )
175
+ logger.info(f"Updating '{local_file.file_name}'")
181
176
  else:
182
177
  parent_path = os.path.dirname(local_file.relative_path)
183
178
  structural_migration.added_resources.append(
@@ -188,9 +183,7 @@ class SwFileHandler:
188
183
  else "source_code",
189
184
  )
190
185
  )
191
- self.console.info(
192
- f"Uploading {click.style(local_file.relative_path, fg='cyan')}"
193
- )
186
+ logger.info(f"Uploading '{local_file.relative_path}'")
194
187
 
195
188
  # identify and add deleted files
196
189
  structural_migration.deleted_resources.extend(
@@ -236,9 +229,7 @@ class SwFileHandler:
236
229
  for _, remote_file in source_code_files.items():
237
230
  if remote_file.id not in processed_source_file_paths:
238
231
  deleted_files.add(remote_file.id)
239
- self.console.info(
240
- f"Deleting {click.style(remote_file.name, fg='bright_red')}"
241
- )
232
+ logger.info(f"Deleting '{remote_file.name}'")
242
233
 
243
234
  return deleted_files
244
235
 
@@ -263,16 +254,14 @@ class SwFileHandler:
263
254
  for folder_info in empty_folder_ids:
264
255
  try:
265
256
  await self._studio_client.delete_item_async(folder_info["id"])
266
- self.console.info(
267
- f"Deleted empty folder {click.style(folder_info['name'], fg='bright_red')}"
268
- )
257
+ logger.info(f"Deleted empty folder '{folder_info['name']}'")
269
258
  except Exception as e:
270
- self.console.warning(
271
- f"Failed to delete empty folder {folder_info['name']}: {str(e)}"
259
+ logger.warning(
260
+ f"Failed to delete empty folder '{folder_info['name']}': {str(e)}"
272
261
  )
273
262
 
274
263
  except Exception as e:
275
- self.console.warning(f"Failed to cleanup empty folders: {str(e)}")
264
+ logger.warning(f"Failed to cleanup empty folders: {str(e)}")
276
265
 
277
266
  def _collect_empty_folders(self, folder: ProjectFolder) -> list[dict[str, str]]:
278
267
  """Recursively collect IDs and names of empty folders.
@@ -335,8 +324,8 @@ class SwFileHandler:
335
324
  entry_points_json["entryPoints"] = uipath_config["entryPoints"]
336
325
 
337
326
  except Exception:
338
- self.console.warning(
339
- "Could not parse existing entry-points.json file, using default version"
327
+ logger.info(
328
+ "Could not parse existing 'entry-points.json' file, using default version"
340
329
  )
341
330
  structural_migration.modified_resources.append(
342
331
  ModifiedResource(
@@ -344,12 +333,10 @@ class SwFileHandler:
344
333
  content_string=json.dumps(entry_points_json),
345
334
  )
346
335
  )
347
- self.console.info(
348
- f"Updating {click.style('entry-points.json', fg='yellow')}"
349
- )
336
+ logger.info("Updating 'entry-points.json'")
350
337
 
351
338
  else:
352
- self.console.warning(
339
+ logger.info(
353
340
  "'entry-points.json' file does not exist in Studio Web project, initializing using default version"
354
341
  )
355
342
  entry_points_json = {
@@ -363,9 +350,7 @@ class SwFileHandler:
363
350
  content_string=json.dumps(entry_points_json),
364
351
  )
365
352
  )
366
- self.console.info(
367
- f"Uploading {click.style('entry-points.json', fg='cyan')}"
368
- )
353
+ logger.info("Uploading 'entry-points.json'")
369
354
 
370
355
  async def _prepare_agent_json_migration(
371
356
  self,
@@ -400,9 +385,10 @@ class SwFileHandler:
400
385
  input_schema = uipath_config["entryPoints"][0]["input"]
401
386
  output_schema = uipath_config["entryPoints"][0]["output"]
402
387
  except (FileNotFoundError, KeyError) as e:
403
- self.console.error(
388
+ logger.error(
404
389
  f"Unable to extract entrypoints from configuration file. Please run 'uipath init' : {str(e)}",
405
390
  )
391
+ return
406
392
 
407
393
  author = get_author_from_token_or_toml()
408
394
 
@@ -444,8 +430,8 @@ class SwFileHandler:
444
430
  AGENT_INITIAL_CODE_VERSION[:-1] + "1"
445
431
  )
446
432
  except Exception:
447
- self.console.warning(
448
- "Could not parse existing agent.json file, using default version"
433
+ logger.info(
434
+ "Could not parse existing 'agent.json' file, using default version"
449
435
  )
450
436
 
451
437
  structural_migration.modified_resources.append(
@@ -454,9 +440,9 @@ class SwFileHandler:
454
440
  content_string=json.dumps(agent_json),
455
441
  )
456
442
  )
457
- self.console.info(f"Updating {click.style('agent.json', fg='yellow')}")
443
+ logger.info("Updating 'agent.json'")
458
444
  else:
459
- self.console.warning(
445
+ logger.info(
460
446
  "'agent.json' file does not exist in Studio Web project, initializing using default version"
461
447
  )
462
448
  structural_migration.added_resources.append(
@@ -465,7 +451,7 @@ class SwFileHandler:
465
451
  content_string=json.dumps(agent_json),
466
452
  )
467
453
  )
468
- self.console.info(f"Uploading {click.style('agent.json', fg='cyan')}")
454
+ logger.info("Uploading 'agent.json'")
469
455
 
470
456
  async def upload_source_files(self, config_data: dict[str, Any]) -> None:
471
457
  """Main method to upload source files to the UiPath project.
@@ -502,9 +488,7 @@ class SwFileHandler:
502
488
  if not source_code_folder:
503
489
  await self._studio_client.create_folder_async("source_code")
504
490
 
505
- self.console.success(
506
- f"Created {click.style('source_code', fg='cyan')} folder"
507
- )
491
+ logger.info("Created 'source_code' folder.")
508
492
  source_code_files = {}
509
493
 
510
494
  # Get files to upload and process them
@@ -266,6 +266,10 @@ class LogsInterceptor:
266
266
  def writable(self) -> bool:
267
267
  return True
268
268
 
269
+ def __getattr__(self, name):
270
+ # Delegate any unknown attributes to the original file
271
+ return getattr(self.sys_file, name)
272
+
269
273
  # Set up stdout and stderr loggers
270
274
  stdout_logger = logging.getLogger("stdout")
271
275
  stderr_logger = logging.getLogger("stderr")
@@ -1,12 +1,12 @@
1
1
  # type: ignore
2
2
  import hashlib
3
3
  import json
4
+ import logging
4
5
  import os
5
6
  import re
6
7
  from pathlib import Path
7
- from typing import Any, Dict, Optional, Tuple
8
+ from typing import Any, Dict, Optional, Protocol, Tuple
8
9
 
9
- import click
10
10
  from pydantic import BaseModel
11
11
 
12
12
  from .._utils._console import ConsoleLogger
@@ -22,6 +22,35 @@ try:
22
22
  import tomllib
23
23
  except ImportError:
24
24
  import tomli as tomllib
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class FileConflictHandler(Protocol):
29
+ """Protocol for handling file conflicts."""
30
+
31
+ def should_overwrite(
32
+ self, file_path: str, local_hash: str, remote_hash: str
33
+ ) -> bool:
34
+ """Return True to overwrite, False to skip."""
35
+ ...
36
+
37
+
38
+ class AlwaysOverwriteHandler:
39
+ """Handler that always overwrites files."""
40
+
41
+ def should_overwrite(
42
+ self, file_path: str, local_hash: str, remote_hash: str
43
+ ) -> bool:
44
+ return True
45
+
46
+
47
+ class AlwaysSkipHandler:
48
+ """Handler that always skips conflicts."""
49
+
50
+ def should_overwrite(
51
+ self, file_path: str, local_hash: str, remote_hash: str
52
+ ) -> bool:
53
+ return False
25
54
 
26
55
 
27
56
  class FileInfo(BaseModel):
@@ -483,10 +512,37 @@ def collect_files_from_folder(
483
512
  collect_files_from_folder(subfolder, subfolder_path, files_dict)
484
513
 
485
514
 
515
+ async def pull_project(
516
+ project_id: str,
517
+ download_configuration: dict[str, Path],
518
+ conflict_handler: Optional[FileConflictHandler] = None,
519
+ ):
520
+ """Pull project with configurable conflict handling."""
521
+ if conflict_handler is None:
522
+ conflict_handler = AlwaysOverwriteHandler()
523
+
524
+ studio_client = StudioClient(project_id)
525
+
526
+ try:
527
+ structure = await studio_client.get_project_structure_async()
528
+ for source_key, destination in download_configuration.items():
529
+ source_folder = get_folder_by_name(structure, source_key)
530
+ if source_folder:
531
+ await download_folder_files(
532
+ studio_client, source_folder, destination, conflict_handler
533
+ )
534
+ else:
535
+ logger.warning(f"No '{source_key}' folder found in remote project")
536
+ except Exception:
537
+ logger.exception("Failed to pull UiPath project")
538
+ raise
539
+
540
+
486
541
  async def download_folder_files(
487
542
  studio_client: StudioClient,
488
543
  folder: ProjectFolder,
489
544
  base_path: Path,
545
+ conflict_handler: FileConflictHandler,
490
546
  ) -> None:
491
547
  """Download files from a folder recursively.
492
548
 
@@ -494,62 +550,36 @@ async def download_folder_files(
494
550
  studio_client: Studio client
495
551
  folder: The folder to download files from
496
552
  base_path: Base path for local file storage
553
+ conflict_handler: Handler for file conflicts
497
554
  """
498
555
  files_dict: Dict[str, ProjectFile] = {}
499
556
  collect_files_from_folder(folder, "", files_dict)
557
+
500
558
  for file_path, remote_file in files_dict.items():
501
559
  local_path = base_path / file_path
502
560
  local_path.parent.mkdir(parents=True, exist_ok=True)
503
561
 
504
- # Download remote file
505
562
  response = await studio_client.download_file_async(remote_file.id)
506
563
  remote_content = response.read().decode("utf-8")
507
564
  remote_hash = compute_normalized_hash(remote_content)
508
565
 
509
566
  if os.path.exists(local_path):
510
- # Read and hash local file
511
567
  with open(local_path, "r", encoding="utf-8") as f:
512
568
  local_content = f.read()
513
569
  local_hash = compute_normalized_hash(local_content)
514
570
 
515
- # Compare hashes
516
571
  if local_hash != remote_hash:
517
- styled_path = click.style(str(file_path), fg="cyan")
518
- console.warning(f"File {styled_path}" + " differs from remote version.")
519
- response = click.prompt("Do you want to overwrite it? (y/n)", type=str)
520
- if response.lower() == "y":
572
+ if conflict_handler.should_overwrite(
573
+ file_path, local_hash, remote_hash
574
+ ):
521
575
  with open(local_path, "w", encoding="utf-8", newline="\n") as f:
522
576
  f.write(remote_content)
523
- console.success(f"Updated {click.style(str(file_path), fg='cyan')}")
577
+ logger.info(f"Updated '{file_path}'")
524
578
  else:
525
- console.info(f"Skipped {click.style(str(file_path), fg='cyan')}")
579
+ logger.info(f"Skipped '{file_path}'")
526
580
  else:
527
- console.info(
528
- f"File {click.style(str(file_path), fg='cyan')} is up to date"
529
- )
581
+ logger.info(f"File '{file_path}' is up to date")
530
582
  else:
531
- # File doesn't exist locally, create it
532
583
  with open(local_path, "w", encoding="utf-8", newline="\n") as f:
533
584
  f.write(remote_content)
534
- console.success(f"Downloaded {click.style(str(file_path), fg='cyan')}")
535
-
536
-
537
- async def pull_project(project_id: str, download_configuration: dict[str, Path]):
538
- studio_client = StudioClient(project_id)
539
-
540
- with console.spinner("Pulling UiPath project files..."):
541
- try:
542
- structure = await studio_client.get_project_structure_async()
543
- for source_key, destination in download_configuration.items():
544
- source_folder = get_folder_by_name(structure, source_key)
545
- if source_folder:
546
- await download_folder_files(
547
- studio_client,
548
- source_folder,
549
- destination,
550
- )
551
- else:
552
- console.warning(f"No {source_key} folder found in remote project")
553
-
554
- except Exception as e:
555
- console.error(f"Failed to pull UiPath project: {str(e)}")
585
+ logger.info(f"Downloaded '{file_path}'")
uipath/_cli/cli_pull.py CHANGED
@@ -24,6 +24,20 @@ from ._utils._project_files import pull_project
24
24
  console = ConsoleLogger()
25
25
 
26
26
 
27
+ class InteractiveConflictHandler:
28
+ """Handler that prompts user for each conflict."""
29
+
30
+ def __init__(self, console: ConsoleLogger):
31
+ self.console = console
32
+
33
+ def should_overwrite(
34
+ self, file_path: str, local_hash: str, remote_hash: str
35
+ ) -> bool:
36
+ self.console.warning(f" File {file_path} differs from remote version.")
37
+ response = click.confirm("Do you want to overwrite it?", default=False)
38
+ return response
39
+
40
+
27
41
  @click.command()
28
42
  @click.argument(
29
43
  "root",
@@ -56,4 +70,11 @@ def pull(root: Path) -> None:
56
70
  "source_code": root,
57
71
  "evals": root / "evals",
58
72
  }
59
- asyncio.run(pull_project(project_id, default_download_configuration))
73
+ with console.spinner("Pulling UiPath project files..."):
74
+ asyncio.run(
75
+ pull_project(
76
+ project_id,
77
+ default_download_configuration,
78
+ InteractiveConflictHandler(console),
79
+ )
80
+ )
uipath/agent/_utils.py CHANGED
@@ -4,7 +4,10 @@ from pathlib import PurePath
4
4
  from httpx import Response
5
5
  from pydantic import TypeAdapter
6
6
 
7
- from uipath._cli._evals._models._evaluation_set import LLMMockingStrategy
7
+ from uipath._cli._evals._models._evaluation_set import (
8
+ InputMockingStrategy,
9
+ LLMMockingStrategy,
10
+ )
8
11
  from uipath._cli._push.sw_file_handler import SwFileHandler
9
12
  from uipath._cli._utils._studio_project import (
10
13
  ProjectFile,
@@ -137,4 +140,14 @@ async def load_agent_definition(project_id: str) -> AgentDefinition:
137
140
  evaluation.mocking_strategy = LLMMockingStrategy(
138
141
  prompt=prompt, tools_to_simulate=tools_to_simulate
139
142
  )
143
+
144
+ if not evaluation.input_mocking_strategy:
145
+ # Migrate lowCode input mocking fields
146
+ if evaluation.model_extra.get("simulateInput", False):
147
+ prompt = evaluation.model_extra.get(
148
+ "inputGenerationInstructions",
149
+ )
150
+ evaluation.input_mocking_strategy = InputMockingStrategy(
151
+ prompt=prompt
152
+ )
140
153
  return agent_definition
@@ -0,0 +1,17 @@
1
+ """LowCode Agent Loop Constructs.
2
+
3
+ This module includes agentic loop constructs specific to LowCode Agent
4
+ such as prompts, tools
5
+ """
6
+
7
+ from uipath.agent.loop.prompts import AGENT_SYSTEM_PROMPT_TEMPLATE
8
+ from uipath.agent.loop.tools import (
9
+ EndExecutionToolSchemaModel,
10
+ RaiseErrorToolSchemaModel,
11
+ )
12
+
13
+ __all__ = [
14
+ "AGENT_SYSTEM_PROMPT_TEMPLATE",
15
+ "EndExecutionToolSchemaModel",
16
+ "RaiseErrorToolSchemaModel",
17
+ ]
@@ -0,0 +1,57 @@
1
+ """LowCode Agent Prompts."""
2
+
3
+ AGENT_SYSTEM_PROMPT_TEMPLATE = """
4
+ You are an advanced automatic agent equipped with a variety of tools to assist users.
5
+ Your primary function is to understand the user goal and utilize the appropriate tools at your disposal to fulfill it.
6
+ The current date is: {{currentDate}}
7
+ Your name is: {{agentName}}
8
+
9
+ {{systemPrompt}}
10
+
11
+ Your adhere strictly to the following rules to ensure accuracy and data validity:
12
+
13
+ <rules>
14
+ Data Verification and Tool Analysis:
15
+ - **Tool Inspection:** ALWAYS examine tool definitions thoroughly for any pre-configured or hardcoded parameter values before requesting information from the user.
16
+ - **Pre-configured Parameter Usage:** If a parameter is pre-configured or hardcoded, use it directly without asking the user for it.
17
+ - Specificity: Ensure **all information used as tool arguments is concrete and specific**. Utilize values provided in tool definitions when available.
18
+ - Complete tasks accurately or clearly state why it cannot be done. Never proceed with incomplete or invalid information in tool arguments.
19
+
20
+ Tool Usage:
21
+ - **Parameter Resolution:** First check tool definitions for any pre-configured values, then use available context, and only then request missing information from the user.
22
+ - **Preconditions:** Use a tool only when all required parameters have verified, specific data.
23
+ - **Avoid Incomplete Calls:** Do not use tools if any parameter lacks specific data or would require placeholders.
24
+
25
+ Handling Missing Information:
26
+ - **End Execution:** If specific data is missing and no tool is available to obtain it, terminate the process using `end_execution` with a clear reason.
27
+ - **No Placeholder Use:** Do not attempt to use tools with incomplete or placeholder information.
28
+
29
+ Execution Steps:
30
+ - **Step-by-Step Approach:** Break down user requests into required steps and gather necessary information sequentially.
31
+ - **Verification:** Explicitly verify each piece of required information for specificity and validity before proceeding.
32
+ - **Reasoning:** Begin by explaining your reasoning in plain text for each tool call.
33
+ - **Tool Calls:** You can invoke tools, following a logical order where dependent actions occur after their prerequisites.
34
+ - **End execution:** When you have no more steps to perform or you reached a point where you cannot proceed, use the end_execution tool to end the execution.
35
+ </rules>
36
+
37
+ <examples>
38
+ **Example 1: Valid Data Available**
39
+ User Request: "Schedule a meeting with Alice at 10 AM on October 9th 2023."
40
+ Your Response: I will schedule a meeting with Alice at 10 AM on October 9th 2023.
41
+ Tool: schedule_meeting
42
+ Payload: { contact: "Alice", time: "2023-10-09T10:00:00" }
43
+
44
+ **Example 2: Missing Data Without Retrieval Tool:**
45
+ User Request: "Book a flight to Paris."
46
+ Your Response: Cannot proceed because the departure location and date are missing, and no tool is available to obtain this information.
47
+ Tool: raise_error
48
+ Payload: { message: "Missing departure location and date.", details: "Cannot proceed because the departure location and date are missing and no tool is available to obtain this information"}
49
+
50
+ **Example 3: Pre-configured Parameter Usage:**
51
+ User Request: "Retrieve specific details."
52
+ Your Response: I will retrieve the details using the pre-configured parameter value.
53
+ Tool: example_tool
54
+ Payload: { parameterName: "preConfiguredValue" }
55
+
56
+ </examples>
57
+ """
@@ -0,0 +1,42 @@
1
+ """LowCode Agent Tools."""
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+ TOOL_FLOW_CONTROL_END_EXECUTION = "end_execution"
6
+ TOOL_FLOW_CONTROL_RAISE_ERROR = "raise_error"
7
+
8
+
9
+ class EndExecutionToolSchemaModel(BaseModel):
10
+ """Arguments schema accepted by the `end_execution` control flow tool."""
11
+
12
+ success: bool = Field(
13
+ ...,
14
+ description="Whether the execution was successful",
15
+ )
16
+ message: str | None = Field(
17
+ None,
18
+ description="The message to return to the user if the execution was successful",
19
+ )
20
+ error: str | None = Field(
21
+ None,
22
+ description="The error message to return to the user if the execution was unsuccessful",
23
+ )
24
+
25
+ model_config = ConfigDict(extra="forbid")
26
+
27
+
28
+ class RaiseErrorToolSchemaModel(BaseModel):
29
+ """Arguments schema accepted by the `raise_error` control flow tool."""
30
+
31
+ message: str = Field(
32
+ ...,
33
+ description="The error message to display to the user. This should be a brief one line message.",
34
+ )
35
+ details: str | None = Field(
36
+ None,
37
+ description=(
38
+ "Optional additional details about the error. This can be a multiline text with more details. Only populate this if there are relevant details not already captured in the error message."
39
+ ),
40
+ )
41
+
42
+ model_config = ConfigDict(extra="forbid")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath
3
- Version: 2.1.88
3
+ Version: 2.1.89
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=pLUzS4wrSsTzO69wvJa6C-l9qTomySc15GOwliPPyHU,1785
18
+ uipath/_cli/cli_pull.py,sha256=muX2gR-W-bSrNI3pIO0zbhZAP0VBOSsOY2-yrq8HgAw,2433
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=0D9a-wphyetnH9T97F08o7-1OKWF1lMweFHHAR0xiOw,4979
@@ -46,9 +46,9 @@ uipath/_cli/_dev/_terminal/_utils/_logger.py,sha256=_ipTl_oAiMF9I7keGt2AAFAMz40D
46
46
  uipath/_cli/_evals/_console_progress_reporter.py,sha256=HgB6pdMyoS6YVwuI3EpM2LBcH3U69nrdaTyNgPG8ssg,9304
47
47
  uipath/_cli/_evals/_evaluator_factory.py,sha256=Gycv94VtGOpMir_Gba-UoiAyrSRfbSfe8_pTfjzcA9Q,3875
48
48
  uipath/_cli/_evals/_progress_reporter.py,sha256=kX7rNSa-QCLXIzK-vb9Jjf-XLEtucdeiQPgPlSkpp2U,16778
49
- uipath/_cli/_evals/_runtime.py,sha256=7ePUvkzaYC_sQUNmDgk1IqxvPpicjRlucQmSZkxHzc0,19834
49
+ uipath/_cli/_evals/_runtime.py,sha256=eK32s4yWOFsiwYBbe5bml02oNarKT6q7bkSalTuoodY,20608
50
50
  uipath/_cli/_evals/_span_collection.py,sha256=RoKoeDFG2XODdlgI27ionCjU7LLD_C0LJJ3gu0wab10,779
51
- uipath/_cli/_evals/_models/_evaluation_set.py,sha256=TEinpTAIzy5JLkF7-JrG_623ec2Y-GN9pfz284KKL_8,4567
51
+ uipath/_cli/_evals/_models/_evaluation_set.py,sha256=0I83I3HNu_BrgFz9miar2QTyXKb0dAaEpYMQrKUHP0U,4958
52
52
  uipath/_cli/_evals/_models/_evaluator.py,sha256=fuC3UOYwPD4d_wdynHeLSCzbu82golNAnnPnxC8Y4rk,3315
53
53
  uipath/_cli/_evals/_models/_evaluator_base_params.py,sha256=lTYKOV66tcjW85KHTyOdtF1p1VDaBNemrMAvH8bFIFc,382
54
54
  uipath/_cli/_evals/_models/_exceptions.py,sha256=-oXLTDa4ab9Boa34ZxuUrCezf8ajIGrIEUVwZnmBASE,195
@@ -56,16 +56,17 @@ uipath/_cli/_evals/_models/_mocks.py,sha256=mlD9qvdZNniuKxzY_ttJtwLVFvKGvvIukYvy
56
56
  uipath/_cli/_evals/_models/_output.py,sha256=4KPgXypO2qvWsDf37KbdMGmlYgaIfBSeGgKL32J2nP0,3157
57
57
  uipath/_cli/_evals/_models/_sw_reporting.py,sha256=tSBLQFAdOIun8eP0vsqt56K6bmCZz_uMaWI3hskg_24,536
58
58
  uipath/_cli/_evals/mocks/__init__.py,sha256=2WXwAy_oZw5bKp6L0HB13QygCJeftOB_Bget0AI6Gik,32
59
+ uipath/_cli/_evals/mocks/input_mocker.py,sha256=imJLriJv1rFthoA-SnJewnNbN27yen8VP11vbv1I9-0,4150
59
60
  uipath/_cli/_evals/mocks/llm_mocker.py,sha256=VrjzkeOVAJNKfkrHntQ17MBEtHZhwszEzc_uuPH3Tbc,7355
60
- uipath/_cli/_evals/mocks/mocker.py,sha256=p9UpJDIckvCgkO0qqJHWdMMSbySBOoa8xpEIi2QIbhA,810
61
+ uipath/_cli/_evals/mocks/mocker.py,sha256=VXCxuRAPqUK40kRUXpPmXZRckd7GrOY5ZzIYDe-NNfo,910
61
62
  uipath/_cli/_evals/mocks/mocker_factory.py,sha256=V5QKSTtQxztTo4-fK1TyAaXw2Z3mHf2UC5mXqwuUGTs,811
62
63
  uipath/_cli/_evals/mocks/mockito_mocker.py,sha256=AO2BmFwA6hz3Lte-STVr7aJDPvMCqKNKa4j2jeNZ_U4,2677
63
64
  uipath/_cli/_evals/mocks/mocks.py,sha256=jfenoCSnMPpaXEyAcRDVu5jIfb62eRredRenZDI_AzE,1965
64
- uipath/_cli/_push/sw_file_handler.py,sha256=ku__shuywCvQfL6_1c1nM-40q-rkQAJsP6-rOlzyTUw,18566
65
+ uipath/_cli/_push/sw_file_handler.py,sha256=gySENzKMcIDtE4u_MrXDK0g-h9wRz6jaJJht1LW54Nc,17877
65
66
  uipath/_cli/_runtime/_contracts.py,sha256=E8Is7EQfAu7_hCbeZI68gmTxSxo4X7_U4vcSl7D3Syg,28988
66
67
  uipath/_cli/_runtime/_escalation.py,sha256=x3vI98qsfRA-fL_tNkRVTFXioM5Gv2w0GFcXJJ5eQtg,7981
67
68
  uipath/_cli/_runtime/_hitl.py,sha256=VKbM021nVg1HEDnTfucSLJ0LsDn83CKyUtVzofS2qTU,11369
68
- uipath/_cli/_runtime/_logging.py,sha256=jwBfsy0Hi4zkfPH-v9dQ7m5dcJeuE0j_OxdpI-DhHaw,13854
69
+ uipath/_cli/_runtime/_logging.py,sha256=srjAi3Cy6g7b8WNHiYNjaZT4t40F3XRqquuoGd2kh4Y,14019
69
70
  uipath/_cli/_runtime/_runtime.py,sha256=P77YtxylLzpK2yz4S0tsBTVFpBSoh5lA7Gpu0SFRD5w,2379
70
71
  uipath/_cli/_runtime/_script_executor.py,sha256=PjbmEbyCMofGH2F85b8RFsxdV3Tqw0kVqdWOOk2ZLlI,9687
71
72
  uipath/_cli/_templates/.psmdcp.template,sha256=C7pBJPt98ovEljcBvGtEUGoWjjQhu9jls1bpYjeLOKA,611
@@ -82,7 +83,7 @@ uipath/_cli/_utils/_folders.py,sha256=RsYrXzF0NA1sPxgBoLkLlUY3jDNLg1V-Y8j71Q8a8H
82
83
  uipath/_cli/_utils/_input_args.py,sha256=AnbQ12D2ACIQFt0QHMaWleRn1ZgRTXuTSTN0ozJiSQg,5766
83
84
  uipath/_cli/_utils/_parse_ast.py,sha256=8Iohz58s6bYQ7rgWtOTjrEInLJ-ETikmOMZzZdIY2Co,20072
84
85
  uipath/_cli/_utils/_processes.py,sha256=q7DfEKHISDWf3pngci5za_z0Pbnf_shWiYEcTOTCiyk,1855
85
- uipath/_cli/_utils/_project_files.py,sha256=1DQ0dY1oUyCP_y7i1PbqJv_JcDQmHzzsJy1bKcr3xYk,19671
86
+ uipath/_cli/_utils/_project_files.py,sha256=ueDUGQrig5EFSgEXUiaXmMGAgs1b_tX2yBpgaXsHcrA,20093
86
87
  uipath/_cli/_utils/_studio_project.py,sha256=8WYwi_CiTPRqo8KV2bsvj0H_KBFxTEN0Q2cXoZb-NnM,17030
87
88
  uipath/_cli/_utils/_tracing.py,sha256=2igb03j3EHjF_A406UhtCKkPfudVfFPjUq5tXUEG4oo,1541
88
89
  uipath/_cli/_utils/_uv_helpers.py,sha256=6SvoLnZPoKIxW0sjMvD1-ENV_HOXDYzH34GjBqwT138,3450
@@ -123,7 +124,7 @@ uipath/_utils/_ssl_context.py,sha256=xSYitos0eJc9cPHzNtHISX9PBvL6D2vas5G_GiBdLp8
123
124
  uipath/_utils/_url.py,sha256=-4eluSrIZCUlnQ3qU17WPJkgaC2KwF9W5NeqGnTNGGo,2512
124
125
  uipath/_utils/_user_agent.py,sha256=pVJkFYacGwaQBomfwWVAvBQgdBUo62e4n3-fLIajWUU,563
125
126
  uipath/_utils/constants.py,sha256=2xLT-1aW0aJS2USeZbK-7zRgyyi1bgV60L0rtQOUqOM,1721
126
- uipath/agent/_utils.py,sha256=jd6AETgrGnY2elzqNLs1JVgcedoc9H046AREmznihk8,5000
127
+ uipath/agent/_utils.py,sha256=ec0FJV95pujbMaa0_Vz1nX1fAHp64nJKbf4tYbQn-aI,5560
127
128
  uipath/agent/conversation/__init__.py,sha256=5hK-Iz131mnd9m6ANnpZZffxXZLVFDQ9GTg5z9ik1oQ,5265
128
129
  uipath/agent/conversation/async_stream.py,sha256=BA_8uU1DgE3VpU2KkJj0rkI3bAHLk_ZJKsajR0ipMpo,2055
129
130
  uipath/agent/conversation/citation.py,sha256=42dGv-wiYx3Lt7MPuPCFTkjAlSADFSzjyNXuZHdxqvo,2253
@@ -134,6 +135,9 @@ uipath/agent/conversation/exchange.py,sha256=nuk1tEMBHc_skrraT17d8U6AtyJ3h07ExGQ
134
135
  uipath/agent/conversation/message.py,sha256=1ZkEs146s79TrOAWCQwzBAEJvjAu4lQBpJ64tKXDgGE,2142
135
136
  uipath/agent/conversation/meta.py,sha256=3t0eS9UHoAPHre97QTUeVbjDhnMX4zj4-qG6ju0B8wY,315
136
137
  uipath/agent/conversation/tool.py,sha256=ol8XI8AVd-QNn5auXNBPcCzOkh9PPFtL7hTK3kqInkU,2191
138
+ uipath/agent/loop/__init__.py,sha256=EZlNqrh8xod0VtTJqc-1pyUofaeS6PKjbsDfWoveVtI,424
139
+ uipath/agent/loop/prompts.py,sha256=qPwyd6Ple2m-Kt0m2foa6ZEmxD3kyCXOmGfXvzHi8Rc,3372
140
+ uipath/agent/loop/tools.py,sha256=OsT3x431KqOEzLi2jLxy2B9xXbcFq16xMfmyAqFaqZQ,1351
137
141
  uipath/agent/models/agent.py,sha256=18NvqLtuQoEHYnrHiEApWjaka91PYCj_fJRFE72z4Uw,13236
138
142
  uipath/eval/_helpers/__init__.py,sha256=GSmZMryjuO3Wo_zdxZdrHCRRsgOxsVFYkYgJ15YNC3E,86
139
143
  uipath/eval/_helpers/helpers.py,sha256=iE2HHdMiAdAMLqxHkPKHpfecEtAuN5BTBqvKFTI8ciE,1315
@@ -177,8 +181,8 @@ uipath/tracing/_utils.py,sha256=X-LFsyIxDeNOGuHPvkb6T5o9Y8ElYhr_rP3CEBJSu4s,1383
177
181
  uipath/utils/__init__.py,sha256=VD-KXFpF_oWexFg6zyiWMkxl2HM4hYJMIUDZ1UEtGx0,105
178
182
  uipath/utils/_endpoints_manager.py,sha256=iRTl5Q0XAm_YgcnMcJOXtj-8052sr6jpWuPNz6CgT0Q,8408
179
183
  uipath/utils/dynamic_schema.py,sha256=w0u_54MoeIAB-mf3GmwX1A_X8_HDrRy6p998PvX9evY,3839
180
- uipath-2.1.88.dist-info/METADATA,sha256=JX0yF1aAsV4X2lENrcJ-Qa8I7QmghlGa-vNk57RerKc,6593
181
- uipath-2.1.88.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
182
- uipath-2.1.88.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
183
- uipath-2.1.88.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
184
- uipath-2.1.88.dist-info/RECORD,,
184
+ uipath-2.1.89.dist-info/METADATA,sha256=n4C3L0fJspBl4pbt_G7qvKkfD_0hdk1AstgioRyE8zg,6593
185
+ uipath-2.1.89.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
186
+ uipath-2.1.89.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
187
+ uipath-2.1.89.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
188
+ uipath-2.1.89.dist-info/RECORD,,