2929from langgraph .graph .message import add_messages
3030from langgraph .graph .state import CompiledStateGraph
3131from pydantic import BaseModel
32- from uipath .core .feature_flags import FeatureFlags
3332from uipath .platform .entities import EntitiesService , Entity
3433
3534from ..datafabric_query_tool import DataFabricQueryTool
3635from . import datafabric_prompt_builder
37- from .datafabric_tool import DATAFABRIC_ONTOLOGY_FF
3836from .models import DataFabricExecuteSqlInput
39- from .ontology_fetch_tool import create_ontology_fetch_tool
4037
4138logger = logging .getLogger (__name__ )
4239
@@ -91,40 +88,25 @@ def __init__(
9188 max_iterations : int = 25 ,
9289 resource_description : str = "" ,
9390 base_system_prompt : str = "" ,
94- ontologies : list [ tuple [ str , str | None ]] | None = None ,
91+ ontology_text : str = "" ,
9592 ) -> None :
9693 self ._max_iterations = max_iterations
9794 self ._execute_sql_tool = self ._create_execute_sql_tool (
9895 entities_service , entities
9996 )
100- # Inner toolset: always execute_sql; optionally an LLM-decided
101- # fetch_ontology tool, added only when ontologies are configured AND the
102- # DataFabricOntologyEnabled feature flag is on. The flag decides which
103- # graph gets built — off → the original entities-only graph.
104- inner_tools : list [BaseTool ] = [self ._execute_sql_tool ]
105- ontology_names : list [str ] = []
106- if ontologies and FeatureFlags .is_flag_enabled (
107- DATAFABRIC_ONTOLOGY_FF , default = False
108- ):
109- inner_tools .append (
110- create_ontology_fetch_tool (entities_service , ontologies )
111- )
112- ontology_names = [name for name , _ in ontologies ]
113- self ._tools_by_name : dict [str , BaseTool ] = {
114- tool .name : tool for tool in inner_tools
115- }
116- # Surface the ontology in the system prompt only when its fetch tool is
117- # actually bound — otherwise the LLM is told to call a tool it lacks.
97+ # The ontology (when configured and enabled) is fetched deterministically
98+ # upstream and embedded directly in the system prompt — the inner agent
99+ # still has a single tool, execute_sql.
118100 self ._system_message = SystemMessage (
119101 content = datafabric_prompt_builder .build (
120102 entities ,
121103 resource_description ,
122104 base_system_prompt ,
123- ontology_names = ontology_names ,
105+ ontology_text = ontology_text ,
124106 )
125107 )
126108 self ._inner_llm = llm .model_copy (update = {"disable_streaming" : True }).bind_tools (
127- inner_tools
109+ [ self . _execute_sql_tool ]
128110 )
129111
130112 # Build and compile the graph
@@ -155,69 +137,36 @@ async def tool_node(self, state: DataFabricSubgraphState) -> dict[str, Any]:
155137 results = await asyncio .gather (
156138 * [self ._execute_tool_call (tc ) for tc in last .tool_calls ]
157139 )
158- # End as soon as ANY tool call is a terminal success (a row-returning
159- # execute_sql). `any` not `all`: a non-terminal tool (e.g. fetch_ontology)
160- # co-issued in the same turn must not prevent a successful SQL from ending
161- # the loop.
162- any_succeeded = any (success for _ , success in results )
163- # When short-circuiting to END, return ONLY the terminal-success
164- # ToolMessages so the outer agent's result is the query rows — not a
165- # co-issued fetch_ontology's OWL. On a non-terminal turn keep all messages
166- # so the inner LLM can use them on its next pass.
167- if any_succeeded :
168- tool_messages = [msg for msg , success in results if success ]
169- else :
170- tool_messages = [msg for msg , _ in results ]
140+ tool_messages = [msg for msg , _ in results ]
141+ all_succeeded = bool (results ) and all (success for _ , success in results )
171142 return {
172143 "messages" : tool_messages ,
173144 "iteration_count" : state .iteration_count + len (last .tool_calls ),
174- "last_tool_success" : any_succeeded ,
145+ "last_tool_success" : all_succeeded ,
175146 }
176147
177148 async def _execute_tool_call (self , tool_call : ToolCall ) -> tuple [ToolMessage , bool ]:
178- """Execute a single tool call and report whether it is a terminal success.
179-
180- Dispatches by tool name so the sub-graph can host more than one tool
181- (e.g. ``execute_sql`` and ``fetch_ontology``). Only a successful
182- ``execute_sql`` that returned rows is terminal; every other tool
183- (including ontology fetch) reports ``False`` so the router loops back to
184- the inner LLM, letting it use the result to write or refine SQL.
185- """
186- name = tool_call .get ("name" , "" )
149+ """Execute a single tool call and report whether it succeeded."""
187150 args = tool_call .get ("args" , {})
188- tool = self ._tools_by_name .get (name )
189- if tool is None :
190- return (
191- ToolMessage (
192- content = f"Unknown tool: { name } " ,
193- tool_call_id = tool_call ["id" ],
194- name = name ,
195- ),
196- False ,
197- )
198151 try :
199- result = await tool .ainvoke (args )
152+ result = await self . _execute_sql_tool .ainvoke (args )
200153 except ValueError as e :
201- if name == self ._execute_sql_tool .name :
202- result = {
203- "records" : [],
204- "total_count" : 0 ,
205- "error" : str (e ),
206- "sql_query" : args .get ("sql_query" , "" ),
207- }
208- else :
209- result = f"Tool '{ name } ' failed: { e } "
154+ result = {
155+ "records" : [],
156+ "total_count" : 0 ,
157+ "error" : str (e ),
158+ "sql_query" : args .get ("sql_query" , "" ),
159+ }
210160 succeeded = (
211- name == self ._execute_sql_tool .name
212- and isinstance (result , dict )
161+ isinstance (result , dict )
213162 and not result .get ("error" )
214163 and result .get ("total_count" , 0 ) > 0
215164 )
216165 return (
217166 ToolMessage (
218167 content = str (result ),
219168 tool_call_id = tool_call ["id" ],
220- name = name ,
169+ name = "execute_sql" ,
221170 ),
222171 succeeded ,
223172 )
@@ -284,7 +233,7 @@ def create(
284233 max_iterations : int = 25 ,
285234 resource_description : str = "" ,
286235 base_system_prompt : str = "" ,
287- ontologies : list [ tuple [ str , str | None ]] | None = None ,
236+ ontology_text : str = "" ,
288237 ) -> CompiledStateGraph [Any ]:
289238 """Create and return a compiled Data Fabric sub-graph."""
290239 graph = DataFabricGraph (
@@ -294,6 +243,6 @@ def create(
294243 max_iterations ,
295244 resource_description ,
296245 base_system_prompt ,
297- ontologies ,
246+ ontology_text ,
298247 )
299248 return graph .compiled_graph
0 commit comments