11"""Context tool creation for semantic index retrieval."""
22
33import uuid
4- from typing import Any , Optional
4+ from typing import Any , Dict , Optional
55
66from langchain_core .documents import Document
77from langchain_core .messages import ToolCall
88from langchain_core .tools import BaseTool , StructuredTool
9- from pydantic import BaseModel , Field , create_model
9+ from pydantic import BaseModel , Field , TypeAdapter , create_model
1010from uipath .agent .models .agent import (
1111 AgentContextResourceConfig ,
1212 AgentContextRetrievalMode ,
13+ AgentToolArgumentProperties ,
1314)
1415from uipath .eval .mocks import mockable
1516from uipath .platform import UiPath
4142from .tool_node import ToolWrapperReturnType
4243from .utils import sanitize_tool_name
4344
45+ _ARG_PROPS_ADAPTER = TypeAdapter (Dict [str , AgentToolArgumentProperties ])
46+
47+
48+ def _get_argument_properties (
49+ resource : AgentContextResourceConfig ,
50+ ) -> dict [str , AgentToolArgumentProperties ]:
51+ """Extract argumentProperties from the resource's extra fields.
52+
53+ AgentContextResourceConfig doesn't declare argument_properties yet,
54+ but BaseCfg(extra="allow") preserves the raw JSON value.
55+ """
56+ raw = (
57+ resource .model_extra .get ("argumentProperties" ) if resource .model_extra else None
58+ )
59+ if not raw :
60+ return {}
61+ return _ARG_PROPS_ADAPTER .validate_python (raw )
62+
63+
64+ def _build_folder_path_prefix_arg_props (
65+ resource : AgentContextResourceConfig ,
66+ ) -> dict [str , Any ]:
67+ """Build argument_properties for folder_path_prefix from settings.
68+
69+ Fallback for when settings bag doesn't include argumentProperties
70+ at the resource level but does set settings.folder_path_prefix
71+ with variant="argument".
72+ """
73+ assert resource .settings .folder_path_prefix is not None
74+ argument_path = (resource .settings .folder_path_prefix .value or "" ).strip ("{}" )
75+ return {
76+ "folder_path_prefix" : {
77+ "variant" : "argument" ,
78+ "argumentPath" : argument_path ,
79+ "isSensitive" : False ,
80+ }
81+ }
82+
4483
4584def is_static_query (resource : AgentContextResourceConfig ) -> bool :
4685 """Check if the resource configuration uses a static query variant."""
@@ -152,27 +191,31 @@ def handle_deep_rag(
152191 if static :
153192 assert prompt is not None
154193
155- folder_path_prefix = None
194+ static_folder_path_prefix = None
156195 if (
157196 resource .settings .folder_path_prefix
158197 and resource .settings .folder_path_prefix .value
198+ and resource .settings .folder_path_prefix .variant == "static"
159199 ):
160- folder_path_prefix = resource .settings .folder_path_prefix .value
200+ static_folder_path_prefix = resource .settings .folder_path_prefix .value
161201
162202 file_extension = None
163203 if resource .settings .file_extension and resource .settings .file_extension .value :
164204 file_extension = resource .settings .file_extension .value
165205
166- glob_pattern = build_glob_pattern (
167- folder_path_prefix = folder_path_prefix , file_extension = file_extension
168- )
169-
170206 output_model = create_model (
171207 "DeepRagOutputModel" ,
172208 __base__ = DeepRagContent ,
173209 deep_rag_id = (str , Field (alias = "deepRagId" )),
174210 )
175211
212+ arg_props = _get_argument_properties (resource )
213+
214+ has_folder_path_prefix_arg = "folder_path_prefix" in arg_props or (
215+ resource .settings .folder_path_prefix
216+ and resource .settings .folder_path_prefix .variant == "argument"
217+ )
218+
176219 schema_fields : dict [str , Any ] = (
177220 {}
178221 if static
@@ -186,6 +229,18 @@ def handle_deep_rag(
186229 ),
187230 }
188231 )
232+
233+ if has_folder_path_prefix_arg :
234+ schema_fields ["folder_path_prefix" ] = (
235+ str ,
236+ Field (
237+ default = None ,
238+ description = "The folder path prefix within the index to filter on" ,
239+ ),
240+ )
241+ if "folder_path_prefix" not in arg_props :
242+ arg_props = _build_folder_path_prefix_arg_props (resource )
243+
189244 input_model = create_model ("DeepRagInput" , ** schema_fields )
190245
191246 @mockable (
@@ -195,8 +250,14 @@ def handle_deep_rag(
195250 output_schema = output_model .model_json_schema (),
196251 example_calls = [], # Examples cannot be provided for context.
197252 )
198- async def context_tool_fn (query : Optional [str ] = None ) -> dict [str , Any ]:
253+ async def context_tool_fn (
254+ query : Optional [str ] = None , folder_path_prefix : Optional [str ] = None
255+ ) -> dict [str , Any ]:
199256 actual_prompt = prompt or query
257+ glob_pattern = build_glob_pattern (
258+ folder_path_prefix = static_folder_path_prefix or folder_path_prefix ,
259+ file_extension = file_extension ,
260+ )
200261
201262 @durable_interrupt
202263 async def create_deep_rag ():
@@ -211,12 +272,13 @@ async def create_deep_rag():
211272
212273 return await create_deep_rag ()
213274
214- return StructuredToolWithOutputType (
275+ return StructuredToolWithArgumentProperties (
215276 name = tool_name ,
216277 description = resource .description ,
217278 args_schema = input_model ,
218279 coroutine = context_tool_fn ,
219280 output_type = output_model ,
281+ argument_properties = arg_props ,
220282 metadata = {
221283 "tool_type" : "context" ,
222284 "display_name" : resource .name ,
@@ -271,15 +333,19 @@ def handle_batch_transform(
271333 if static :
272334 assert prompt is not None
273335
274- folder_path_prefix = None
336+ static_folder_path_prefix = None
275337 if (
276338 resource .settings .folder_path_prefix
277339 and resource .settings .folder_path_prefix .value
340+ and resource .settings .folder_path_prefix .variant == "static"
278341 ):
279- folder_path_prefix = resource .settings .folder_path_prefix .value
342+ static_folder_path_prefix = resource .settings .folder_path_prefix .value
280343
281- glob_pattern = build_glob_pattern (
282- folder_path_prefix = folder_path_prefix , file_extension = None
344+ arg_props = _get_argument_properties (resource )
345+
346+ has_folder_path_prefix_arg = "folder_path_prefix" in arg_props or (
347+ resource .settings .folder_path_prefix
348+ and resource .settings .folder_path_prefix .variant == "argument"
283349 )
284350
285351 output_model = create_model_from_schema (BATCH_TRANSFORM_OUTPUT_SCHEMA )
@@ -300,6 +366,16 @@ def handle_batch_transform(
300366 description = "The relative file path destination for the modified csv file" ,
301367 ),
302368 )
369+ if has_folder_path_prefix_arg :
370+ schema_fields ["folder_path_prefix" ] = (
371+ str ,
372+ Field (
373+ default = None ,
374+ description = "The folder path prefix within the index to filter on" ,
375+ ),
376+ )
377+ if "folder_path_prefix" not in arg_props :
378+ arg_props = _build_folder_path_prefix_arg_props (resource )
303379 input_model = create_model ("BatchTransformInput" , ** schema_fields )
304380
305381 @mockable (
@@ -310,9 +386,15 @@ def handle_batch_transform(
310386 example_calls = [], # Examples cannot be provided for context.
311387 )
312388 async def context_tool_fn (
313- query : Optional [str ] = None , destination_path : str = "output.csv"
389+ query : Optional [str ] = None ,
390+ destination_path : str = "output.csv" ,
391+ folder_path_prefix : Optional [str ] = None ,
314392 ) -> dict [str , Any ]:
315393 actual_prompt = prompt or query
394+ glob_pattern = build_glob_pattern (
395+ folder_path_prefix = static_folder_path_prefix or folder_path_prefix ,
396+ file_extension = None ,
397+ )
316398
317399 @durable_interrupt
318400 async def create_batch_transform ():
@@ -362,7 +444,7 @@ async def context_batch_transform_wrapper(
362444 args_schema = input_model ,
363445 coroutine = context_tool_fn ,
364446 output_type = output_model ,
365- argument_properties = {} ,
447+ argument_properties = arg_props ,
366448 metadata = {
367449 "tool_type" : "context" ,
368450 "display_name" : resource .name ,
0 commit comments