11import json
2- from typing import Any , Dict , List
2+ from typing import Any , Dict , List , Optional
3+ from urllib .request import Request
34
45from pydantic import TypeAdapter
56
67from .._config import Config
78from .._execution_context import ExecutionContext
89from .._folder_context import FolderContext
910from .._models .context_grounding import ContextGroundingQueryResponse
11+ from .._models .ecs_index import EcsIndex
1012from .._utils import Endpoint , RequestSpec
13+ from .._utils .constants import ORCHESTRATOR_STORAGE_BUCKET_DATA_SOURCE , ENV_FOLDER_PATH , HEADER_FOLDER_KEY
1114from ._base_service import BaseService
12-
13-
15+ from .folder_service import FolderService
1416class ContextGroundingService (FolderContext , BaseService ):
1517 """Service for managing semantic automation contexts in UiPath.
1618
@@ -24,10 +26,11 @@ class ContextGroundingService(FolderContext, BaseService):
2426 context.
2527 """
2628
27- def __init__ (self , config : Config , execution_context : ExecutionContext ) -> None :
29+ def __init__ (self , config : Config , execution_context : ExecutionContext , folders_service : FolderService ) -> None :
30+ self ._folders_service = folders_service
2831 super ().__init__ (config = config , execution_context = execution_context )
2932
30- def retrieve (self , name : str ) -> Any :
33+ def retrieve (self , name : str ) -> Optional [ EcsIndex ] :
3134 """Retrieve context grounding index information by its name.
3235
3336 This method fetches details about a specific context index, which can be
@@ -38,17 +41,18 @@ def retrieve(self, name: str) -> Any:
3841 name (str): The name of the context index to retrieve.
3942
4043 Returns:
41- Any : The index information, including its configuration and metadata.
44+ Optional[EcsIndex] : The index information, including its configuration and metadata if found, otherwise None .
4245 """
4346 spec = self ._retrieve_spec (name )
4447
45- return self .request (
48+ response = self .request (
4649 spec .method ,
4750 spec .endpoint ,
4851 params = spec .params ,
4952 ).json ()
53+ return next ((EcsIndex .model_validate (item ) for item in response ["value" ] if item ["name" ] == name ), None )
5054
51- async def retrieve_async (self , name : str ) -> Any :
55+ async def retrieve_async (self , name : str ) -> Optional [ EcsIndex ] :
5256 """Retrieve asynchronously context grounding index information by its name.
5357
5458 This method fetches details about a specific context index, which can be
@@ -59,18 +63,17 @@ async def retrieve_async(self, name: str) -> Any:
5963 name (str): The name of the context index to retrieve.
6064
6165 Returns:
62- Any : The index information, including its configuration and metadata.
66+ Optional[EcsIndex] : The index information, including its configuration and metadata if found, otherwise None .
6367
6468 """
6569 spec = self ._retrieve_spec (name )
6670
67- response = await self .request_async (
71+ response = ( await self .request_async (
6872 spec .method ,
6973 spec .endpoint ,
7074 params = spec .params ,
71- )
72-
73- return response .json ()
75+ )).json ()
76+ return next ((EcsIndex .model_validate (item ) for item in response ["value" ] if item ["name" ] == name ), None )
7477
7578 def retrieve_by_id (self , id : str ) -> Any :
7679 """Retrieve context grounding index information by its ID.
@@ -183,19 +186,117 @@ async def search_async(
183186 response .json ()
184187 )
185188
189+ def get_or_create_index (self ,
190+ name : str ,
191+ * ,
192+ description : Optional [str ],
193+ storage_bucket_name : Optional [str ],
194+ file_name_glob : Optional [str ],
195+ storage_bucket_folder_path : Optional [str ],
196+ ) -> EcsIndex :
197+ spec = self ._create_or_get_spec (name , description , storage_bucket_name , file_name_glob , storage_bucket_folder_path )
198+ index = self .retrieve (name = name )
199+ if index :
200+ return index
201+
202+ response = self .request (
203+ spec .method ,
204+ spec .endpoint ,
205+ content = spec .content ,
206+ headers = spec .headers ,
207+ ).json ()
208+ return EcsIndex .model_validate (response )
209+
210+ async def get_or_create_index_async (self ,
211+ name : str ,
212+ * ,
213+ description : Optional [str ],
214+ storage_bucket_name : Optional [str ],
215+ file_name_glob : Optional [str ],
216+ storage_bucket_folder_path : Optional [str ],
217+ ) -> EcsIndex :
218+ spec = self ._create_or_get_spec (name , description , storage_bucket_name , file_name_glob , storage_bucket_folder_path )
219+ index = await self .retrieve_async (name = name )
220+ if index :
221+ return index
222+
223+ response = (await self .request_async (
224+ spec .method ,
225+ spec .endpoint ,
226+ content = spec .content ,
227+ headers = spec .headers ,
228+ )).json ()
229+ return EcsIndex .model_validate (response )
230+
231+ def ingest_data (self , index : EcsIndex ) -> None :
232+ spec = self ._ingest_spec (index .id )
233+ self .request (
234+ spec .method ,
235+ spec .endpoint ,
236+ headers = spec .headers ,
237+ )
238+
239+ async def ingest_data (self , index : EcsIndex ) -> None :
240+ spec = self ._ingest_spec (index .id )
241+ await self .request_async (
242+ spec .method ,
243+ spec .endpoint ,
244+ headers = spec .headers ,
245+ )
246+
186247 @property
187248 def custom_headers (self ) -> Dict [str , str ]:
188249 if self .folder_headers ["x-uipath-folderkey" ] is None :
189250 raise ValueError ("Folder key is not set (UIPATH_FOLDER_KEY)" )
190251
191252 return self .folder_headers
192253
254+ def _ingest_spec (self , key : str ) -> RequestSpec :
255+ if not self ._folder_path :
256+ raise ValueError (f"Folder path is not set ({ ENV_FOLDER_PATH } )" )
257+ folder_key = self ._folder_key if self ._folder_key else self ._folders_service .retrieve_key_by_folder_path (self ._folder_path )
258+
259+ return RequestSpec (
260+ method = "POST" ,
261+ endpoint = Endpoint (f"/ecs_/v2/indexes/{ key } /ingest" ),
262+ headers = {HEADER_FOLDER_KEY : folder_key }
263+ )
264+
193265 def _retrieve_spec (self , name : str ) -> RequestSpec :
194266 return RequestSpec (
195267 method = "GET" ,
196268 endpoint = Endpoint ("/ecs_/v2/indexes" ),
197269 params = {"$filter" : f"Name eq '{ name } '" },
198270 )
271+ def _create_or_get_spec (self ,
272+ name : str ,
273+ description : Optional [str ],
274+ storage_bucket_name : Optional [str ],
275+ file_name_glob : Optional [str ],
276+ storage_bucket_folder_path : Optional [str ],
277+ ) -> RequestSpec :
278+ if not self ._folder_path :
279+ raise ValueError (f"Folder path is not set ({ ENV_FOLDER_PATH } )" )
280+ storage_bucket_folder_path = storage_bucket_folder_path if storage_bucket_folder_path else self ._folder_path
281+ folder_key = self ._folder_key if self ._folder_key else self ._folders_service .retrieve_key_by_folder_path (self ._folder_path )
282+ return RequestSpec (
283+ method = "POST" ,
284+ endpoint = Endpoint ("/ecs_/v2/indexes/create" ),
285+ content = json .dumps (
286+ {
287+ "name" : name ,
288+ "description" : description ,
289+ "dataSource" :
290+ {
291+ "@odata.type" : ORCHESTRATOR_STORAGE_BUCKET_DATA_SOURCE ,
292+ "folder" : storage_bucket_folder_path ,
293+ "bucketName" : storage_bucket_name ,
294+ "fileNameGlob" : file_name_glob if file_name_glob is not None else "*" ,
295+ }
296+ }
297+ ),
298+ headers = {HEADER_FOLDER_KEY : folder_key },
299+ )
199300
200301 def _retrieve_by_id_spec (self , id : str ) -> RequestSpec :
201302 return RequestSpec (
0 commit comments