uipath 2.0.0.dev2__tar.gz → 2.0.0.dev3__tar.gz

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.

Files changed (75) hide show
  1. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/PKG-INFO +1 -1
  2. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/pyproject.toml +1 -1
  3. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/__init__.py +2 -0
  4. uipath-2.0.0.dev3/uipath/_models/context_grounding_index.py +60 -0
  5. uipath-2.0.0.dev3/uipath/_models/exceptions.py +6 -0
  6. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/__init__.py +2 -0
  7. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/buckets_service.py +68 -5
  8. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/context_grounding_service.py +199 -15
  9. uipath-2.0.0.dev3/uipath/_services/folder_service.py +49 -0
  10. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_uipath.py +13 -1
  11. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_utils/constants.py +5 -0
  12. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/.gitignore +0 -0
  13. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/README.md +0 -0
  14. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/__init__.py +0 -0
  15. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/README.md +0 -0
  16. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/__init__.py +0 -0
  17. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_auth/_auth_server.py +0 -0
  18. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_auth/_models.py +0 -0
  19. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_auth/_oidc_utils.py +0 -0
  20. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_auth/_portal_service.py +0 -0
  21. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_auth/_utils.py +0 -0
  22. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_auth/auth_config.json +0 -0
  23. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_auth/index.html +0 -0
  24. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_auth/localhost.crt +0 -0
  25. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_auth/localhost.key +0 -0
  26. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_runtime/_contracts.py +0 -0
  27. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_runtime/_logging.py +0 -0
  28. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_runtime/_runtime.py +0 -0
  29. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_templates/.psmdcp.template +0 -0
  30. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_templates/.rels.template +0 -0
  31. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_templates/[Content_Types].xml.template +0 -0
  32. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_templates/main.py.template +0 -0
  33. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_templates/package.nuspec.template +0 -0
  34. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_utils/_common.py +0 -0
  35. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_utils/_input_args.py +0 -0
  36. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/_utils/_parse_ast.py +0 -0
  37. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/cli_auth.py +0 -0
  38. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/cli_deploy.py +0 -0
  39. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/cli_init.py +0 -0
  40. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/cli_new.py +0 -0
  41. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/cli_pack.py +0 -0
  42. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/cli_publish.py +0 -0
  43. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/cli_run.py +0 -0
  44. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_cli/middlewares.py +0 -0
  45. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_config.py +0 -0
  46. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_execution_context.py +0 -0
  47. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_folder_context.py +0 -0
  48. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/action_schema.py +0 -0
  49. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/actions.py +0 -0
  50. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/assets.py +0 -0
  51. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/connections.py +0 -0
  52. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/context_grounding.py +0 -0
  53. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/interrupt_models.py +0 -0
  54. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/job.py +0 -0
  55. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/llm_gateway.py +0 -0
  56. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/processes.py +0 -0
  57. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_models/queues.py +0 -0
  58. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/_base_service.py +0 -0
  59. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/actions_service.py +0 -0
  60. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/api_client.py +0 -0
  61. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/assets_service.py +0 -0
  62. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/connections_service.py +0 -0
  63. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/connections_service.pyi +0 -0
  64. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/jobs_service.py +0 -0
  65. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/llm_gateway_service.py +0 -0
  66. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/processes_service.py +0 -0
  67. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_services/queues_service.py +0 -0
  68. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_utils/__init__.py +0 -0
  69. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_utils/_endpoint.py +0 -0
  70. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_utils/_infer_bindings.py +0 -0
  71. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_utils/_logs.py +0 -0
  72. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_utils/_request_override.py +0 -0
  73. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_utils/_request_spec.py +0 -0
  74. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/_utils/_user_agent.py +0 -0
  75. {uipath-2.0.0.dev2 → uipath-2.0.0.dev3}/uipath/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath
3
- Version: 2.0.0.dev2
3
+ Version: 2.0.0.dev3
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "uipath"
3
- version = "2.0.0.dev2"
3
+ version = "2.0.0.dev3"
4
4
  description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools."
5
5
  readme = { file = "README.md", content-type = "text/markdown" }
6
6
  requires-python = ">=3.9"
@@ -3,6 +3,7 @@ from .actions import Action
3
3
  from .assets import UserAsset
4
4
  from .connections import Connection, ConnectionToken
5
5
  from .context_grounding import ContextGroundingQueryResponse
6
+ from .exceptions import IngestionInProgressException
6
7
  from .interrupt_models import CreateAction, InvokeProcess, WaitAction, WaitJob
7
8
  from .job import Job
8
9
  from .processes import Process
@@ -32,4 +33,5 @@ __all__ = [
32
33
  "WaitJob",
33
34
  "WaitAction",
34
35
  "CreateAction",
36
+ "IngestionInProgressException",
35
37
  ]
@@ -0,0 +1,60 @@
1
+ from datetime import datetime
2
+ from typing import Any, List, Optional
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+
6
+
7
+ class ContextGroundingField(BaseModel):
8
+ id: Optional[str] = Field(default=None, alias="id")
9
+ name: Optional[str] = Field(default=None, alias="name")
10
+ description: Optional[str] = Field(default=None, alias="description")
11
+ type: Optional[str] = Field(default=None, alias="type")
12
+ is_filterable: Optional[bool] = Field(default=None, alias="isFilterable")
13
+ searchable_type: Optional[str] = Field(default=None, alias="searchableType")
14
+ is_user_defined: Optional[bool] = Field(default=None, alias="isUserDefined")
15
+
16
+
17
+ class ContextGroundingDataSource(BaseModel):
18
+ model_config = ConfigDict(
19
+ validate_by_name=True,
20
+ validate_by_alias=True,
21
+ use_enum_values=True,
22
+ arbitrary_types_allowed=True,
23
+ extra="allow",
24
+ json_encoders={datetime: lambda v: v.isoformat() if v else None},
25
+ )
26
+ id: Optional[str] = Field(default=None, alias="id")
27
+ folder: Optional[str] = Field(default=None, alias="folder")
28
+
29
+
30
+ class ContextGroundingIndex(BaseModel):
31
+ model_config = ConfigDict(
32
+ validate_by_name=True,
33
+ validate_by_alias=True,
34
+ use_enum_values=True,
35
+ arbitrary_types_allowed=True,
36
+ extra="allow",
37
+ json_encoders={datetime: lambda v: v.isoformat() if v else None},
38
+ )
39
+ id: Optional[str] = Field(default=None, alias="id")
40
+ name: Optional[str] = Field(default=None, alias="name")
41
+ description: Optional[str] = Field(default=None, alias="description")
42
+ memory_usage: Optional[int] = Field(default=None, alias="memoryUsage")
43
+ disk_usage: Optional[int] = Field(default=None, alias="diskUsage")
44
+ data_source: Optional[ContextGroundingDataSource] = Field(
45
+ default=None, alias="dataSource"
46
+ )
47
+ pre_processing: Any = Field(default=None, alias="preProcessing")
48
+ fields: Optional[List[ContextGroundingField]] = Field(default=None, alias="fields")
49
+ last_ingestion_status: Optional[str] = Field(
50
+ default=None, alias="lastIngestionStatus"
51
+ )
52
+ last_ingested: Optional[datetime] = Field(default=None, alias="lastIngested")
53
+ last_queried: Optional[datetime] = Field(default=None, alias="lastQueried")
54
+ folder_key: Optional[str] = Field(default=None, alias="folderKey")
55
+
56
+ def in_progress_ingestion(self):
57
+ return (
58
+ self.last_ingestion_status == "Queued"
59
+ or self.last_ingestion_status == "In Progress"
60
+ )
@@ -0,0 +1,6 @@
1
+ class IngestionInProgressException(Exception):
2
+ """An exception that is triggered when a search is attempted on an index that is currently undergoing ingestion."""
3
+
4
+ def __init__(self, index_name):
5
+ self.message = f"index {index_name} cannot be searched during ingestion"
6
+ super().__init__(self.message)
@@ -4,6 +4,7 @@ from .assets_service import AssetsService
4
4
  from .buckets_service import BucketsService
5
5
  from .connections_service import ConnectionsService
6
6
  from .context_grounding_service import ContextGroundingService
7
+ from .folder_service import FolderService
7
8
  from .jobs_service import JobsService
8
9
  from .llm_gateway_service import UiPathLlmChatService, UiPathOpenAIService
9
10
  from .processes_service import ProcessesService
@@ -21,4 +22,5 @@ __all__ = [
21
22
  "JobsService",
22
23
  "UiPathOpenAIService",
23
24
  "UiPathLlmChatService",
25
+ "FolderService",
24
26
  ]
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict
1
+ from typing import Any, Dict, Optional, Union
2
2
 
3
3
  from httpx import request
4
4
 
@@ -59,7 +59,9 @@ class BucketsService(FolderContext, BaseService):
59
59
 
60
60
  def upload(
61
61
  self,
62
- bucket_key: str,
62
+ *,
63
+ bucket_key: Optional[str] = None,
64
+ bucket_name: Optional[str] = None,
63
65
  blob_file_path: str,
64
66
  content_type: str,
65
67
  source_path: str,
@@ -68,11 +70,18 @@ class BucketsService(FolderContext, BaseService):
68
70
 
69
71
  Args:
70
72
  bucket_key: The key of the bucket
73
+ bucket_name: The name of the bucket
71
74
  blob_file_path: The path where the file will be stored in the bucket
72
75
  content_type: The MIME type of the file
73
76
  source_path: The local path of the file to upload
74
77
  """
75
- bucket = self.retrieve_by_key(bucket_key)
78
+ if bucket_key:
79
+ bucket = self.retrieve_by_key(bucket_key)
80
+ elif bucket_name:
81
+ bucket = self.retrieve(bucket_name)
82
+ else:
83
+ raise ValueError("Must specify a bucket name or bucket key")
84
+
76
85
  bucket_id = bucket["Id"]
77
86
 
78
87
  endpoint = Endpoint(
@@ -99,6 +108,60 @@ class BucketsService(FolderContext, BaseService):
99
108
  else:
100
109
  request("PUT", write_uri, headers=headers, files={"file": file})
101
110
 
111
+ def upload_from_memory(
112
+ self,
113
+ *,
114
+ bucket_key: Optional[str] = None,
115
+ bucket_name: Optional[str] = None,
116
+ blob_file_path: str,
117
+ content_type: str,
118
+ content: Union[str, bytes],
119
+ ) -> None:
120
+ """Upload content from memory to a bucket.
121
+
122
+ Args:
123
+ bucket_key: The key of the bucket
124
+ bucket_name: The name of the bucket
125
+ blob_file_path: The path where the content will be stored in the bucket
126
+ content_type: The MIME type of the content
127
+ content: The content to upload (string or bytes)
128
+ """
129
+ if bucket_key:
130
+ bucket = self.retrieve_by_key(bucket_key)
131
+ elif bucket_name:
132
+ bucket = self.retrieve(bucket_name)
133
+ else:
134
+ raise ValueError("Must specify a bucket name or bucket key")
135
+
136
+ bucket_id = bucket["Id"]
137
+
138
+ endpoint = Endpoint(
139
+ f"/orchestrator_/odata/Buckets({bucket_id})/UiPath.Server.Configuration.OData.GetWriteUri"
140
+ )
141
+
142
+ result = self.request(
143
+ "GET",
144
+ endpoint,
145
+ params={"path": blob_file_path, "contentType": content_type},
146
+ ).json()
147
+ write_uri = result["Uri"]
148
+
149
+ headers = {
150
+ key: value
151
+ for key, value in zip(
152
+ result["Headers"]["Keys"], result["Headers"]["Values"]
153
+ )
154
+ }
155
+
156
+ # Convert string to bytes if needed
157
+ if isinstance(content, str):
158
+ content = content.encode("utf-8")
159
+
160
+ if result["RequiresAuth"]:
161
+ self.request("PUT", write_uri, headers=headers, content=content)
162
+ else:
163
+ request("PUT", write_uri, headers=headers, content=content)
164
+
102
165
  @infer_bindings()
103
166
  def retrieve(self, name: str) -> Any:
104
167
  """Retrieve bucket information by its name.
@@ -192,7 +255,7 @@ class BucketsService(FolderContext, BaseService):
192
255
  def _retrieve_spec(self, name: str) -> RequestSpec:
193
256
  return RequestSpec(
194
257
  method="GET",
195
- endpoint=Endpoint("/odata/Buckets"),
258
+ endpoint=Endpoint("/orchestrator_/odata/Buckets"),
196
259
  params={"$filter": f"Name eq '{name}'", "$top": 1},
197
260
  )
198
261
 
@@ -200,6 +263,6 @@ class BucketsService(FolderContext, BaseService):
200
263
  return RequestSpec(
201
264
  method="GET",
202
265
  endpoint=Endpoint(
203
- f"/odata/Buckets/UiPath.Server.Configuration.OData.GetByKey(identifier={key})"
266
+ f"/orchestrator_/odata/Buckets/UiPath.Server.Configuration.OData.GetByKey(identifier={key})"
204
267
  ),
205
268
  )
@@ -1,14 +1,21 @@
1
1
  import json
2
- from typing import Any, Dict, List
2
+ from typing import Any, Dict, List, Optional
3
3
 
4
4
  from pydantic import TypeAdapter
5
5
 
6
6
  from .._config import Config
7
7
  from .._execution_context import ExecutionContext
8
8
  from .._folder_context import FolderContext
9
+ from .._models import IngestionInProgressException
9
10
  from .._models.context_grounding import ContextGroundingQueryResponse
11
+ from .._models.context_grounding_index import ContextGroundingIndex
10
12
  from .._utils import Endpoint, RequestSpec
13
+ from .._utils.constants import (
14
+ HEADER_FOLDER_KEY,
15
+ ORCHESTRATOR_STORAGE_BUCKET_DATA_SOURCE,
16
+ )
11
17
  from ._base_service import BaseService
18
+ from .folder_service import FolderService
12
19
 
13
20
 
14
21
  class ContextGroundingService(FolderContext, BaseService):
@@ -24,10 +31,16 @@ class ContextGroundingService(FolderContext, BaseService):
24
31
  context.
25
32
  """
26
33
 
27
- def __init__(self, config: Config, execution_context: ExecutionContext) -> None:
34
+ def __init__(
35
+ self,
36
+ config: Config,
37
+ execution_context: ExecutionContext,
38
+ folders_service: FolderService,
39
+ ) -> None:
40
+ self._folders_service = folders_service
28
41
  super().__init__(config=config, execution_context=execution_context)
29
42
 
30
- def retrieve(self, name: str) -> Any:
43
+ def retrieve(self, name: str) -> Optional[ContextGroundingIndex]:
31
44
  """Retrieve context grounding index information by its name.
32
45
 
33
46
  This method fetches details about a specific context index, which can be
@@ -38,17 +51,25 @@ class ContextGroundingService(FolderContext, BaseService):
38
51
  name (str): The name of the context index to retrieve.
39
52
 
40
53
  Returns:
41
- Any: The index information, including its configuration and metadata.
54
+ Optional[ContextGroundingIndex]: The index information, including its configuration and metadata if found, otherwise None.
42
55
  """
43
56
  spec = self._retrieve_spec(name)
44
57
 
45
- return self.request(
58
+ response = self.request(
46
59
  spec.method,
47
60
  spec.endpoint,
48
61
  params=spec.params,
49
62
  ).json()
63
+ return next(
64
+ (
65
+ ContextGroundingIndex.model_validate(item)
66
+ for item in response["value"]
67
+ if item["name"] == name
68
+ ),
69
+ None,
70
+ )
50
71
 
51
- async def retrieve_async(self, name: str) -> Any:
72
+ async def retrieve_async(self, name: str) -> Optional[ContextGroundingIndex]:
52
73
  """Retrieve asynchronously context grounding index information by its name.
53
74
 
54
75
  This method fetches details about a specific context index, which can be
@@ -59,19 +80,27 @@ class ContextGroundingService(FolderContext, BaseService):
59
80
  name (str): The name of the context index to retrieve.
60
81
 
61
82
  Returns:
62
- Any: The index information, including its configuration and metadata.
83
+ Optional[ContextGroundingIndex]: The index information, including its configuration and metadata if found, otherwise None.
63
84
 
64
85
  """
65
86
  spec = self._retrieve_spec(name)
66
87
 
67
- response = await self.request_async(
68
- spec.method,
69
- spec.endpoint,
70
- params=spec.params,
88
+ response = (
89
+ await self.request_async(
90
+ spec.method,
91
+ spec.endpoint,
92
+ params=spec.params,
93
+ )
94
+ ).json()
95
+ return next(
96
+ (
97
+ ContextGroundingIndex.model_validate(item)
98
+ for item in response["value"]
99
+ if item["name"] == name
100
+ ),
101
+ None,
71
102
  )
72
103
 
73
- return response.json()
74
-
75
104
  def retrieve_by_id(self, id: str) -> Any:
76
105
  """Retrieve context grounding index information by its ID.
77
106
 
@@ -137,6 +166,10 @@ class ContextGroundingService(FolderContext, BaseService):
137
166
  List[ContextGroundingQueryResponse]: A list of search results, each containing
138
167
  relevant contextual information and metadata.
139
168
  """
169
+ index = self.retrieve(name)
170
+ if index and index.in_progress_ingestion():
171
+ raise IngestionInProgressException(index_name=name)
172
+
140
173
  spec = self._search_spec(name, query, number_of_results)
141
174
 
142
175
  response = self.request(
@@ -171,6 +204,9 @@ class ContextGroundingService(FolderContext, BaseService):
171
204
  List[ContextGroundingQueryResponse]: A list of search results, each containing
172
205
  relevant contextual information and metadata.
173
206
  """
207
+ index = self.retrieve(name)
208
+ if index and index.in_progress_ingestion():
209
+ raise IngestionInProgressException(index_name=name)
174
210
  spec = self._search_spec(name, query, number_of_results)
175
211
 
176
212
  response = await self.request_async(
@@ -183,13 +219,122 @@ class ContextGroundingService(FolderContext, BaseService):
183
219
  response.json()
184
220
  )
185
221
 
222
+ def get_or_create_index(
223
+ self,
224
+ name: str,
225
+ *,
226
+ description: Optional[str] = None,
227
+ storage_bucket_name: str,
228
+ file_name_glob: Optional[str] = None,
229
+ storage_bucket_folder_path: Optional[str] = None,
230
+ ) -> ContextGroundingIndex:
231
+ spec = self._create_spec(
232
+ name,
233
+ description,
234
+ storage_bucket_name,
235
+ file_name_glob,
236
+ storage_bucket_folder_path,
237
+ )
238
+ index = self.retrieve(name=name)
239
+ if index:
240
+ return index
241
+
242
+ response = self.request(
243
+ spec.method,
244
+ spec.endpoint,
245
+ content=spec.content,
246
+ headers=spec.headers,
247
+ ).json()
248
+ return ContextGroundingIndex.model_validate(response)
249
+
250
+ async def get_or_create_index_async(
251
+ self,
252
+ name: str,
253
+ *,
254
+ description: Optional[str] = None,
255
+ storage_bucket_name: str,
256
+ file_name_glob: Optional[str] = None,
257
+ storage_bucket_folder_path: Optional[str] = None,
258
+ ) -> ContextGroundingIndex:
259
+ index = await self.retrieve_async(name=name)
260
+ if index:
261
+ return index
262
+
263
+ spec = self._create_spec(
264
+ name,
265
+ description,
266
+ storage_bucket_name,
267
+ file_name_glob,
268
+ storage_bucket_folder_path,
269
+ )
270
+ response = (
271
+ await self.request_async(
272
+ spec.method,
273
+ spec.endpoint,
274
+ content=spec.content,
275
+ headers=spec.headers,
276
+ )
277
+ ).json()
278
+ return ContextGroundingIndex.model_validate(response)
279
+
280
+ def ingest_data(self, index: ContextGroundingIndex) -> None:
281
+ if not index.id:
282
+ return
283
+ spec = self._ingest_spec(index.id)
284
+ self.request(
285
+ spec.method,
286
+ spec.endpoint,
287
+ headers=spec.headers,
288
+ )
289
+
290
+ async def ingest_data_async(self, index: ContextGroundingIndex) -> None:
291
+ if not index.id:
292
+ return
293
+ spec = self._ingest_spec(index.id)
294
+ await self.request_async(
295
+ spec.method,
296
+ spec.endpoint,
297
+ headers=spec.headers,
298
+ )
299
+
300
+ def delete_index(self, index: ContextGroundingIndex) -> None:
301
+ if not index.id:
302
+ return
303
+ spec = self._delete_by_id_spec(index.id)
304
+ self.request(
305
+ spec.method,
306
+ spec.endpoint,
307
+ headers=spec.headers,
308
+ )
309
+
310
+ async def delete_index_async(self, index: ContextGroundingIndex) -> None:
311
+ if not index.id:
312
+ return
313
+ spec = self._delete_by_id_spec(index.id)
314
+ await self.request_async(
315
+ spec.method,
316
+ spec.endpoint,
317
+ headers=spec.headers,
318
+ )
319
+
186
320
  @property
187
321
  def custom_headers(self) -> Dict[str, str]:
188
- if self.folder_headers["x-uipath-folderkey"] is None:
189
- raise ValueError("Folder key is not set (UIPATH_FOLDER_KEY)")
322
+ self._folder_key = self._folder_key or (
323
+ self._folders_service.retrieve_key_by_folder_path(self._folder_path)
324
+ if self._folder_path
325
+ else None
326
+ )
327
+
328
+ if self._folder_key is None:
329
+ raise ValueError(f"Folder key is not set ({HEADER_FOLDER_KEY})")
190
330
 
191
331
  return self.folder_headers
192
332
 
333
+ def _ingest_spec(self, key: str) -> RequestSpec:
334
+ return RequestSpec(
335
+ method="POST", endpoint=Endpoint(f"/ecs_/v2/indexes/{key}/ingest")
336
+ )
337
+
193
338
  def _retrieve_spec(self, name: str) -> RequestSpec:
194
339
  return RequestSpec(
195
340
  method="GET",
@@ -197,12 +342,51 @@ class ContextGroundingService(FolderContext, BaseService):
197
342
  params={"$filter": f"Name eq '{name}'"},
198
343
  )
199
344
 
345
+ def _create_spec(
346
+ self,
347
+ name: str,
348
+ description: Optional[str],
349
+ storage_bucket_name: Optional[str],
350
+ file_name_glob: Optional[str],
351
+ storage_bucket_folder_path: Optional[str],
352
+ ) -> RequestSpec:
353
+ storage_bucket_folder_path = (
354
+ storage_bucket_folder_path
355
+ if storage_bucket_folder_path
356
+ else self._folder_path
357
+ )
358
+ return RequestSpec(
359
+ method="POST",
360
+ endpoint=Endpoint("/ecs_/v2/indexes/create"),
361
+ content=json.dumps(
362
+ {
363
+ "name": name,
364
+ "description": description,
365
+ "dataSource": {
366
+ "@odata.type": ORCHESTRATOR_STORAGE_BUCKET_DATA_SOURCE,
367
+ "folder": storage_bucket_folder_path,
368
+ "bucketName": storage_bucket_name,
369
+ "fileNameGlob": file_name_glob
370
+ if file_name_glob is not None
371
+ else "*",
372
+ "directoryPath": "/",
373
+ },
374
+ }
375
+ ),
376
+ )
377
+
200
378
  def _retrieve_by_id_spec(self, id: str) -> RequestSpec:
201
379
  return RequestSpec(
202
380
  method="GET",
203
381
  endpoint=Endpoint(f"/ecs_/v2/indexes/{id}"),
204
382
  )
205
383
 
384
+ def _delete_by_id_spec(self, id: str) -> RequestSpec:
385
+ return RequestSpec(
386
+ method="DELETE",
387
+ endpoint=Endpoint(f"/ecs_/v2/indexes/{id}"),
388
+ )
389
+
206
390
  def _search_spec(
207
391
  self, name: str, query: str, number_of_results: int = 10
208
392
  ) -> RequestSpec:
@@ -0,0 +1,49 @@
1
+ from typing import Optional
2
+
3
+ from .._config import Config
4
+ from .._execution_context import ExecutionContext
5
+ from .._utils import Endpoint, RequestSpec
6
+ from ._base_service import BaseService
7
+
8
+
9
+ def _retrieve_spec(folder_path: str) -> RequestSpec:
10
+ folder_name = folder_path.split("/")[-1]
11
+ return RequestSpec(
12
+ method="GET",
13
+ endpoint=Endpoint(
14
+ "orchestrator_/api/FoldersNavigation/GetFoldersForCurrentUser"
15
+ ),
16
+ params={
17
+ "searchText": folder_name,
18
+ "take": 1,
19
+ },
20
+ )
21
+
22
+
23
+ class FolderService(BaseService):
24
+ """Service for managing UiPath Folders.
25
+
26
+ A folder represents a single area for data organization
27
+ and access control - it is created when you need to categorize, manage, and enforce authorization rules for a group
28
+ of UiPath resources (i.e. processes, assets, connections, storage buckets etc.) or other folders
29
+ """
30
+
31
+ def __init__(self, config: Config, execution_context: ExecutionContext) -> None:
32
+ super().__init__(config=config, execution_context=execution_context)
33
+
34
+ def retrieve_key_by_folder_path(self, folder_path: str) -> Optional[str]:
35
+ spec = _retrieve_spec(folder_path)
36
+ response = self.request(
37
+ spec.method,
38
+ url=spec.endpoint,
39
+ params=spec.params,
40
+ ).json()
41
+
42
+ return next(
43
+ (
44
+ item["Key"]
45
+ for item in response["PageItems"]
46
+ if item["FullyQualifiedName"] == folder_path
47
+ ),
48
+ None,
49
+ )
@@ -12,6 +12,7 @@ from ._services import (
12
12
  BucketsService,
13
13
  ConnectionsService,
14
14
  ContextGroundingService,
15
+ FolderService,
15
16
  JobsService,
16
17
  ProcessesService,
17
18
  QueuesService,
@@ -45,6 +46,7 @@ class UiPath:
45
46
  base_url=base_url_value, # type: ignore
46
47
  secret=secret_value, # type: ignore
47
48
  )
49
+ self._folders_service: Optional[FolderService] = None
48
50
 
49
51
  setup_logging(debug)
50
52
  self._execution_context = ExecutionContext()
@@ -75,7 +77,11 @@ class UiPath:
75
77
 
76
78
  @property
77
79
  def context_grounding(self) -> ContextGroundingService:
78
- return ContextGroundingService(self._config, self._execution_context)
80
+ if not self._folders_service:
81
+ self._folders_service = FolderService(self._config, self._execution_context)
82
+ return ContextGroundingService(
83
+ self._config, self._execution_context, self._folders_service
84
+ )
79
85
 
80
86
  @property
81
87
  def queues(self) -> QueuesService:
@@ -84,3 +90,9 @@ class UiPath:
84
90
  @property
85
91
  def jobs(self) -> JobsService:
86
92
  return JobsService(self._config, self._execution_context)
93
+
94
+ @property
95
+ def folders(self) -> FolderService:
96
+ if not self._folders_service:
97
+ self._folders_service = FolderService(self._config, self._execution_context)
98
+ return self._folders_service
@@ -18,3 +18,8 @@ HEADER_JOB_KEY = "x-uipath-jobkey"
18
18
 
19
19
  # Entrypoint for plugins
20
20
  ENTRYPOINT = "uipath.connectors"
21
+
22
+ # Data sources
23
+ ORCHESTRATOR_STORAGE_BUCKET_DATA_SOURCE = (
24
+ "#UiPath.Vdbs.Domain.Api.V20Models.StorageBucketDataSourceRequest"
25
+ )
File without changes
File without changes
File without changes