11"""Data Fabric tool creation and resource detection.
22
3- This module provides:
4- 1. An agentic ``query_datafabric`` tool with inner LLM sub-graph
5- 2. Entity schema fetching from the Data Fabric API
3+ This module provides an agentic ``query_datafabric`` tool with an inner
4+ LLM sub-graph.
65
76The tool accepts natural language queries, runs an inner LangGraph
87sub-graph for SQL generation + execution + self-correction, and
2120from langchain_core .tools import BaseTool
2221from langgraph .graph .state import CompiledStateGraph
2322from uipath .agent .models .agent import AgentContextResourceConfig
24- from uipath .platform .entities import Entity , EntityRouting , QueryRoutingOverrideContext
23+ from uipath .platform .entities import DataFabricEntityItem
2524
2625from ..base_uipath_structured_tool import BaseUiPathStructuredTool
2726from .models import DataFabricQueryInput
3433class DataFabricTextQueryHandler :
3534 """Manages lazy initialization and invocation of the Data Fabric sub-graph.
3635
37- On first call, fetches entity schemas from the DF API and compiles
38- the inner LangGraph sub-graph. Subsequent calls reuse the cached graph.
36+ On first call, resolves entity schemas and routing via the platform
37+ layer and compiles the inner LangGraph sub-graph. Subsequent calls
38+ reuse the cached graph.
3939 """
4040
4141 def __init__ (
4242 self ,
43- entity_identifiers : list [str ],
44- routing_context : QueryRoutingOverrideContext ,
43+ entity_set : list [DataFabricEntityItem ],
4544 llm : BaseChatModel ,
4645 resource_description : str = "" ,
4746 base_system_prompt : str = "" ,
4847 ) -> None :
49- self ._entity_identifiers = entity_identifiers
50- self ._routing_context = routing_context
48+ self ._entity_set = entity_set
5149 self ._llm = llm
5250 self ._resource_description = resource_description
5351 self ._base_system_prompt = base_system_prompt
5452 self ._compiled : CompiledStateGraph [Any ] | None = None
5553 self ._init_lock = asyncio .Lock ()
5654
5755 async def _ensure_datafabric_graph (self ) -> CompiledStateGraph [Any ]:
58- """Lazy-init: fetch schemas + build sub-graph on first call.
56+ """Lazy-init: resolve entities + build sub-graph on first call.
5957
6058 Uses asyncio.Lock because the outer agent supports parallel
6159 tool calls — two concurrent invocations could race on first call.
@@ -67,18 +65,21 @@ async def _ensure_datafabric_graph(self) -> CompiledStateGraph[Any]:
6765 if self ._compiled is not None :
6866 return self ._compiled
6967
68+ from uipath .platform import UiPath
69+
7070 from .datafabric_subgraph import DataFabricGraph
7171
72- entities = await fetch_entity_schemas (self ._entity_identifiers )
73- if not entities :
72+ sdk = UiPath ()
73+ resolution = await sdk .entities .resolve_entity_set_async (self ._entity_set )
74+ if not resolution .entities :
7475 raise ValueError (
7576 "No Data Fabric entity schemas could be fetched. "
7677 "Check entity identifiers and permissions."
7778 )
7879 self ._compiled = DataFabricGraph .create (
7980 llm = self ._llm ,
80- entities = entities ,
81- routing_context = self . _routing_context ,
81+ entities = resolution . entities ,
82+ entities_service = resolution . entities_service ,
8283 resource_description = self ._resource_description ,
8384 base_system_prompt = self ._base_system_prompt ,
8485 )
@@ -98,44 +99,6 @@ async def __call__(self, user_query: str) -> str:
9899 return "Unable to generate an answer from the available data."
99100
100101
101- async def _fetch_single_entity (sdk : Any , identifier : str ) -> Entity | None :
102- """Fetch a single entity by identifier, returning None on failure."""
103- try :
104- entity = await sdk .entities .retrieve_async (identifier )
105- logger .info ("Fetched schema for entity '%s'" , entity .display_name )
106- return entity
107- except Exception :
108- logger .warning ("Failed to fetch entity '%s'" , identifier , exc_info = True )
109- return None
110-
111-
112- async def fetch_entity_schemas (entity_identifiers : list [str ]) -> list [Entity ]:
113- """Fetch entity metadata from Data Fabric concurrently."""
114- from uipath .platform import UiPath
115-
116- sdk = UiPath ()
117- results = await asyncio .gather (
118- * [_fetch_single_entity (sdk , eid ) for eid in entity_identifiers ]
119- )
120- return [e for e in results if e is not None ]
121-
122-
123- def _build_routing_context (
124- resource : AgentContextResourceConfig ,
125- ) -> QueryRoutingOverrideContext :
126- """Build query routing context from entity set items.
127-
128- Maps each entity to its folder so the backend resolves
129- entities at folder level instead of tenant level.
130- """
131- return QueryRoutingOverrideContext (
132- entity_routings = [
133- EntityRouting (entity_name = item .name , folder_id = item .folder_id )
134- for item in (resource .entity_set or [])
135- ]
136- )
137-
138-
139102def create_datafabric_query_tool (
140103 resource : AgentContextResourceConfig ,
141104 llm : BaseChatModel ,
@@ -152,9 +115,12 @@ def create_datafabric_query_tool(
152115 Key ``base_system_prompt`` carries the outer agent's system prompt.
153116 """
154117 config = agent_config or {}
118+ entity_set = [
119+ DataFabricEntityItem .model_validate (item .model_dump (by_alias = True ))
120+ for item in (resource .entity_set or [])
121+ ]
155122 handler = DataFabricTextQueryHandler (
156- entity_identifiers = resource .datafabric_entity_identifiers ,
157- routing_context = _build_routing_context (resource ),
123+ entity_set = entity_set ,
158124 llm = llm ,
159125 resource_description = resource .description or "" ,
160126 base_system_prompt = config .get (BASE_SYSTEM_PROMPT , "" ),
0 commit comments