From 64fc27334ad6d33de753528892c35031056aae49 Mon Sep 17 00:00:00 2001 From: Caleb Martin Date: Tue, 13 Jan 2026 16:42:41 -0800 Subject: [PATCH 1/4] feat: add new internal ecs tool definions --- src/uipath/agent/models/agent.py | 118 +++++++++- .../platform/common/interrupt_models.py | 8 +- .../_context_grounding_service.py | 208 +++++++++++++++--- .../platform/resume_triggers/_protocol.py | 62 ++++-- tests/cli/test_hitl.py | 2 + .../test_context_grounding_service.py | 58 +++++ 6 files changed, 395 insertions(+), 61 deletions(-) diff --git a/src/uipath/agent/models/agent.py b/src/uipath/agent/models/agent.py index f1053b654..aca323f86 100644 --- a/src/uipath/agent/models/agent.py +++ b/src/uipath/agent/models/agent.py @@ -63,6 +63,8 @@ class AgentInternalToolType(str, Enum): """Agent internal tool type enumeration.""" ANALYZE_FILES = "analyze-attachments" + DEEP_RAG = "deep-rag" + BATCH_TRANSFORM = "batch-transform" class AgentEscalationRecipientType(str, Enum): @@ -120,6 +122,33 @@ class TextTokenType(str, Enum): EXPRESSION = "expression" +class CitationMode(str, Enum): + """Citation mode enumeration.""" + + INLINE = "Inline" + SKIP = "Skip" + + +class DeepRagFileExtension(str, Enum): + """File extension enumeration for DeepRAG.""" + + PDF = "pdf" + TXT = "txt" + + +class BatchTransformFileExtension(str, Enum): + """File extension enumeration for Batch Transform.""" + + CSV = "csv" + + +class BatchTransformWebSearchGrounding(str, Enum): + """Batch Transform web search grounding enumeration.""" + + ENABLED = "Enabled" + DISABLED = "Disabled" + + class BaseCfg(BaseModel): """Base configuration model with common settings.""" @@ -236,9 +265,9 @@ class AgentUnknownResourceConfig(BaseAgentResourceConfig): class AgentContextQuerySetting(BaseCfg): """Agent context query setting model.""" - value: str | None = Field(None) - description: str | None = Field(None) - variant: str | None = Field(None) + value: str | None = Field(default=None) + description: str | None = Field(default=None) + variant: str | None = Field(default=None) class AgentContextValueSetting(BaseCfg): @@ -247,6 +276,30 @@ class AgentContextValueSetting(BaseCfg): value: Any = Field(...) +class DeepRagCitationModeSetting(BaseCfg): + """DeepRAG citation mode setting model.""" + + value: CitationMode = Field(...) + + +class DeepRagFileExtensionSetting(BaseCfg): + """DeepRAG file extension setting model.""" + + value: DeepRagFileExtension = Field(...) + + +class BatchTransformFileExtensionSetting(BaseCfg): + """Batch Transform file extension setting model.""" + + value: BatchTransformFileExtension = Field(...) + + +class BatchTransformWebSearchGroundingSetting(BaseCfg): + """DeepRAG file extension setting model.""" + + value: BatchTransformWebSearchGrounding = Field(...) + + class AgentContextOutputColumn(BaseCfg): """Agent context output column model.""" @@ -607,11 +660,64 @@ class AgentIntegrationToolProperties(BaseResourceProperties): ) -class AgentInternalToolProperties(BaseResourceProperties): - """Agent internal tool properties model.""" +class AgentInternalAnalyzeFilesToolProperties(BaseResourceProperties): + """Agent internal analyze files tool properties model.""" tool_type: Literal[AgentInternalToolType.ANALYZE_FILES] = Field( - ..., alias="toolType" + alias="toolType", default=AgentInternalToolType.ANALYZE_FILES, frozen=True + ) + + +class AgentInternalDeepRagToolProperties(BaseResourceProperties): + """Agent internal DeepRAG tool properties model.""" + + tool_type: Literal[AgentInternalToolType.DEEP_RAG] = Field( + alias="toolType", default=AgentInternalToolType.DEEP_RAG, frozen=True + ) + settings: AgentInternalDeepRagSettings = Field(..., alias="settings") + + +class AgentInternalBatchTransformToolProperties(BaseResourceProperties): + """Agent internal Batch Tranform tool properties model.""" + + tool_type: Literal[AgentInternalToolType.BATCH_TRANSFORM] = Field( + alias="toolType", default=AgentInternalToolType.BATCH_TRANSFORM, frozen=True + ) + settings: AgentInternalBatchTransformSettings = Field(..., alias="settings") + + +AgentInternalToolProperties = Annotated[ + Union[ + AgentInternalAnalyzeFilesToolProperties, + AgentInternalDeepRagToolProperties, + AgentInternalBatchTransformToolProperties, + ], + Field(discriminator="tool_type"), +] + + +class AgentInternalDeepRagSettings(BaseCfg): + """Agent internal DeepRAG tool settings model.""" + + context_type: str = Field(..., alias="contextType") + query: AgentContextQuerySetting = Field(...) + folder_path_prefix: AgentContextQuerySetting | None = Field(default=None, alias="folderPathPrefix") + citation_mode: DeepRagCitationModeSetting = Field(..., alias="citationMode") + file_extension: DeepRagFileExtensionSetting = Field(..., alias="fileExtension") + + +class AgentInternalBatchTransformSettings(BaseCfg): + """Agent internal Batch Transform tool settings model.""" + + context_type: str = Field(..., alias="contextType") + query: AgentContextQuerySetting = Field(...) + folder_path_prefix: AgentContextQuerySetting | None = Field(default=None, alias="folderPathPrefix") + file_extension: BatchTransformFileExtensionSetting = Field( + ..., alias="fileExtension" + ) + output_columns: List[AgentContextOutputColumn] = Field(..., alias="outputColumns") + web_search_grounding: BatchTransformWebSearchGroundingSetting = Field( + ..., alias="webSearchGrounding" ) diff --git a/src/uipath/platform/common/interrupt_models.py b/src/uipath/platform/common/interrupt_models.py index 6bf9b983a..ef9bf65c5 100644 --- a/src/uipath/platform/common/interrupt_models.py +++ b/src/uipath/platform/common/interrupt_models.py @@ -87,12 +87,14 @@ class CreateDeepRag(BaseModel): """Model representing a Deep RAG task creation.""" name: str - index_name: Annotated[str, Field(max_length=512)] + index_name: Annotated[str, Field(max_length=512)] | None = None + index_id: Annotated[str, Field(max_length=512)] | None = None prompt: Annotated[str, Field(max_length=250000)] glob_pattern: Annotated[str, Field(max_length=512, default="*")] = "**" citation_mode: CitationMode = CitationMode.SKIP index_folder_key: str | None = None index_folder_path: str | None = None + is_ephemeral_index: bool | None = None class WaitDeepRag(BaseModel): @@ -120,7 +122,8 @@ class CreateBatchTransform(BaseModel): """Model representing a Batch Transform task creation.""" name: str - index_name: str + index_name: str | None = None + index_id: Annotated[str, Field(max_length=512)] | None = None prompt: Annotated[str, Field(max_length=250000)] output_columns: list[BatchTransformOutputColumn] storage_bucket_folder_path_prefix: Annotated[str | None, Field(max_length=512)] = ( @@ -130,6 +133,7 @@ class CreateBatchTransform(BaseModel): destination_path: str index_folder_key: str | None = None index_folder_path: str | None = None + is_ephemeral_index: bool | None = None class WaitBatchTransform(BaseModel): diff --git a/src/uipath/platform/context_grounding/_context_grounding_service.py b/src/uipath/platform/context_grounding/_context_grounding_service.py index 900795ebc..88f5bfe70 100644 --- a/src/uipath/platform/context_grounding/_context_grounding_service.py +++ b/src/uipath/platform/context_grounding/_context_grounding_service.py @@ -456,9 +456,7 @@ async def create_index_async( @resource_override(resource_type="index") @traced(name="contextgrounding_create_ephemeral_index", run_type="uipath") def create_ephemeral_index( - self, - usage: EphemeralIndexUsage, - attachments: list[str], + self, usage: EphemeralIndexUsage, attachments: list[str] ) -> ContextGroundingIndex: """Create a new ephemeral context grounding index. @@ -486,9 +484,7 @@ def create_ephemeral_index( @resource_override(resource_type="index") @traced(name="contextgrounding_create_ephemeral_index", run_type="uipath") async def create_ephemeral_index_async( - self, - usage: EphemeralIndexUsage, - attachments: list[str], + self, usage: EphemeralIndexUsage, attachments: list[str] ) -> ContextGroundingIndex: """Create a new ephemeral context grounding index. @@ -578,7 +574,6 @@ async def retrieve_deep_rag_async( def start_batch_transform( self, name: str, - index_name: str, prompt: Annotated[str, Field(max_length=250000)], output_columns: list[BatchTransformOutputColumn], storage_bucket_folder_path_prefix: Annotated[ @@ -586,6 +581,8 @@ def start_batch_transform( ] = None, target_file_name: Annotated[str | None, Field(max_length=512)] = None, enable_web_search_grounding: bool = False, + index_name: str | None = None, + index_id: Annotated[str, Field(max_length=512)] | None = None, folder_key: str | None = None, folder_path: str | None = None, ) -> BatchTransformCreationResponse: @@ -606,20 +603,21 @@ def start_batch_transform( If only target_file_name is provided, it will be used directly. Only one file can be processed per batch transform job. enable_web_search_grounding (Optional[bool]): Whether to enable web search. Defaults to False. + index_id (str): The id of the context index to search in, used in place of name if present folder_key (str, optional): The folder key where the index resides. Defaults to None. folder_path (str, optional): The folder path where the index resides. Defaults to None. Returns: BatchTransformCreationResponse: The batch transform task creation response. """ - index = self.retrieve( - index_name, folder_key=folder_key, folder_path=folder_path - ) - if index and index.in_progress_ingestion(): - raise IngestionInProgressException(index_name=index_name) + if not index_id: + index = self.retrieve( + index_name, folder_key=folder_key, folder_path=folder_path + ) + index_id = index.id spec = self._batch_transform_creation_spec( - index_id=index.id, + index_id=index_id, name=name, storage_bucket_folder_path_prefix=storage_bucket_folder_path_prefix, target_file_name=target_file_name, @@ -644,7 +642,6 @@ def start_batch_transform( async def start_batch_transform_async( self, name: str, - index_name: str, prompt: Annotated[str, Field(max_length=250000)], output_columns: list[BatchTransformOutputColumn], storage_bucket_folder_path_prefix: Annotated[ @@ -652,6 +649,8 @@ async def start_batch_transform_async( ] = None, target_file_name: Annotated[str | None, Field(max_length=512)] = None, enable_web_search_grounding: bool = False, + index_name: str | None = None, + index_id: Annotated[str, Field(max_length=512)] | None = None, folder_key: str | None = None, folder_path: str | None = None, ) -> BatchTransformCreationResponse: @@ -672,20 +671,21 @@ async def start_batch_transform_async( If only target_file_name is provided, it will be used directly. Only one file can be processed per batch transform job. enable_web_search_grounding (Optional[bool]): Whether to enable web search. Defaults to False. + index_id (str): The id of the context index to search in, used in place of name if present folder_key (str, optional): The folder key where the index resides. Defaults to None. folder_path (str, optional): The folder path where the index resides. Defaults to None. Returns: BatchTransformCreationResponse: The batch transform task creation response. """ - index = await self.retrieve_async( - index_name, folder_key=folder_key, folder_path=folder_path - ) - if index and index.in_progress_ingestion(): - raise IngestionInProgressException(index_name=index_name) + if not index_id: + index = await self.retrieve_async( + index_name, folder_key=folder_key, folder_path=folder_path + ) + index_id = index.id spec = self._batch_transform_creation_spec( - index_id=index.id, + index_id=index_id, name=name, storage_bucket_folder_path_prefix=storage_bucket_folder_path_prefix, target_file_name=target_file_name, @@ -705,6 +705,52 @@ async def start_batch_transform_async( ) return BatchTransformCreationResponse.model_validate(response.json()) + @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_start_batch_transform_async", run_type="uipath") + async def start_batch_transform_ephemeral_async( + self, + name: str, + prompt: Annotated[str, Field(max_length=250000)], + output_columns: list[BatchTransformOutputColumn], + storage_bucket_folder_path_prefix: Annotated[ + str | None, Field(max_length=512) + ] = None, + enable_web_search_grounding: bool = False, + index_id: Annotated[str, Field(max_length=512)] | None = None, + ) -> BatchTransformCreationResponse: + """Asynchronously starts a Batch Transform, task on the targeted index. + + Batch Transform tasks are processing and transforming csv files from the index. + + Args: + name (str): The name of the Deep RAG task. + prompt (str): Describe the task: what to research, what to synthesize. + output_columns (list[BatchTransformOutputColumn]): The output columns to add into the csv. + storage_bucket_folder_path_prefix (str): The prefix pattern for filtering files in the storage bucket. Use "*" to include all files. Defaults to "*". + enable_web_search_grounding (Optional[bool]): Whether to enable web search. Defaults to False. + index_id (str): The id of the context index to search in, used in place of name if present + + Returns: + BatchTransformCreationResponse: The batch transform task creation response. + """ + spec = self._batch_transform_ephemeral_creation_spec( + index_id=index_id, + name=name, + storage_bucket_folder_path_prefix=storage_bucket_folder_path_prefix, + prompt=prompt, + output_columns=output_columns, + enable_web_search_grounding=enable_web_search_grounding, + ) + + response = await self.request_async( + spec.method, + spec.endpoint, + json=spec.json, + params=spec.params, + headers=spec.headers, + ) + return BatchTransformCreationResponse.model_validate(response.json()) + @resource_override(resource_type="index", resource_identifier="index_name") @traced(name="contextgrounding_retrieve_batch_transform", run_type="uipath") def retrieve_batch_transform( @@ -880,10 +926,11 @@ async def download_batch_transform_result_async( def start_deep_rag( self, name: str, - index_name: Annotated[str, Field(max_length=512)], prompt: Annotated[str, Field(max_length=250000)], glob_pattern: Annotated[str, Field(max_length=512, default="*")] = "**", citation_mode: CitationMode = CitationMode.SKIP, + index_name: Annotated[str, Field(max_length=512)] | None = None, + index_id: Annotated[str, Field(max_length=512)] | None = None, folder_key: str | None = None, folder_path: str | None = None, ) -> DeepRagCreationResponse: @@ -897,18 +944,19 @@ def start_deep_rag( citation_mode (CitationMode): The citation mode to use. Defaults to SKIP. folder_key (str, optional): The folder key where the index resides. Defaults to None. folder_path (str, optional): The folder path where the index resides. Defaults to None. + index_id (str): The id of the context index to search in, used in place of name if present Returns: DeepRagCreationResponse: The Deep RAG task creation response. """ - index = self.retrieve( - index_name, folder_key=folder_key, folder_path=folder_path - ) - if index and index.in_progress_ingestion(): - raise IngestionInProgressException(index_name=index_name) + if not index_id: + index = self.retrieve( + index_name, folder_key=folder_key, folder_path=folder_path + ) + index_id = index.id spec = self._deep_rag_creation_spec( - index_id=index.id, + index_id=index_id, name=name, glob_pattern=glob_pattern, prompt=prompt, @@ -932,10 +980,11 @@ def start_deep_rag( async def start_deep_rag_async( self, name: str, - index_name: Annotated[str, Field(max_length=512)], prompt: Annotated[str, Field(max_length=250000)], glob_pattern: Annotated[str, Field(max_length=512, default="*")] = "**", citation_mode: CitationMode = CitationMode.SKIP, + index_name: Annotated[str, Field(max_length=512)] | None = None, + index_id: Annotated[str, Field(max_length=512)] | None = None, folder_key: str | None = None, folder_path: str | None = None, ) -> DeepRagCreationResponse: @@ -950,18 +999,19 @@ async def start_deep_rag_async( citation_mode (CitationMode): The citation mode to use. Defaults to SKIP. folder_key (str, optional): The folder key where the index resides. Defaults to None. folder_path (str, optional): The folder path where the index resides. Defaults to None. + index_id (str): The id of the context index to search in, used in place of name if present Returns: DeepRagCreationResponse: The Deep RAG task creation response. """ - index = await self.retrieve_async( - index_name, folder_key=folder_key, folder_path=folder_path - ) - if index and index.in_progress_ingestion(): - raise IngestionInProgressException(index_name=index_name) + if not index_id: + index = await self.retrieve_async( + index_name, folder_key=folder_key, folder_path=folder_path + ) + index_id = index.id spec = self._deep_rag_creation_spec( - index_id=index.id, + index_id=index_id, name=name, glob_pattern=glob_pattern, prompt=prompt, @@ -980,6 +1030,47 @@ async def start_deep_rag_async( return DeepRagCreationResponse.model_validate(response.json()) + @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_start_deep_rag_async", run_type="uipath") + async def start_deep_rag_ephemeral_async( + self, + name: str, + prompt: Annotated[str, Field(max_length=250000)], + glob_pattern: Annotated[str, Field(max_length=512, default="*")] = "**", + citation_mode: CitationMode = CitationMode.SKIP, + index_id: Annotated[str, Field(max_length=512)] | None = None, + ) -> DeepRagCreationResponse: + """Asynchronously starts a Deep RAG task on the targeted index. + + Args: + name (str): The name of the Deep RAG task. + name (str): The name of the Deep RAG task. + prompt (str): Describe the task: what to research across documents, what to synthesize and how to cite sources. + glob_pattern (str): The glob pattern to search in the index. Defaults to "**". + citation_mode (CitationMode): The citation mode to use. Defaults to SKIP. + index_id (str): The id of the context index to search in, used in place of name if present + + Returns: + DeepRagCreationResponse: The Deep RAG task creation response. + """ + spec = self._deep_rag_ephemeral_creation_spec( + index_id=index_id, + name=name, + glob_pattern=glob_pattern, + prompt=prompt, + citation_mode=citation_mode, + ) + + response = await self.request_async( + spec.method, + spec.endpoint, + params=spec.params, + json=spec.json, + headers=spec.headers, + ) + + return DeepRagCreationResponse.model_validate(response.json()) + @resource_override(resource_type="index") @traced(name="contextgrounding_search", run_type="uipath") def search( @@ -1490,6 +1581,29 @@ def _deep_rag_creation_spec( }, ) + def _deep_rag_ephemeral_creation_spec( + self, + index_id: str | None, + name: str, + glob_pattern: str, + prompt: str, + citation_mode: CitationMode, + ) -> RequestSpec: + return RequestSpec( + method="POST", + endpoint=Endpoint(f"/ecs_/v2/indexes/{index_id}/createDeepRag"), + json={ + "name": name, + "prompt": prompt, + "globPattern": glob_pattern, + "citationMode": citation_mode.value, + }, + params={ + "$select": "id,lastDeepRagStatus,createdDate", + }, + headers={}, + ) + def _batch_transform_creation_spec( self, index_id: str, @@ -1537,6 +1651,32 @@ def _batch_transform_creation_spec( }, ) + def _batch_transform_ephemeral_creation_spec( + self, + index_id: str | None, + name: str, + enable_web_search_grounding: bool, + output_columns: list[BatchTransformOutputColumn], + storage_bucket_folder_path_prefix: str | None, + prompt: str, + ) -> RequestSpec: + return RequestSpec( + method="POST", + endpoint=Endpoint(f"/ecs_/v2/indexes/{index_id}/createBatchRag"), + json={ + "name": name, + "prompt": prompt, + "targetFileGlobPattern": f"{storage_bucket_folder_path_prefix}/*" + if storage_bucket_folder_path_prefix + else "**", + "useWebSearchGrounding": enable_web_search_grounding, + "outputColumns": [ + column.model_dump(by_alias=True) for column in output_columns + ], + }, + headers={}, + ) + def _deep_rag_retrieve_spec( self, id: str, diff --git a/src/uipath/platform/resume_triggers/_protocol.py b/src/uipath/platform/resume_triggers/_protocol.py index 2ed95f05b..8876e9bf5 100644 --- a/src/uipath/platform/resume_triggers/_protocol.py +++ b/src/uipath/platform/resume_triggers/_protocol.py @@ -574,15 +574,28 @@ async def _handle_deep_rag_job_trigger( resume_trigger.item_key = value.deep_rag.id elif isinstance(value, CreateDeepRag): uipath = UiPath() - deep_rag = await uipath.context_grounding.start_deep_rag_async( - name=value.name, - index_name=value.index_name, - prompt=value.prompt, - glob_pattern=value.glob_pattern, - citation_mode=value.citation_mode, - folder_path=value.index_folder_path, - folder_key=value.index_folder_key, - ) + if value.is_ephemeral_index: + deep_rag = ( + await uipath.context_grounding.start_deep_rag_ephemeral_async( + name=value.name, + index_id=value.index_id, + prompt=value.prompt, + glob_pattern=value.glob_pattern, + citation_mode=value.citation_mode, + ) + ) + + else: + deep_rag = await uipath.context_grounding.start_deep_rag_async( + name=value.name, + index_name=value.index_name, + index_id=value.index_id, + prompt=value.prompt, + glob_pattern=value.glob_pattern, + citation_mode=value.citation_mode, + folder_path=value.index_folder_path, + folder_key=value.index_folder_key, + ) if not deep_rag: raise Exception("Failed to start deep rag") @@ -642,16 +655,27 @@ async def _handle_batch_rag_job_trigger( resume_trigger.item_key = value.batch_transform.id elif isinstance(value, CreateBatchTransform): uipath = UiPath() - batch_transform = await uipath.context_grounding.start_batch_transform_async( - name=value.name, - index_name=value.index_name, - prompt=value.prompt, - output_columns=value.output_columns, - storage_bucket_folder_path_prefix=value.storage_bucket_folder_path_prefix, - enable_web_search_grounding=value.enable_web_search_grounding, - folder_path=value.index_folder_path, - folder_key=value.index_folder_key, - ) + if value.is_ephemeral_index: + batch_transform = await uipath.context_grounding.start_batch_transform_ephemeral_async( + name=value.name, + index_id=value.index_id, + prompt=value.prompt, + output_columns=value.output_columns, + storage_bucket_folder_path_prefix=value.storage_bucket_folder_path_prefix, + enable_web_search_grounding=value.enable_web_search_grounding, + ) + else: + batch_transform = await uipath.context_grounding.start_batch_transform_async( + name=value.name, + index_name=value.index_name, + index_id=value.index_id, + prompt=value.prompt, + output_columns=value.output_columns, + storage_bucket_folder_path_prefix=value.storage_bucket_folder_path_prefix, + enable_web_search_grounding=value.enable_web_search_grounding, + folder_path=value.index_folder_path, + folder_key=value.index_folder_key, + ) if not batch_transform: raise Exception("Failed to start batch transform") diff --git a/tests/cli/test_hitl.py b/tests/cli/test_hitl.py index 45464b891..00dfcaafb 100644 --- a/tests/cli/test_hitl.py +++ b/tests/cli/test_hitl.py @@ -1027,6 +1027,7 @@ async def test_create_resume_trigger_create_deep_rag( mock_start_deep_rag_async.assert_called_once_with( name=create_deep_rag.name, index_name=create_deep_rag.index_name, + index_id=None, prompt=create_deep_rag.prompt, glob_pattern=create_deep_rag.glob_pattern, citation_mode=create_deep_rag.citation_mode, @@ -1093,6 +1094,7 @@ async def test_create_resume_trigger_create_batch_transform( mock_start_batch_transform.assert_called_once_with( name=create_batch_transform.name, index_name=create_batch_transform.index_name, + index_id=None, prompt=create_batch_transform.prompt, output_columns=create_batch_transform.output_columns, storage_bucket_folder_path_prefix=create_batch_transform.storage_bucket_folder_path_prefix, diff --git a/tests/sdk/services/test_context_grounding_service.py b/tests/sdk/services/test_context_grounding_service.py index 46d730584..2e78c20ea 100644 --- a/tests/sdk/services/test_context_grounding_service.py +++ b/tests/sdk/services/test_context_grounding_service.py @@ -2302,6 +2302,64 @@ async def test_download_batch_transform_result_async_creates_nested_directories( assert destination.read_bytes() == b"col1,col2\nval1,val2" assert destination.parent.exists() + @pytest.mark.anyio + async def test_start_deep_rag_ephemeral_async( + self, + httpx_mock: HTTPXMock, + service: ContextGroundingService, + base_url: str, + org: str, + tenant: str, + version: str, + ) -> None: + httpx_mock.add_response( + url=f"{base_url}{org}{tenant}/ecs_/v2/indexes/ephemeral-index-id/createDeepRag?$select=id,lastDeepRagStatus,createdDate", + status_code=200, + json={ + "id": "new-deep-rag-task-id", + "lastDeepRagStatus": "Queued", + "createdDate": "2024-01-15T10:30:00Z", + }, + ) + + response = await service.start_deep_rag_ephemeral_async( + name="my-ephemeral-deep-rag-task", + prompt="Summarize all documents related to financial reports", + glob_pattern="*.pdf", + citation_mode=CitationMode.INLINE, + index_id="ephemeral-index-id", + ) + + assert isinstance(response, DeepRagCreationResponse) + assert response.id == "new-deep-rag-task-id" + assert response.last_deep_rag_status == "Queued" + assert response.created_date == "2024-01-15T10:30:00Z" + + sent_requests = httpx_mock.get_requests() + if sent_requests is None: + raise Exception("No request was sent") + + assert sent_requests[0].method == "POST" + assert ( + f"{base_url}{org}{tenant}/ecs_/v2/indexes/ephemeral-index-id/createDeepRag" + in str(sent_requests[0].url) + ) + + request_data = json.loads(sent_requests[0].content) + assert request_data["name"] == "my-ephemeral-deep-rag-task" + assert ( + request_data["prompt"] + == "Summarize all documents related to financial reports" + ) + assert request_data["globPattern"] == "*.pdf" + assert request_data["citationMode"] == "Inline" + + assert HEADER_USER_AGENT in sent_requests[0].headers + assert ( + sent_requests[0].headers[HEADER_USER_AGENT] + == f"UiPath.Python.Sdk/UiPath.Python.Sdk.Activities.ContextGroundingService.start_deep_rag_ephemeral_async/{version}" + ) + @pytest.mark.anyio async def test_download_batch_transform_result_async_encrypted( self, From 0ce669a3faaca2dd8962339ef89123832c2bc3c4 Mon Sep 17 00:00:00 2001 From: Caleb Martin Date: Fri, 30 Jan 2026 15:10:43 -0800 Subject: [PATCH 2/4] fix: comment fixes --- .../platform/common/interrupt_models.py | 14 +++ .../_context_grounding_service.py | 115 ++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/src/uipath/platform/common/interrupt_models.py b/src/uipath/platform/common/interrupt_models.py index ef9bf65c5..d2d3fd130 100644 --- a/src/uipath/platform/common/interrupt_models.py +++ b/src/uipath/platform/common/interrupt_models.py @@ -96,6 +96,13 @@ class CreateDeepRag(BaseModel): index_folder_path: str | None = None is_ephemeral_index: bool | None = None + @model_validator(mode="after") + def validate_ephemeral_index_requires_index_id(self) -> "CreateBatchTransform": + """Validate that if it is an ephemeral index that it is using index id.""" + if self.is_ephemeral_index is True and self.index_id is None: + raise ValueError("Index id must be provided for an ephemeral index") + return self + class WaitDeepRag(BaseModel): """Model representing a wait Deep RAG task.""" @@ -135,6 +142,13 @@ class CreateBatchTransform(BaseModel): index_folder_path: str | None = None is_ephemeral_index: bool | None = None + @model_validator(mode="after") + def validate_ephemeral_index_requires_index_id(self) -> "CreateBatchTransform": + """Validate that if it is an ephemeral index that it is using index id.""" + if self.is_ephemeral_index is True and self.index_id is None: + raise ValueError("Index id must be provided for an ephemeral index") + return self + class WaitBatchTransform(BaseModel): """Model representing a wait Batch Transform task.""" diff --git a/src/uipath/platform/context_grounding/_context_grounding_service.py b/src/uipath/platform/context_grounding/_context_grounding_service.py index 88f5bfe70..11ad14f30 100644 --- a/src/uipath/platform/context_grounding/_context_grounding_service.py +++ b/src/uipath/platform/context_grounding/_context_grounding_service.py @@ -610,10 +610,17 @@ def start_batch_transform( Returns: BatchTransformCreationResponse: The batch transform task creation response. """ + if not (index_name or index_id): + raise ValueError("Index name or id is required") + if index_name and index_id: + raise ValueError("Index name or id are mutually exclusive") + if not index_id: index = self.retrieve( index_name, folder_key=folder_key, folder_path=folder_path ) + if index and index.in_progress_ingestion(): + raise IngestionInProgressException(index_name=index_name) index_id = index.id spec = self._batch_transform_creation_spec( @@ -678,10 +685,17 @@ async def start_batch_transform_async( Returns: BatchTransformCreationResponse: The batch transform task creation response. """ + if not (index_name or index_id): + raise ValueError("Index name or id is required") + if index_name and index_id: + raise ValueError("Index name or id are mutually exclusive") + if not index_id: index = await self.retrieve_async( index_name, folder_key=folder_key, folder_path=folder_path ) + if index and index.in_progress_ingestion(): + raise IngestionInProgressException(index_name=index_name) index_id = index.id spec = self._batch_transform_creation_spec( @@ -705,6 +719,52 @@ async def start_batch_transform_async( ) return BatchTransformCreationResponse.model_validate(response.json()) + @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_start_batch_transform", run_type="uipath") + async def start_batch_transform_ephemeral( + self, + name: str, + prompt: Annotated[str, Field(max_length=250000)], + output_columns: list[BatchTransformOutputColumn], + storage_bucket_folder_path_prefix: Annotated[ + str | None, Field(max_length=512) + ] = None, + enable_web_search_grounding: bool = False, + index_id: Annotated[str, Field(max_length=512)] | None = None, + ) -> BatchTransformCreationResponse: + """Asynchronously starts a Batch Transform, task on the targeted index. + + Batch Transform tasks are processing and transforming csv files from the index. + + Args: + name (str): The name of the Deep RAG task. + prompt (str): Describe the task: what to research, what to synthesize. + output_columns (list[BatchTransformOutputColumn]): The output columns to add into the csv. + storage_bucket_folder_path_prefix (str): The prefix pattern for filtering files in the storage bucket. Use "*" to include all files. Defaults to "*". + enable_web_search_grounding (Optional[bool]): Whether to enable web search. Defaults to False. + index_id (str): The id of the context index to search in, used in place of name if present + + Returns: + BatchTransformCreationResponse: The batch transform task creation response. + """ + spec = self._batch_transform_ephemeral_creation_spec( + index_id=index_id, + name=name, + storage_bucket_folder_path_prefix=storage_bucket_folder_path_prefix, + prompt=prompt, + output_columns=output_columns, + enable_web_search_grounding=enable_web_search_grounding, + ) + + response = self.request( + spec.method, + spec.endpoint, + json=spec.json, + params=spec.params, + headers=spec.headers, + ) + return BatchTransformCreationResponse.model_validate(response.json()) + @resource_override(resource_type="index", resource_identifier="index_name") @traced(name="contextgrounding_start_batch_transform_async", run_type="uipath") async def start_batch_transform_ephemeral_async( @@ -949,10 +1009,17 @@ def start_deep_rag( Returns: DeepRagCreationResponse: The Deep RAG task creation response. """ + if not (index_name or index_id): + raise ValueError("Index name or id is required") + if index_name and index_id: + raise ValueError("Index name or id are mutually exclusive") + if not index_id: index = self.retrieve( index_name, folder_key=folder_key, folder_path=folder_path ) + if index and index.in_progress_ingestion(): + raise IngestionInProgressException(index_name=index_name) index_id = index.id spec = self._deep_rag_creation_spec( @@ -1004,10 +1071,17 @@ async def start_deep_rag_async( Returns: DeepRagCreationResponse: The Deep RAG task creation response. """ + if not (index_name or index_id): + raise ValueError("Index name or id is required") + if index_name and index_id: + raise ValueError("Index name or id are mutually exclusive") + if not index_id: index = await self.retrieve_async( index_name, folder_key=folder_key, folder_path=folder_path ) + if index and index.in_progress_ingestion(): + raise IngestionInProgressException(index_name=index_name) index_id = index.id spec = self._deep_rag_creation_spec( @@ -1030,6 +1104,47 @@ async def start_deep_rag_async( return DeepRagCreationResponse.model_validate(response.json()) + @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_start_deep_rag", run_type="uipath") + async def start_deep_rag_ephemeral( + self, + name: str, + prompt: Annotated[str, Field(max_length=250000)], + glob_pattern: Annotated[str, Field(max_length=512, default="*")] = "**", + citation_mode: CitationMode = CitationMode.SKIP, + index_id: Annotated[str, Field(max_length=512)] | None = None, + ) -> DeepRagCreationResponse: + """Asynchronously starts a Deep RAG task on the targeted index. + + Args: + name (str): The name of the Deep RAG task. + name (str): The name of the Deep RAG task. + prompt (str): Describe the task: what to research across documents, what to synthesize and how to cite sources. + glob_pattern (str): The glob pattern to search in the index. Defaults to "**". + citation_mode (CitationMode): The citation mode to use. Defaults to SKIP. + index_id (str): The id of the context index to search in, used in place of name if present + + Returns: + DeepRagCreationResponse: The Deep RAG task creation response. + """ + spec = self._deep_rag_ephemeral_creation_spec( + index_id=index_id, + name=name, + glob_pattern=glob_pattern, + prompt=prompt, + citation_mode=citation_mode, + ) + + response = self.request( + spec.method, + spec.endpoint, + params=spec.params, + json=spec.json, + headers=spec.headers, + ) + + return DeepRagCreationResponse.model_validate(response.json()) + @resource_override(resource_type="index", resource_identifier="index_name") @traced(name="contextgrounding_start_deep_rag_async", run_type="uipath") async def start_deep_rag_ephemeral_async( From 7a6ddc5a1f4dbd8d6a04971782e917a4ab3eb15a Mon Sep 17 00:00:00 2001 From: Caleb Martin Date: Tue, 3 Feb 2026 11:24:46 -0800 Subject: [PATCH 3/4] fix: type --- src/uipath/platform/common/interrupt_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uipath/platform/common/interrupt_models.py b/src/uipath/platform/common/interrupt_models.py index d2d3fd130..2b6e13402 100644 --- a/src/uipath/platform/common/interrupt_models.py +++ b/src/uipath/platform/common/interrupt_models.py @@ -97,7 +97,7 @@ class CreateDeepRag(BaseModel): is_ephemeral_index: bool | None = None @model_validator(mode="after") - def validate_ephemeral_index_requires_index_id(self) -> "CreateBatchTransform": + def validate_ephemeral_index_requires_index_id(self) -> "CreateDeepRag": """Validate that if it is an ephemeral index that it is using index id.""" if self.is_ephemeral_index is True and self.index_id is None: raise ValueError("Index id must be provided for an ephemeral index") From a32df07dfd22b985f0c52114a9a7f4ef84028d11 Mon Sep 17 00:00:00 2001 From: Caleb Martin Date: Tue, 3 Feb 2026 11:30:14 -0800 Subject: [PATCH 4/4] fix: bump version --- pyproject.toml | 2 +- src/uipath/agent/models/agent.py | 8 ++++++-- uv.lock | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c4da9a00e..e9cf17fea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.6.31" +version = "2.6.32" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/src/uipath/agent/models/agent.py b/src/uipath/agent/models/agent.py index aca323f86..2941577d3 100644 --- a/src/uipath/agent/models/agent.py +++ b/src/uipath/agent/models/agent.py @@ -701,7 +701,9 @@ class AgentInternalDeepRagSettings(BaseCfg): context_type: str = Field(..., alias="contextType") query: AgentContextQuerySetting = Field(...) - folder_path_prefix: AgentContextQuerySetting | None = Field(default=None, alias="folderPathPrefix") + folder_path_prefix: AgentContextQuerySetting | None = Field( + default=None, alias="folderPathPrefix" + ) citation_mode: DeepRagCitationModeSetting = Field(..., alias="citationMode") file_extension: DeepRagFileExtensionSetting = Field(..., alias="fileExtension") @@ -711,7 +713,9 @@ class AgentInternalBatchTransformSettings(BaseCfg): context_type: str = Field(..., alias="contextType") query: AgentContextQuerySetting = Field(...) - folder_path_prefix: AgentContextQuerySetting | None = Field(default=None, alias="folderPathPrefix") + folder_path_prefix: AgentContextQuerySetting | None = Field( + default=None, alias="folderPathPrefix" + ) file_extension: BatchTransformFileExtensionSetting = Field( ..., alias="fileExtension" ) diff --git a/uv.lock b/uv.lock index 00ee127ee..9e17eb58f 100644 --- a/uv.lock +++ b/uv.lock @@ -2491,7 +2491,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.6.31" +version = "2.6.32" source = { editable = "." } dependencies = [ { name = "applicationinsights" },