Skip to content

Commit 841e97e

Browse files
cfauchereClement Fauchereclaude
authored
feat(context-grounding): add start_batch_transform_from_attachment primitive (#1920)
Co-authored-by: Clement Fauchere <[email protected]> Co-authored-by: Claude Opus 4.7 <[email protected]>
1 parent 80d6735 commit 841e97e

10 files changed

Lines changed: 491 additions & 18 deletions

File tree

‎packages/uipath-platform/pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-platform"
3-
version = "0.2.32"
3+
version = "0.2.33"
44
description = "HTTP client library for programmatic access to UiPath Platform"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"

‎packages/uipath-platform/src/uipath/platform/common/interrupt_models.py‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,24 @@ class CreateBatchTransform(BaseModel):
196196
index_folder_key: str | None = None
197197
index_folder_path: str | None = None
198198
is_ephemeral_index: bool | None = None
199+
attachment: str | None = None
199200

200201
@model_validator(mode="after")
201202
def validate_ephemeral_index_requires_index_id(self) -> "CreateBatchTransform":
202203
"""Validate that if it is an ephemeral index that it is using index id."""
203-
if self.is_ephemeral_index is True and self.index_id is None:
204-
raise ValueError("Index id must be provided for an ephemeral index")
204+
if (
205+
self.is_ephemeral_index is True
206+
and self.index_id is None
207+
and not self.attachment
208+
):
209+
raise ValueError(
210+
"Index id must be provided for an ephemeral index (or use attachment)"
211+
)
212+
if self.attachment is not None:
213+
if self.index_id is not None or self.index_name is not None:
214+
raise ValueError(
215+
"attachment cannot be combined with index_id or index_name"
216+
)
205217
return self
206218

207219

‎packages/uipath-platform/src/uipath/platform/context_grounding/_context_grounding_service.py‎

Lines changed: 172 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,7 @@ def start_batch_transform(
823823
] = None,
824824
target_file_name: Annotated[str | None, Field(max_length=512)] = None,
825825
enable_web_search_grounding: bool = False,
826+
exclude_domains: Optional[List[str]] = None,
826827
index_name: str | None = None,
827828
index_id: Annotated[str, Field(max_length=512)] | None = None,
828829
folder_key: str | None = None,
@@ -845,6 +846,8 @@ def start_batch_transform(
845846
If only target_file_name is provided, it will be used directly.
846847
Only one file can be processed per batch transform job.
847848
enable_web_search_grounding (Optional[bool]): Whether to enable web search. Defaults to False.
849+
exclude_domains (list[str], optional): Domains to exclude from web search grounding.
850+
Requires enable_web_search_grounding=True; the server rejects the combination otherwise.
848851
index_id (str): The id of the context index to search in, used in place of name if present
849852
folder_key (str, optional): The folder key where the index resides. Defaults to None.
850853
folder_path (str, optional): The folder path where the index resides. Defaults to None.
@@ -874,6 +877,7 @@ def start_batch_transform(
874877
prompt=prompt,
875878
output_columns=output_columns,
876879
enable_web_search_grounding=enable_web_search_grounding,
880+
exclude_domains=exclude_domains,
877881
folder_key=folder_key,
878882
folder_path=folder_path,
879883
)
@@ -899,6 +903,7 @@ async def start_batch_transform_async(
899903
] = None,
900904
target_file_name: Annotated[str | None, Field(max_length=512)] = None,
901905
enable_web_search_grounding: bool = False,
906+
exclude_domains: Optional[List[str]] = None,
902907
index_name: str | None = None,
903908
index_id: Annotated[str, Field(max_length=512)] | None = None,
904909
folder_key: str | None = None,
@@ -921,6 +926,8 @@ async def start_batch_transform_async(
921926
If only target_file_name is provided, it will be used directly.
922927
Only one file can be processed per batch transform job.
923928
enable_web_search_grounding (Optional[bool]): Whether to enable web search. Defaults to False.
929+
exclude_domains (list[str], optional): Domains to exclude from web search grounding.
930+
Requires enable_web_search_grounding=True; the server rejects the combination otherwise.
924931
index_id (str): The id of the context index to search in, used in place of name if present
925932
folder_key (str, optional): The folder key where the index resides. Defaults to None.
926933
folder_path (str, optional): The folder path where the index resides. Defaults to None.
@@ -950,6 +957,7 @@ async def start_batch_transform_async(
950957
prompt=prompt,
951958
output_columns=output_columns,
952959
enable_web_search_grounding=enable_web_search_grounding,
960+
exclude_domains=exclude_domains,
953961
folder_key=folder_key,
954962
folder_path=folder_path,
955963
)
@@ -963,6 +971,9 @@ async def start_batch_transform_async(
963971
)
964972
return BatchTransformCreationResponse.model_validate(response.json())
965973

974+
@deprecated(
975+
"Use start_batch_transform_from_attachment instead — one call, no separate index step."
976+
)
966977
@resource_override(resource_type="index", resource_identifier="index_name")
967978
@traced(name="contextgrounding_start_batch_transform", run_type="uipath")
968979
async def start_batch_transform_ephemeral(
@@ -1009,6 +1020,9 @@ async def start_batch_transform_ephemeral(
10091020
)
10101021
return BatchTransformCreationResponse.model_validate(response.json())
10111022

1023+
@deprecated(
1024+
"Use start_batch_transform_from_attachment_async instead — one call, no separate index step."
1025+
)
10121026
@resource_override(resource_type="index", resource_identifier="index_name")
10131027
@traced(name="contextgrounding_start_batch_transform_async", run_type="uipath")
10141028
async def start_batch_transform_ephemeral_async(
@@ -1055,6 +1069,115 @@ async def start_batch_transform_ephemeral_async(
10551069
)
10561070
return BatchTransformCreationResponse.model_validate(response.json())
10571071

1072+
@traced(
1073+
name="contextgrounding_start_batch_transform_from_attachment", run_type="uipath"
1074+
)
1075+
def start_batch_transform_from_attachment(
1076+
self,
1077+
name: str,
1078+
prompt: Annotated[str, Field(max_length=250000)],
1079+
output_columns: List[BatchTransformOutputColumn],
1080+
attachment: str,
1081+
enable_web_search_grounding: bool = False,
1082+
exclude_domains: Optional[List[str]] = None,
1083+
folder_key: str | None = None,
1084+
folder_path: str | None = None,
1085+
) -> BatchTransformCreationResponse:
1086+
"""Starts a Batch Transform task from a single attachment in one server call.
1087+
1088+
The server creates the ephemeral index from the attachment and starts the Batch Transform
1089+
task in one operation; the caller does not need to create the index separately.
1090+
1091+
Args:
1092+
name (str): The name of the Batch Transform task.
1093+
prompt (str): Describe the task: what to transform and how to output.
1094+
output_columns (list[BatchTransformOutputColumn]): The output columns to add into the csv.
1095+
attachment (str): The attachment id to transform. Exactly one attachment is supported today.
1096+
enable_web_search_grounding (bool): Whether to enable web search. Defaults to False.
1097+
exclude_domains (list[str], optional): Domains to exclude from web search grounding.
1098+
Requires enable_web_search_grounding=True; the server rejects the combination otherwise.
1099+
folder_key (str, optional): The folder key context. Defaults to None.
1100+
folder_path (str, optional): The folder path context. Defaults to None.
1101+
1102+
Returns:
1103+
BatchTransformCreationResponse: The Batch Transform task creation response.
1104+
"""
1105+
spec = self._batch_transform_from_attachment_creation_spec(
1106+
name=name,
1107+
prompt=prompt,
1108+
output_columns=output_columns,
1109+
attachment=attachment,
1110+
enable_web_search_grounding=enable_web_search_grounding,
1111+
exclude_domains=exclude_domains,
1112+
folder_key=folder_key,
1113+
folder_path=folder_path,
1114+
)
1115+
1116+
response = self.request(
1117+
spec.method,
1118+
spec.endpoint,
1119+
params=spec.params,
1120+
json=spec.json,
1121+
headers=spec.headers,
1122+
)
1123+
1124+
return BatchTransformCreationResponse.model_validate(response.json())
1125+
1126+
@traced(
1127+
name="contextgrounding_start_batch_transform_from_attachment_async",
1128+
run_type="uipath",
1129+
)
1130+
async def start_batch_transform_from_attachment_async(
1131+
self,
1132+
name: str,
1133+
prompt: Annotated[str, Field(max_length=250000)],
1134+
output_columns: List[BatchTransformOutputColumn],
1135+
attachment: str,
1136+
enable_web_search_grounding: bool = False,
1137+
exclude_domains: Optional[List[str]] = None,
1138+
folder_key: str | None = None,
1139+
folder_path: str | None = None,
1140+
) -> BatchTransformCreationResponse:
1141+
"""Asynchronously starts a Batch Transform task from a single attachment in one server call.
1142+
1143+
The server creates the ephemeral index from the attachment and starts the Batch Transform
1144+
task in one operation; the caller does not need to create the index separately.
1145+
1146+
Args:
1147+
name (str): The name of the Batch Transform task.
1148+
prompt (str): Describe the task: what to transform and how to output.
1149+
output_columns (list[BatchTransformOutputColumn]): The output columns to add into the csv.
1150+
attachment (str): The attachment id to transform. Exactly one attachment is supported today.
1151+
enable_web_search_grounding (bool): Whether to enable web search. Defaults to False.
1152+
exclude_domains (list[str], optional): Domains to exclude from web search grounding.
1153+
Requires enable_web_search_grounding=True; the server rejects the combination otherwise.
1154+
folder_key (str, optional): The folder key context. Defaults to None.
1155+
folder_path (str, optional): The folder path context. Defaults to None.
1156+
1157+
Returns:
1158+
BatchTransformCreationResponse: The Batch Transform task creation response.
1159+
"""
1160+
spec = self._batch_transform_from_attachment_creation_spec(
1161+
name=name,
1162+
prompt=prompt,
1163+
output_columns=output_columns,
1164+
attachment=attachment,
1165+
enable_web_search_grounding=enable_web_search_grounding,
1166+
exclude_domains=exclude_domains,
1167+
folder_key=folder_key,
1168+
folder_path=folder_path,
1169+
)
1170+
1171+
response = await self.request_async(
1172+
spec.method,
1173+
spec.endpoint,
1174+
params=spec.params,
1175+
json=spec.json,
1176+
headers=spec.headers,
1177+
)
1178+
1179+
return BatchTransformCreationResponse.model_validate(response.json())
1180+
10581181
@resource_override(resource_type="index", resource_identifier="index_name")
10591182
@traced(name="contextgrounding_retrieve_batch_transform", run_type="uipath")
10601183
def retrieve_batch_transform(
@@ -2565,6 +2688,7 @@ def _batch_transform_creation_spec(
25652688
storage_bucket_folder_path_prefix: str | None,
25662689
target_file_name: str | None,
25672690
prompt: str,
2691+
exclude_domains: Optional[List[str]] = None,
25682692
folder_key: str | None = None,
25692693
folder_path: str | None = None,
25702694
) -> RequestSpec:
@@ -2589,18 +2713,22 @@ def _batch_transform_creation_spec(
25892713
else:
25902714
target_file_glob_pattern = "**"
25912715

2716+
body: dict[str, object] = {
2717+
"name": name,
2718+
"prompt": prompt,
2719+
"targetFileGlobPattern": target_file_glob_pattern,
2720+
"useWebSearchGrounding": enable_web_search_grounding,
2721+
"outputColumns": [
2722+
column.model_dump(by_alias=True) for column in output_columns
2723+
],
2724+
}
2725+
if exclude_domains:
2726+
body["excludeDomains"] = exclude_domains
2727+
25922728
return RequestSpec(
25932729
method="POST",
25942730
endpoint=Endpoint(f"/ecs_/v2/indexes/{index_id}/createBatchRag"),
2595-
json={
2596-
"name": name,
2597-
"prompt": prompt,
2598-
"targetFileGlobPattern": target_file_glob_pattern,
2599-
"useWebSearchGrounding": enable_web_search_grounding,
2600-
"outputColumns": [
2601-
column.model_dump(by_alias=True) for column in output_columns
2602-
],
2603-
},
2731+
json=body,
26042732
headers={
26052733
**header_folder(folder_key, None),
26062734
**header_job_key(),
@@ -2633,6 +2761,41 @@ def _batch_transform_ephemeral_creation_spec(
26332761
headers={**header_job_key()},
26342762
)
26352763

2764+
def _batch_transform_from_attachment_creation_spec(
2765+
self,
2766+
name: str,
2767+
prompt: str,
2768+
output_columns: List[BatchTransformOutputColumn],
2769+
attachment: str,
2770+
enable_web_search_grounding: bool,
2771+
exclude_domains: Optional[List[str]],
2772+
folder_key: str | None = None,
2773+
folder_path: str | None = None,
2774+
) -> RequestSpec:
2775+
headers: dict[str, str] = {**header_job_key()}
2776+
if folder_key is not None or folder_path is not None:
2777+
folder_key = self._resolve_folder_key(folder_key, folder_path)
2778+
headers = {**header_folder(folder_key, None), **headers}
2779+
2780+
body: dict[str, object] = {
2781+
"name": name,
2782+
"prompt": prompt,
2783+
"useWebSearchGrounding": enable_web_search_grounding,
2784+
"outputColumns": [
2785+
column.model_dump(by_alias=True) for column in output_columns
2786+
],
2787+
"attachments": [attachment],
2788+
}
2789+
if exclude_domains:
2790+
body["excludeDomains"] = exclude_domains
2791+
2792+
return RequestSpec(
2793+
method="POST",
2794+
endpoint=Endpoint("/ecs_/v2/batchrag/create"),
2795+
json=body,
2796+
headers=headers,
2797+
)
2798+
26362799
def _deep_rag_retrieve_spec(
26372800
self,
26382801
id: str,

‎packages/uipath-platform/src/uipath/platform/resume_triggers/_protocol.py‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,17 @@ async def _handle_batch_rag_job_trigger(
799799
elif isinstance(value, CreateBatchTransform):
800800
uipath = UiPath()
801801
try:
802-
if value.is_ephemeral_index:
802+
if value.attachment is not None:
803+
batch_transform = await uipath.context_grounding.start_batch_transform_from_attachment_async(
804+
name=value.name,
805+
prompt=value.prompt,
806+
output_columns=value.output_columns,
807+
attachment=value.attachment,
808+
enable_web_search_grounding=value.enable_web_search_grounding,
809+
folder_path=value.index_folder_path,
810+
folder_key=value.index_folder_key,
811+
)
812+
elif value.is_ephemeral_index:
803813
batch_transform = await uipath.context_grounding.start_batch_transform_ephemeral_async(
804814
name=value.name,
805815
index_id=value.index_id,

0 commit comments

Comments
 (0)