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

uipath/_cli/cli_pull.py CHANGED
@@ -19,7 +19,7 @@ import click
19
19
  from ..telemetry import track
20
20
  from ._utils._console import ConsoleLogger
21
21
  from ._utils._constants import UIPATH_PROJECT_ID
22
- from ._utils._project_files import pull_project
22
+ from ._utils._project_files import ProjectPullError, pull_project
23
23
 
24
24
  console = ConsoleLogger()
25
25
 
@@ -63,18 +63,25 @@ def pull(root: Path) -> None:
63
63
  $ uipath pull
64
64
  $ uipath pull /path/to/project
65
65
  """
66
- if not (project_id := os.getenv(UIPATH_PROJECT_ID, False)):
66
+ project_id = os.getenv(UIPATH_PROJECT_ID)
67
+ if not project_id:
67
68
  console.error("UIPATH_PROJECT_ID environment variable not found.")
68
69
 
69
70
  default_download_configuration = {
70
71
  "source_code": root,
71
72
  "evals": root / "evals",
72
73
  }
73
- with console.spinner("Pulling UiPath project files..."):
74
- asyncio.run(
75
- pull_project(
74
+
75
+ async def pull_with_updates():
76
+ try:
77
+ async for update in pull_project(
76
78
  project_id,
77
79
  default_download_configuration,
78
80
  InteractiveConflictHandler(console),
79
- )
80
- )
81
+ ):
82
+ console.info(update.message)
83
+ except ProjectPullError as e:
84
+ console.error(e.message, include_traceback=True)
85
+
86
+ with console.spinner("Pulling UiPath project files..."):
87
+ asyncio.run(pull_with_updates())
uipath/_cli/cli_push.py CHANGED
@@ -1,13 +1,15 @@
1
1
  # type: ignore
2
2
  import asyncio
3
3
  import os
4
- from typing import Any, Optional
4
+ from typing import Any, AsyncIterator, Optional
5
5
  from urllib.parse import urlparse
6
6
 
7
7
  import click
8
8
 
9
+ from uipath.models.exceptions import EnrichedException
10
+
9
11
  from ..telemetry import track
10
- from ._push.sw_file_handler import SwFileHandler
12
+ from ._push.sw_file_handler import FileOperationUpdate, SwFileHandler
11
13
  from ._utils._console import ConsoleLogger
12
14
  from ._utils._constants import (
13
15
  UIPATH_PROJECT_ID,
@@ -35,14 +37,20 @@ async def upload_source_files_to_project(
35
37
  settings: Optional[dict[str, Any]],
36
38
  directory: str,
37
39
  include_uv_lock: bool = True,
38
- ) -> None:
39
- """Upload source files to UiPath project.
40
+ ) -> AsyncIterator[FileOperationUpdate]:
41
+ """Upload source files to UiPath project, yielding progress updates.
40
42
 
41
43
  This function handles the pushing of local files to the remote project:
42
44
  - Updates existing files that have changed
43
45
  - Uploads new files that don't exist remotely
44
46
  - Deletes remote files that no longer exist locally
45
47
  - Optionally includes the UV lock file
48
+
49
+ Yields:
50
+ FileOperationUpdate: Progress updates for each file operation
51
+
52
+ Raises:
53
+ ProjectPushError: If the push operation fails
46
54
  """
47
55
  sw_file_handler = SwFileHandler(
48
56
  project_id=project_id,
@@ -50,7 +58,8 @@ async def upload_source_files_to_project(
50
58
  include_uv_lock=include_uv_lock,
51
59
  )
52
60
 
53
- await sw_file_handler.upload_source_files(settings)
61
+ async for update in sw_file_handler.upload_source_files(settings):
62
+ yield update
54
63
 
55
64
 
56
65
  @click.command()
@@ -88,21 +97,29 @@ def push(root: str, nolock: bool) -> None:
88
97
  config = get_project_config(root)
89
98
  validate_config(config)
90
99
 
91
- if not os.getenv(UIPATH_PROJECT_ID, False):
100
+ project_id = os.getenv(UIPATH_PROJECT_ID)
101
+ if not project_id:
92
102
  console.error("UIPATH_PROJECT_ID environment variable not found.")
93
103
 
94
- with console.spinner("Pushing coded UiPath project to Studio Web..."):
104
+ async def push_with_updates():
105
+ """Wrapper to handle async iteration and display updates."""
106
+ async for update in upload_source_files_to_project(
107
+ project_id,
108
+ config.get("settings", {}),
109
+ root,
110
+ include_uv_lock=not nolock,
111
+ ):
112
+ console.info(update.message)
113
+
114
+ with console.spinner("Pushing UiPath project to Studio Web..."):
95
115
  try:
96
116
  if not nolock:
97
117
  handle_uv_operations(root)
98
118
 
99
- asyncio.run(
100
- upload_source_files_to_project(
101
- os.getenv(UIPATH_PROJECT_ID),
102
- config.get("settings", {}),
103
- root,
104
- include_uv_lock=not nolock,
105
- )
106
- )
119
+ asyncio.run(push_with_updates())
120
+
107
121
  except Exception as e:
108
- console.error(f"Failed to push UiPath project: {e}")
122
+ console.error(
123
+ f"Failed to push UiPath project: {e}",
124
+ include_traceback=not isinstance(e, EnrichedException),
125
+ )
uipath/_uipath.py CHANGED
@@ -22,6 +22,7 @@ from ._services import (
22
22
  UiPathOpenAIService,
23
23
  )
24
24
  from ._utils._auth import resolve_config
25
+ from ._utils._logs import setup_logging
25
26
  from .models.errors import BaseUrlMissingError, SecretMissingError
26
27
 
27
28
 
@@ -54,7 +55,7 @@ class UiPath:
54
55
  self._buckets_service: Optional[BucketsService] = None
55
56
  self._attachments_service: Optional[AttachmentsService] = None
56
57
  self._connections_service: Optional[ConnectionsService] = None
57
-
58
+ setup_logging(should_debug=debug)
58
59
  self._execution_context = ExecutionContext()
59
60
 
60
61
  @property
uipath/_utils/_logs.py CHANGED
@@ -1,14 +1,13 @@
1
1
  import logging
2
- import sys
3
2
  from typing import Optional
4
3
 
5
4
  logger: logging.Logger = logging.getLogger("uipath")
6
5
 
7
6
 
8
7
  def setup_logging(should_debug: Optional[bool] = None) -> None:
9
- logging.basicConfig(
10
- format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s",
11
- datefmt="%Y-%m-%d %H:%M:%S",
12
- )
13
- logger.setLevel(logging.DEBUG if should_debug else logging.INFO)
14
- logger.addHandler(logging.StreamHandler(sys.stderr))
8
+ if not logging.root.handlers and not logger.handlers:
9
+ logging.basicConfig(
10
+ format="%(message)s",
11
+ datefmt="%Y-%m-%d %H:%M:%S",
12
+ )
13
+ logger.setLevel(logging.DEBUG if should_debug else logging.INFO)
uipath/agent/_utils.py CHANGED
@@ -8,7 +8,6 @@ from uipath._cli._evals._models._evaluation_set import (
8
8
  InputMockingStrategy,
9
9
  LLMMockingStrategy,
10
10
  )
11
- from uipath._cli._push.sw_file_handler import SwFileHandler
12
11
  from uipath._cli._utils._studio_project import (
13
12
  ProjectFile,
14
13
  ProjectFolder,
@@ -38,14 +37,6 @@ async def create_agent_project(solution_id: str, project_name: str) -> str:
38
37
  return project["id"]
39
38
 
40
39
 
41
- async def upload_project_files(project_id: str, root: str) -> None:
42
- sw_file_handler = SwFileHandler(
43
- project_id=project_id,
44
- directory=root,
45
- )
46
- await sw_file_handler.upload_source_files({})
47
-
48
-
49
40
  async def load_agent_definition(project_id: str) -> AgentDefinition:
50
41
  studio_client = StudioClient(project_id=project_id)
51
42
  project_structure = await studio_client.get_project_structure_async()
@@ -6,6 +6,7 @@ from typing import Annotated, Any, Dict, List, Literal, Optional, Union
6
6
  from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, field_validator
7
7
 
8
8
  from uipath.models import Connection
9
+ from uipath.models.guardrails import AgentEscalationRecipient, Guardrail
9
10
 
10
11
 
11
12
  class AgentResourceType(str, Enum):
@@ -305,39 +306,6 @@ class AgentMcpResourceConfig(BaseAgentResourceConfig):
305
306
  )
306
307
 
307
308
 
308
- class AgentEscalationRecipientType(str, Enum):
309
- """Enum for escalation recipient types."""
310
-
311
- USER_ID = "UserId"
312
- GROUP_ID = "GroupId"
313
- USER_EMAIL = "UserEmail"
314
-
315
-
316
- class AgentEscalationRecipient(BaseModel):
317
- """Recipient for escalation."""
318
-
319
- type: Union[AgentEscalationRecipientType, str] = Field(..., alias="type")
320
- value: str = Field(..., alias="value")
321
- display_name: Optional[str] = Field(default=None, alias="displayName")
322
-
323
- @field_validator("type", mode="before")
324
- @classmethod
325
- def normalize_type(cls, v: Any) -> str:
326
- """Normalize recipient type from int (1=UserId, 2=GroupId, 3=UserEmail) or string. Unknown integers are converted to string."""
327
- if isinstance(v, int):
328
- mapping = {
329
- 1: AgentEscalationRecipientType.USER_ID,
330
- 2: AgentEscalationRecipientType.GROUP_ID,
331
- 3: AgentEscalationRecipientType.USER_EMAIL,
332
- }
333
- return mapping.get(v, str(v))
334
- return v
335
-
336
- model_config = ConfigDict(
337
- validate_by_name=True, validate_by_alias=True, extra="allow"
338
- )
339
-
340
-
341
309
  class AgentEscalationChannelProperties(BaseResourceProperties):
342
310
  """Agent escalation channel properties."""
343
311
 
@@ -508,6 +476,9 @@ class BaseAgentDefinition(BaseModel):
508
476
  output_schema: Dict[str, Any] = Field(
509
477
  ..., alias="outputSchema", description="JSON schema for output arguments"
510
478
  )
479
+ guardrails: Optional[List[Guardrail]] = Field(
480
+ None, description="List of agent guardrails"
481
+ )
511
482
 
512
483
  model_config = ConfigDict(
513
484
  validate_by_name=True, validate_by_alias=True, extra="allow"
@@ -25,11 +25,15 @@ class EnrichedException(Exception):
25
25
  if error.request and error.request.method
26
26
  else "Unknown"
27
27
  )
28
- self.response_content = (
29
- error.response.content.decode("utf-8")
30
- if error.response and error.response.content
31
- else "No content"
32
- )
28
+ max_content_length = 200
29
+ if error.response and error.response.content:
30
+ content = error.response.content.decode("utf-8")
31
+ if len(content) > max_content_length:
32
+ self.response_content = content[:max_content_length] + "... (truncated)"
33
+ else:
34
+ self.response_content = content
35
+ else:
36
+ self.response_content = "No content"
33
37
 
34
38
  enriched_message = (
35
39
  f"\nRequest URL: {self.url}"