11import json
2- from typing import Any , Dict , List
2+ from typing import Any , Dict , List , Optional
33
44from pydantic import TypeAdapter
55
66from .._config import Config
77from .._execution_context import ExecutionContext
88from .._folder_context import FolderContext
99from .._models .context_grounding import ContextGroundingQueryResponse
10+ from .._models .ecs_index import EcsIndex
1011from .._utils import Endpoint , RequestSpec
12+ from .._utils .constants import ORCHESTRATOR_STORAGE_BUCKET_DATA_SOURCE , HEADER_FOLDER_KEY
1113from ._base_service import BaseService
12-
13-
14+ from .folder_service import FolderService
1415class ContextGroundingService (FolderContext , BaseService ):
1516 """Service for managing semantic automation contexts in UiPath.
1617
@@ -24,10 +25,11 @@ class ContextGroundingService(FolderContext, BaseService):
2425 context.
2526 """
2627
27- def __init__ (self , config : Config , execution_context : ExecutionContext ) -> None :
28+ def __init__ (self , config : Config , execution_context : ExecutionContext , folders_service : FolderService ) -> None :
29+ self ._folders_service = folders_service
2830 super ().__init__ (config = config , execution_context = execution_context )
2931
30- def retrieve (self , name : str ) -> Any :
32+ def retrieve (self , name : str ) -> Optional [ EcsIndex ] :
3133 """Retrieve context grounding index information by its name.
3234
3335 This method fetches details about a specific context index, which can be
@@ -38,17 +40,18 @@ def retrieve(self, name: str) -> Any:
3840 name (str): The name of the context index to retrieve.
3941
4042 Returns:
41- Any : The index information, including its configuration and metadata.
43+ Optional[EcsIndex] : The index information, including its configuration and metadata if found, otherwise None .
4244 """
4345 spec = self ._retrieve_spec (name )
4446
45- return self .request (
47+ response = self .request (
4648 spec .method ,
4749 spec .endpoint ,
4850 params = spec .params ,
4951 ).json ()
52+ return next ((EcsIndex .model_validate (item ) for item in response ["value" ] if item ["name" ] == name ), None )
5053
51- async def retrieve_async (self , name : str ) -> Any :
54+ async def retrieve_async (self , name : str ) -> Optional [ EcsIndex ] :
5255 """Retrieve asynchronously context grounding index information by its name.
5356
5457 This method fetches details about a specific context index, which can be
@@ -59,18 +62,17 @@ async def retrieve_async(self, name: str) -> Any:
5962 name (str): The name of the context index to retrieve.
6063
6164 Returns:
62- Any : The index information, including its configuration and metadata.
65+ Optional[EcsIndex] : The index information, including its configuration and metadata if found, otherwise None .
6366
6467 """
6568 spec = self ._retrieve_spec (name )
6669
67- response = await self .request_async (
70+ response = ( await self .request_async (
6871 spec .method ,
6972 spec .endpoint ,
7073 params = spec .params ,
71- )
72-
73- return response .json ()
74+ )).json ()
75+ return next ((EcsIndex .model_validate (item ) for item in response ["value" ] if item ["name" ] == name ), None )
7476
7577 def retrieve_by_id (self , id : str ) -> Any :
7678 """Retrieve context grounding index information by its ID.
@@ -183,19 +185,109 @@ async def search_async(
183185 response .json ()
184186 )
185187
188+ def get_or_create_index (self ,
189+ name : str ,
190+ * ,
191+ description : Optional [str ] = None ,
192+ storage_bucket_name : str ,
193+ file_name_glob : Optional [str ] = None ,
194+ storage_bucket_folder_path : Optional [str ] = None ,
195+ ) -> EcsIndex :
196+ spec = self ._create_spec (name , description , storage_bucket_name , file_name_glob , storage_bucket_folder_path )
197+ index = self .retrieve (name = name )
198+ if index :
199+ return index
200+
201+ response = self .request (
202+ spec .method ,
203+ spec .endpoint ,
204+ content = spec .content ,
205+ headers = spec .headers ,
206+ ).json ()
207+ return EcsIndex .model_validate (response )
208+
209+ async def get_or_create_index_async (self ,
210+ name : str ,
211+ * ,
212+ description : Optional [str ] = None ,
213+ storage_bucket_name : str ,
214+ file_name_glob : Optional [str ] = None ,
215+ storage_bucket_folder_path : Optional [str ] = None ,
216+ ) -> EcsIndex :
217+ spec = self ._create_spec (name , description , storage_bucket_name , file_name_glob , storage_bucket_folder_path )
218+ index = await self .retrieve_async (name = name )
219+ if index :
220+ return index
221+
222+ response = (await self .request_async (
223+ spec .method ,
224+ spec .endpoint ,
225+ content = spec .content ,
226+ headers = spec .headers ,
227+ )).json ()
228+ return EcsIndex .model_validate (response )
229+
230+ def ingest_data (self , index : EcsIndex ) -> None :
231+ spec = self ._ingest_spec (index .id )
232+ self .request (
233+ spec .method ,
234+ spec .endpoint ,
235+ headers = spec .headers ,
236+ )
237+
238+ async def ingest_data (self , index : EcsIndex ) -> None :
239+ spec = self ._ingest_spec (index .id )
240+ await self .request_async (
241+ spec .method ,
242+ spec .endpoint ,
243+ headers = spec .headers ,
244+ )
245+
186246 @property
187247 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)" )
248+ self ._folder_key = self ._folder_key or self ._folders_service .retrieve_key_by_folder_path (self ._folder_path )
249+ if self ._folder_key is None :
250+ raise ValueError (f"Folder key is not set ({ HEADER_FOLDER_KEY } )" )
190251
191252 return self .folder_headers
192253
254+ def _ingest_spec (self , key : str ) -> RequestSpec :
255+ return RequestSpec (
256+ method = "POST" ,
257+ endpoint = Endpoint (f"/ecs_/v2/indexes/{ key } /ingest" )
258+ )
259+
193260 def _retrieve_spec (self , name : str ) -> RequestSpec :
194261 return RequestSpec (
195262 method = "GET" ,
196263 endpoint = Endpoint ("/ecs_/v2/indexes" ),
197264 params = {"$filter" : f"Name eq '{ name } '" },
198265 )
266+ def _create_spec (self ,
267+ name : str ,
268+ description : Optional [str ],
269+ storage_bucket_name : Optional [str ],
270+ file_name_glob : Optional [str ],
271+ storage_bucket_folder_path : Optional [str ],
272+ ) -> RequestSpec :
273+ storage_bucket_folder_path = storage_bucket_folder_path if storage_bucket_folder_path else self ._folder_path
274+ return RequestSpec (
275+ method = "POST" ,
276+ endpoint = Endpoint ("/ecs_/v2/indexes/create" ),
277+ content = json .dumps (
278+ {
279+ "name" : name ,
280+ "description" : description ,
281+ "dataSource" :
282+ {
283+ "@odata.type" : ORCHESTRATOR_STORAGE_BUCKET_DATA_SOURCE ,
284+ "folder" : storage_bucket_folder_path ,
285+ "bucketName" : storage_bucket_name ,
286+ "fileNameGlob" : file_name_glob if file_name_glob is not None else "*" ,
287+ }
288+ }
289+ )
290+ )
199291
200292 def _retrieve_by_id_spec (self , id : str ) -> RequestSpec :
201293 return RequestSpec (
0 commit comments