nvidia-nat 1.3.0a20250828__py3-none-any.whl → 1.3.0a20250830__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nat/agent/base.py +6 -1
- nat/agent/react_agent/agent.py +46 -38
- nat/agent/react_agent/register.py +7 -2
- nat/agent/rewoo_agent/agent.py +16 -30
- nat/agent/rewoo_agent/register.py +3 -3
- nat/agent/tool_calling_agent/agent.py +9 -19
- nat/agent/tool_calling_agent/register.py +2 -2
- nat/builder/eval_builder.py +2 -2
- nat/builder/function.py +8 -8
- nat/builder/workflow.py +6 -2
- nat/builder/workflow_builder.py +21 -24
- nat/cli/cli_utils/config_override.py +1 -1
- nat/cli/commands/info/list_channels.py +1 -1
- nat/cli/commands/info/list_mcp.py +183 -47
- nat/cli/commands/registry/publish.py +2 -2
- nat/cli/commands/registry/pull.py +2 -2
- nat/cli/commands/registry/remove.py +2 -2
- nat/cli/commands/registry/search.py +1 -1
- nat/cli/commands/start.py +15 -3
- nat/cli/commands/uninstall.py +1 -1
- nat/cli/commands/workflow/workflow_commands.py +4 -4
- nat/data_models/discovery_metadata.py +4 -4
- nat/data_models/thinking_mixin.py +27 -8
- nat/eval/evaluate.py +6 -6
- nat/eval/intermediate_step_adapter.py +1 -1
- nat/eval/rag_evaluator/evaluate.py +2 -2
- nat/eval/rag_evaluator/register.py +1 -1
- nat/eval/remote_workflow.py +3 -3
- nat/eval/swe_bench_evaluator/evaluate.py +5 -5
- nat/eval/trajectory_evaluator/evaluate.py +1 -1
- nat/eval/tunable_rag_evaluator/evaluate.py +3 -3
- nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +2 -2
- nat/front_ends/fastapi/fastapi_front_end_controller.py +4 -4
- nat/front_ends/fastapi/fastapi_front_end_plugin.py +1 -1
- nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +3 -3
- nat/front_ends/fastapi/message_handler.py +2 -2
- nat/front_ends/fastapi/message_validator.py +8 -10
- nat/front_ends/fastapi/response_helpers.py +4 -4
- nat/front_ends/fastapi/step_adaptor.py +1 -1
- nat/front_ends/mcp/mcp_front_end_config.py +5 -0
- nat/front_ends/mcp/mcp_front_end_plugin.py +8 -2
- nat/front_ends/mcp/mcp_front_end_plugin_worker.py +2 -2
- nat/front_ends/mcp/tool_converter.py +40 -13
- nat/observability/exporter/base_exporter.py +1 -1
- nat/observability/exporter/processing_exporter.py +8 -9
- nat/observability/exporter_manager.py +5 -5
- nat/observability/mixin/file_mixin.py +7 -7
- nat/observability/processor/batching_processor.py +4 -6
- nat/observability/register.py +3 -1
- nat/profiler/calc/calc_runner.py +3 -4
- nat/profiler/callbacks/agno_callback_handler.py +1 -1
- nat/profiler/callbacks/langchain_callback_handler.py +5 -5
- nat/profiler/callbacks/llama_index_callback_handler.py +3 -3
- nat/profiler/callbacks/semantic_kernel_callback_handler.py +2 -2
- nat/profiler/profile_runner.py +1 -1
- nat/profiler/utils.py +1 -1
- nat/registry_handlers/local/local_handler.py +2 -2
- nat/registry_handlers/package_utils.py +1 -1
- nat/registry_handlers/pypi/pypi_handler.py +3 -3
- nat/registry_handlers/rest/rest_handler.py +4 -4
- nat/retriever/milvus/retriever.py +1 -1
- nat/retriever/nemo_retriever/retriever.py +1 -1
- nat/runtime/loader.py +1 -1
- nat/runtime/runner.py +2 -2
- nat/settings/global_settings.py +1 -1
- nat/tool/code_execution/local_sandbox/local_sandbox_server.py +1 -1
- nat/tool/mcp/{mcp_client.py → mcp_client_base.py} +197 -46
- nat/tool/mcp/mcp_client_impl.py +229 -0
- nat/tool/mcp/mcp_tool.py +79 -42
- nat/tool/nvidia_rag.py +1 -1
- nat/tool/register.py +1 -0
- nat/tool/retriever.py +3 -2
- nat/utils/io/yaml_tools.py +1 -1
- nat/utils/reactive/observer.py +2 -2
- nat/utils/settings/global_settings.py +2 -2
- {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/METADATA +3 -3
- {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/RECORD +82 -81
- {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/WHEEL +0 -0
- {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/entry_points.txt +0 -0
- {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
- {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/licenses/LICENSE.md +0 -0
- {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/top_level.txt +0 -0
nat/agent/base.py
CHANGED
|
@@ -160,6 +160,11 @@ class BaseAgent(ABC):
|
|
|
160
160
|
tool_call_id=tool.name,
|
|
161
161
|
content=f"The tool {tool.name} provided an empty response.")
|
|
162
162
|
|
|
163
|
+
# ToolMessage only accepts str or list[str | dict] as content.
|
|
164
|
+
# Convert into list if the response is a dict.
|
|
165
|
+
if isinstance(response, dict):
|
|
166
|
+
response = [response]
|
|
167
|
+
|
|
163
168
|
return ToolMessage(name=tool.name, tool_call_id=tool.name, content=response)
|
|
164
169
|
|
|
165
170
|
except Exception as e:
|
|
@@ -183,7 +188,7 @@ class BaseAgent(ABC):
|
|
|
183
188
|
|
|
184
189
|
# All retries exhausted, return error message
|
|
185
190
|
error_content = "Tool call failed after all retry attempts. Last error: %s" % str(last_exception)
|
|
186
|
-
logger.error("%s %s", AGENT_LOG_PREFIX, error_content)
|
|
191
|
+
logger.error("%s %s", AGENT_LOG_PREFIX, error_content, exc_info=True)
|
|
187
192
|
return ToolMessage(name=tool.name, tool_call_id=tool.name, content=error_content, status="error")
|
|
188
193
|
|
|
189
194
|
def _log_tool_response(self, tool_name: str, tool_input: Any, tool_response: str) -> None:
|
nat/agent/react_agent/agent.py
CHANGED
|
@@ -77,7 +77,8 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
77
77
|
retry_agent_response_parsing_errors: bool = True,
|
|
78
78
|
parse_agent_response_max_retries: int = 1,
|
|
79
79
|
tool_call_max_retries: int = 1,
|
|
80
|
-
pass_tool_call_errors_to_agent: bool = True
|
|
80
|
+
pass_tool_call_errors_to_agent: bool = True,
|
|
81
|
+
normalize_tool_input_quotes: bool = True):
|
|
81
82
|
super().__init__(llm=llm,
|
|
82
83
|
tools=tools,
|
|
83
84
|
callbacks=callbacks,
|
|
@@ -87,6 +88,7 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
87
88
|
if retry_agent_response_parsing_errors else 1)
|
|
88
89
|
self.tool_call_max_retries = tool_call_max_retries
|
|
89
90
|
self.pass_tool_call_errors_to_agent = pass_tool_call_errors_to_agent
|
|
91
|
+
self.normalize_tool_input_quotes = normalize_tool_input_quotes
|
|
90
92
|
logger.debug(
|
|
91
93
|
"%s Filling the prompt variables 'tools' and 'tool_names', using the tools provided in the config.",
|
|
92
94
|
AGENT_LOG_PREFIX)
|
|
@@ -129,12 +131,8 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
129
131
|
try:
|
|
130
132
|
return self.tools_dict.get(tool_name)
|
|
131
133
|
except Exception as ex:
|
|
132
|
-
logger.
|
|
133
|
-
|
|
134
|
-
tool_name,
|
|
135
|
-
ex,
|
|
136
|
-
exc_info=True)
|
|
137
|
-
raise ex
|
|
134
|
+
logger.error("%s Unable to find tool with the name %s\n%s", AGENT_LOG_PREFIX, tool_name, ex)
|
|
135
|
+
raise
|
|
138
136
|
|
|
139
137
|
async def agent_node(self, state: ReActGraphState):
|
|
140
138
|
try:
|
|
@@ -238,8 +236,8 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
238
236
|
working_state.append(output_message)
|
|
239
237
|
working_state.append(HumanMessage(content=str(ex.observation)))
|
|
240
238
|
except Exception as ex:
|
|
241
|
-
logger.
|
|
242
|
-
raise
|
|
239
|
+
logger.error("%s Failed to call agent_node: %s", AGENT_LOG_PREFIX, ex)
|
|
240
|
+
raise
|
|
243
241
|
|
|
244
242
|
async def conditional_edge(self, state: ReActGraphState):
|
|
245
243
|
try:
|
|
@@ -257,7 +255,7 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
257
255
|
agent_output.tool_input)
|
|
258
256
|
return AgentDecision.TOOL
|
|
259
257
|
except Exception as ex:
|
|
260
|
-
logger.exception("Failed to determine whether agent is calling a tool: %s", ex
|
|
258
|
+
logger.exception("Failed to determine whether agent is calling a tool: %s", ex)
|
|
261
259
|
logger.warning("%s Ending graph traversal", AGENT_LOG_PREFIX)
|
|
262
260
|
return AgentDecision.END
|
|
263
261
|
|
|
@@ -290,35 +288,45 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
290
288
|
agent_thoughts.tool_input)
|
|
291
289
|
|
|
292
290
|
# Run the tool. Try to use structured input, if possible.
|
|
291
|
+
tool_input_str = agent_thoughts.tool_input.strip()
|
|
292
|
+
|
|
293
293
|
try:
|
|
294
|
-
|
|
295
|
-
tool_input_dict = json.loads(tool_input_str) if tool_input_str != 'None' else tool_input_str
|
|
294
|
+
tool_input = json.loads(tool_input_str) if tool_input_str != 'None' else tool_input_str
|
|
296
295
|
logger.debug("%s Successfully parsed structured tool input from Action Input", AGENT_LOG_PREFIX)
|
|
297
296
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
297
|
+
except JSONDecodeError as original_ex:
|
|
298
|
+
if self.normalize_tool_input_quotes:
|
|
299
|
+
# If initial JSON parsing fails, try with quote normalization as a fallback
|
|
300
|
+
normalized_str = tool_input_str.replace("'", '"')
|
|
301
|
+
try:
|
|
302
|
+
tool_input = json.loads(normalized_str)
|
|
303
|
+
logger.debug("%s Successfully parsed structured tool input after quote normalization",
|
|
304
|
+
AGENT_LOG_PREFIX)
|
|
305
|
+
except JSONDecodeError:
|
|
306
|
+
# the quote normalization failed, use raw string input
|
|
307
|
+
logger.debug(
|
|
308
|
+
"%s Unable to parse structured tool input after quote normalization. Using Action Input as is."
|
|
309
|
+
"\nParsing error: %s",
|
|
310
|
+
AGENT_LOG_PREFIX,
|
|
311
|
+
original_ex)
|
|
312
|
+
tool_input = tool_input_str
|
|
313
|
+
else:
|
|
314
|
+
# use raw string input
|
|
315
|
+
logger.debug(
|
|
316
|
+
"%s Unable to parse structured tool input from Action Input. Using Action Input as is."
|
|
317
|
+
"\nParsing error: %s",
|
|
318
|
+
AGENT_LOG_PREFIX,
|
|
319
|
+
original_ex)
|
|
320
|
+
tool_input = tool_input_str
|
|
321
|
+
|
|
322
|
+
# Call tool once with the determined input (either parsed dict or raw string)
|
|
323
|
+
tool_response = await self._call_tool(requested_tool,
|
|
324
|
+
tool_input,
|
|
325
|
+
RunnableConfig(callbacks=self.callbacks),
|
|
326
|
+
max_retries=self.tool_call_max_retries)
|
|
319
327
|
|
|
320
328
|
if self.detailed_logs:
|
|
321
|
-
self._log_tool_response(requested_tool.name,
|
|
329
|
+
self._log_tool_response(requested_tool.name, tool_input, str(tool_response.content))
|
|
322
330
|
|
|
323
331
|
if not self.pass_tool_call_errors_to_agent:
|
|
324
332
|
if tool_response.status == "error":
|
|
@@ -334,8 +342,8 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
334
342
|
logger.debug("%s ReAct Graph built and compiled successfully", AGENT_LOG_PREFIX)
|
|
335
343
|
return self.graph
|
|
336
344
|
except Exception as ex:
|
|
337
|
-
logger.
|
|
338
|
-
raise
|
|
345
|
+
logger.error("%s Failed to build ReAct Graph: %s", AGENT_LOG_PREFIX, ex)
|
|
346
|
+
raise
|
|
339
347
|
|
|
340
348
|
@staticmethod
|
|
341
349
|
def validate_system_prompt(system_prompt: str) -> bool:
|
|
@@ -351,7 +359,7 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
351
359
|
errors.append(error_message)
|
|
352
360
|
if errors:
|
|
353
361
|
error_text = "\n".join(errors)
|
|
354
|
-
logger.
|
|
362
|
+
logger.error("%s %s", AGENT_LOG_PREFIX, error_text)
|
|
355
363
|
raise ValueError(error_text)
|
|
356
364
|
return True
|
|
357
365
|
|
|
@@ -378,7 +386,7 @@ def create_react_agent_prompt(config: "ReActAgentWorkflowConfig") -> ChatPromptT
|
|
|
378
386
|
|
|
379
387
|
valid_prompt = ReActAgentGraph.validate_system_prompt(prompt_str)
|
|
380
388
|
if not valid_prompt:
|
|
381
|
-
logger.
|
|
389
|
+
logger.error("%s Invalid system_prompt", AGENT_LOG_PREFIX)
|
|
382
390
|
raise ValueError("Invalid system_prompt")
|
|
383
391
|
prompt = ChatPromptTemplate([("system", prompt_str), ("user", USER_PROMPT),
|
|
384
392
|
MessagesPlaceholder(variable_name='agent_scratchpad', optional=True)])
|
|
@@ -62,6 +62,10 @@ class ReActAgentWorkflowConfig(FunctionBaseConfig, name="react_agent"):
|
|
|
62
62
|
include_tool_input_schema_in_tool_description: bool = Field(
|
|
63
63
|
default=True, description="Specify inclusion of tool input schemas in the prompt.")
|
|
64
64
|
description: str = Field(default="ReAct Agent Workflow", description="The description of this functions use.")
|
|
65
|
+
normalize_tool_input_quotes: bool = Field(
|
|
66
|
+
default=True,
|
|
67
|
+
description="Whether to replace single quotes with double quotes in the tool input. "
|
|
68
|
+
"This is useful for tools that expect structured json input.")
|
|
65
69
|
system_prompt: str | None = Field(
|
|
66
70
|
default=None,
|
|
67
71
|
description="Provides the SYSTEM_PROMPT to use with the agent") # defaults to SYSTEM_PROMPT in prompt.py
|
|
@@ -107,7 +111,8 @@ async def react_agent_workflow(config: ReActAgentWorkflowConfig, builder: Builde
|
|
|
107
111
|
retry_agent_response_parsing_errors=config.retry_agent_response_parsing_errors,
|
|
108
112
|
parse_agent_response_max_retries=config.parse_agent_response_max_retries,
|
|
109
113
|
tool_call_max_retries=config.tool_call_max_retries,
|
|
110
|
-
pass_tool_call_errors_to_agent=config.pass_tool_call_errors_to_agent
|
|
114
|
+
pass_tool_call_errors_to_agent=config.pass_tool_call_errors_to_agent,
|
|
115
|
+
normalize_tool_input_quotes=config.normalize_tool_input_quotes).build_graph()
|
|
111
116
|
|
|
112
117
|
async def _response_fn(input_message: ChatRequest) -> ChatResponse:
|
|
113
118
|
try:
|
|
@@ -133,7 +138,7 @@ async def react_agent_workflow(config: ReActAgentWorkflowConfig, builder: Builde
|
|
|
133
138
|
return ChatResponse.from_string(str(output_message.content))
|
|
134
139
|
|
|
135
140
|
except Exception as ex:
|
|
136
|
-
logger.exception("%s ReAct Agent failed with exception: %s", AGENT_LOG_PREFIX, ex
|
|
141
|
+
logger.exception("%s ReAct Agent failed with exception: %s", AGENT_LOG_PREFIX, ex)
|
|
137
142
|
# here, we can implement custom error messages
|
|
138
143
|
if config.verbose:
|
|
139
144
|
return ChatResponse.from_string(str(ex))
|
nat/agent/rewoo_agent/agent.py
CHANGED
|
@@ -100,12 +100,8 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
100
100
|
try:
|
|
101
101
|
return self.tools_dict.get(tool_name)
|
|
102
102
|
except Exception as ex:
|
|
103
|
-
logger.
|
|
104
|
-
|
|
105
|
-
tool_name,
|
|
106
|
-
ex,
|
|
107
|
-
exc_info=True)
|
|
108
|
-
raise ex
|
|
103
|
+
logger.error("%s Unable to find tool with the name %s\n%s", AGENT_LOG_PREFIX, tool_name, ex)
|
|
104
|
+
raise
|
|
109
105
|
|
|
110
106
|
@staticmethod
|
|
111
107
|
def _get_current_step(state: ReWOOGraphState) -> int:
|
|
@@ -207,8 +203,8 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
207
203
|
return {"plan": plan, "steps": steps}
|
|
208
204
|
|
|
209
205
|
except Exception as ex:
|
|
210
|
-
logger.
|
|
211
|
-
raise
|
|
206
|
+
logger.error("%s Failed to call planner_node: %s", AGENT_LOG_PREFIX, ex)
|
|
207
|
+
raise
|
|
212
208
|
|
|
213
209
|
async def executor_node(self, state: ReWOOGraphState):
|
|
214
210
|
try:
|
|
@@ -274,22 +270,15 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
274
270
|
RunnableConfig(callbacks=self.callbacks),
|
|
275
271
|
max_retries=3)
|
|
276
272
|
|
|
277
|
-
# ToolMessage only accepts str or list[str | dict] as content.
|
|
278
|
-
# Convert into list if the response is a dict.
|
|
279
|
-
if isinstance(tool_response, dict):
|
|
280
|
-
tool_response = [tool_response]
|
|
281
|
-
|
|
282
|
-
tool_response_message = ToolMessage(name=tool, tool_call_id=tool, content=tool_response)
|
|
283
|
-
|
|
284
273
|
if self.detailed_logs:
|
|
285
274
|
self._log_tool_response(requested_tool.name, tool_input_parsed, str(tool_response))
|
|
286
275
|
|
|
287
|
-
intermediate_results[placeholder] =
|
|
276
|
+
intermediate_results[placeholder] = tool_response
|
|
288
277
|
return {"intermediate_results": intermediate_results}
|
|
289
278
|
|
|
290
279
|
except Exception as ex:
|
|
291
|
-
logger.
|
|
292
|
-
raise
|
|
280
|
+
logger.error("%s Failed to call executor_node: %s", AGENT_LOG_PREFIX, ex)
|
|
281
|
+
raise
|
|
293
282
|
|
|
294
283
|
async def solver_node(self, state: ReWOOGraphState):
|
|
295
284
|
try:
|
|
@@ -332,8 +321,8 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
332
321
|
return {"result": output_message}
|
|
333
322
|
|
|
334
323
|
except Exception as ex:
|
|
335
|
-
logger.
|
|
336
|
-
raise
|
|
324
|
+
logger.error("%s Failed to call solver_node: %s", AGENT_LOG_PREFIX, ex)
|
|
325
|
+
raise
|
|
337
326
|
|
|
338
327
|
async def conditional_edge(self, state: ReWOOGraphState):
|
|
339
328
|
try:
|
|
@@ -348,10 +337,7 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
348
337
|
return AgentDecision.TOOL
|
|
349
338
|
|
|
350
339
|
except Exception as ex:
|
|
351
|
-
logger.exception("%s Failed to determine whether agent is calling a tool: %s",
|
|
352
|
-
AGENT_LOG_PREFIX,
|
|
353
|
-
ex,
|
|
354
|
-
exc_info=True)
|
|
340
|
+
logger.exception("%s Failed to determine whether agent is calling a tool: %s", AGENT_LOG_PREFIX, ex)
|
|
355
341
|
logger.warning("%s Ending graph traversal", AGENT_LOG_PREFIX)
|
|
356
342
|
return AgentDecision.END
|
|
357
343
|
|
|
@@ -377,8 +363,8 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
377
363
|
return self.graph
|
|
378
364
|
|
|
379
365
|
except Exception as ex:
|
|
380
|
-
logger.
|
|
381
|
-
raise
|
|
366
|
+
logger.error("%s Failed to build ReWOO Graph: %s", AGENT_LOG_PREFIX, ex)
|
|
367
|
+
raise
|
|
382
368
|
|
|
383
369
|
async def build_graph(self):
|
|
384
370
|
try:
|
|
@@ -386,8 +372,8 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
386
372
|
logger.debug("%s ReWOO Graph built and compiled successfully", AGENT_LOG_PREFIX)
|
|
387
373
|
return self.graph
|
|
388
374
|
except Exception as ex:
|
|
389
|
-
logger.
|
|
390
|
-
raise
|
|
375
|
+
logger.error("%s Failed to build ReWOO Graph: %s", AGENT_LOG_PREFIX, ex)
|
|
376
|
+
raise
|
|
391
377
|
|
|
392
378
|
@staticmethod
|
|
393
379
|
def validate_planner_prompt(planner_prompt: str) -> bool:
|
|
@@ -403,7 +389,7 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
403
389
|
errors.append(error_message)
|
|
404
390
|
if errors:
|
|
405
391
|
error_text = "\n".join(errors)
|
|
406
|
-
logger.
|
|
392
|
+
logger.error("%s %s", AGENT_LOG_PREFIX, error_text)
|
|
407
393
|
raise ValueError(error_text)
|
|
408
394
|
return True
|
|
409
395
|
|
|
@@ -414,6 +400,6 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
414
400
|
errors.append("The solver prompt cannot be empty.")
|
|
415
401
|
if errors:
|
|
416
402
|
error_text = "\n".join(errors)
|
|
417
|
-
logger.
|
|
403
|
+
logger.error("%s %s", AGENT_LOG_PREFIX, error_text)
|
|
418
404
|
raise ValueError(error_text)
|
|
419
405
|
return True
|
|
@@ -89,7 +89,7 @@ async def rewoo_agent_workflow(config: ReWOOAgentWorkflowConfig, builder: Builde
|
|
|
89
89
|
if config.additional_planner_instructions:
|
|
90
90
|
planner_system_prompt += f"{config.additional_planner_instructions}"
|
|
91
91
|
if not ReWOOAgentGraph.validate_planner_prompt(planner_system_prompt):
|
|
92
|
-
logger.
|
|
92
|
+
logger.error("Invalid planner prompt")
|
|
93
93
|
raise ValueError("Invalid planner prompt")
|
|
94
94
|
planner_prompt = ChatPromptTemplate([("system", planner_system_prompt), ("user", PLANNER_USER_PROMPT)])
|
|
95
95
|
|
|
@@ -97,7 +97,7 @@ async def rewoo_agent_workflow(config: ReWOOAgentWorkflowConfig, builder: Builde
|
|
|
97
97
|
if config.additional_solver_instructions:
|
|
98
98
|
solver_system_prompt += f"{config.additional_solver_instructions}"
|
|
99
99
|
if not ReWOOAgentGraph.validate_solver_prompt(solver_system_prompt):
|
|
100
|
-
logger.
|
|
100
|
+
logger.error("Invalid solver prompt")
|
|
101
101
|
raise ValueError("Invalid solver prompt")
|
|
102
102
|
solver_prompt = ChatPromptTemplate([("system", solver_system_prompt), ("user", SOLVER_USER_PROMPT)])
|
|
103
103
|
|
|
@@ -141,7 +141,7 @@ async def rewoo_agent_workflow(config: ReWOOAgentWorkflowConfig, builder: Builde
|
|
|
141
141
|
return ChatResponse.from_string(output_message)
|
|
142
142
|
|
|
143
143
|
except Exception as ex:
|
|
144
|
-
logger.exception("ReWOO Agent failed with exception: %s", ex
|
|
144
|
+
logger.exception("ReWOO Agent failed with exception: %s", ex)
|
|
145
145
|
# here, we can implement custom error messages
|
|
146
146
|
if config.verbose:
|
|
147
147
|
return ChatResponse.from_string(str(ex))
|
|
@@ -69,8 +69,8 @@ class ToolCallAgentGraph(DualNodeAgent):
|
|
|
69
69
|
# in tool calling agents, we bind the tools to the LLM, to pass the tools' input schemas at runtime
|
|
70
70
|
self.bound_llm = llm.bind_tools(tools)
|
|
71
71
|
except NotImplementedError as ex:
|
|
72
|
-
logger.error("%s Failed to bind tools: %s", AGENT_LOG_PREFIX, ex
|
|
73
|
-
raise
|
|
72
|
+
logger.error("%s Failed to bind tools: %s", AGENT_LOG_PREFIX, ex)
|
|
73
|
+
raise
|
|
74
74
|
|
|
75
75
|
if prompt is not None:
|
|
76
76
|
system_prompt = SystemMessage(content=prompt)
|
|
@@ -105,8 +105,8 @@ class ToolCallAgentGraph(DualNodeAgent):
|
|
|
105
105
|
state.messages += [response]
|
|
106
106
|
return state
|
|
107
107
|
except Exception as ex:
|
|
108
|
-
logger.
|
|
109
|
-
raise
|
|
108
|
+
logger.error("%s Failed to call agent_node: %s", AGENT_LOG_PREFIX, ex)
|
|
109
|
+
raise
|
|
110
110
|
|
|
111
111
|
async def conditional_edge(self, state: ToolCallAgentGraphState):
|
|
112
112
|
try:
|
|
@@ -120,12 +120,7 @@ class ToolCallAgentGraph(DualNodeAgent):
|
|
|
120
120
|
logger.debug("%s Final answer:\n%s", AGENT_LOG_PREFIX, state.messages[-1].content)
|
|
121
121
|
return AgentDecision.END
|
|
122
122
|
except Exception as ex:
|
|
123
|
-
logger.exception(
|
|
124
|
-
"%s Failed to determine whether agent is calling a tool: %s",
|
|
125
|
-
AGENT_LOG_PREFIX,
|
|
126
|
-
ex,
|
|
127
|
-
exc_info=True,
|
|
128
|
-
)
|
|
123
|
+
logger.exception("%s Failed to determine whether agent is calling a tool: %s", AGENT_LOG_PREFIX, ex)
|
|
129
124
|
logger.warning("%s Ending graph traversal", AGENT_LOG_PREFIX)
|
|
130
125
|
return AgentDecision.END
|
|
131
126
|
|
|
@@ -148,8 +143,8 @@ class ToolCallAgentGraph(DualNodeAgent):
|
|
|
148
143
|
|
|
149
144
|
return state
|
|
150
145
|
except Exception as ex:
|
|
151
|
-
logger.
|
|
152
|
-
raise
|
|
146
|
+
logger.error("%s Failed to call tool_node: %s", AGENT_LOG_PREFIX, ex)
|
|
147
|
+
raise
|
|
153
148
|
|
|
154
149
|
async def build_graph(self):
|
|
155
150
|
try:
|
|
@@ -160,13 +155,8 @@ class ToolCallAgentGraph(DualNodeAgent):
|
|
|
160
155
|
)
|
|
161
156
|
return self.graph
|
|
162
157
|
except Exception as ex:
|
|
163
|
-
logger.
|
|
164
|
-
|
|
165
|
-
AGENT_LOG_PREFIX,
|
|
166
|
-
ex,
|
|
167
|
-
exc_info=ex,
|
|
168
|
-
)
|
|
169
|
-
raise ex
|
|
158
|
+
logger.error("%s Failed to build Tool Calling Agent Graph: %s", AGENT_LOG_PREFIX, ex)
|
|
159
|
+
raise
|
|
170
160
|
|
|
171
161
|
|
|
172
162
|
def create_tool_calling_agent_prompt(config: "ToolCallAgentWorkflowConfig") -> str | None:
|
|
@@ -93,7 +93,7 @@ async def tool_calling_agent_workflow(config: ToolCallAgentWorkflowConfig, build
|
|
|
93
93
|
output_message = state.messages[-1]
|
|
94
94
|
return output_message.content
|
|
95
95
|
except Exception as ex:
|
|
96
|
-
logger.exception("%s Tool Calling Agent failed with exception: %s", AGENT_LOG_PREFIX, ex
|
|
96
|
+
logger.exception("%s Tool Calling Agent failed with exception: %s", AGENT_LOG_PREFIX, ex)
|
|
97
97
|
if config.verbose:
|
|
98
98
|
return str(ex)
|
|
99
99
|
return "I seem to be having a problem."
|
|
@@ -101,6 +101,6 @@ async def tool_calling_agent_workflow(config: ToolCallAgentWorkflowConfig, build
|
|
|
101
101
|
try:
|
|
102
102
|
yield FunctionInfo.from_fn(_response_fn, description=config.description)
|
|
103
103
|
except GeneratorExit:
|
|
104
|
-
logger.exception("%s Workflow exited early!", AGENT_LOG_PREFIX
|
|
104
|
+
logger.exception("%s Workflow exited early!", AGENT_LOG_PREFIX)
|
|
105
105
|
finally:
|
|
106
106
|
logger.debug("%s Cleaning up react_agent workflow.", AGENT_LOG_PREFIX)
|
nat/builder/eval_builder.py
CHANGED
|
@@ -61,7 +61,7 @@ class WorkflowEvalBuilder(WorkflowBuilder, EvalBuilder):
|
|
|
61
61
|
# Store the evaluator
|
|
62
62
|
self._evaluators[name] = ConfiguredEvaluator(config=config, instance=info_obj)
|
|
63
63
|
except Exception as e:
|
|
64
|
-
logger.error("Error %s adding evaluator `%s` with config `%s`", e, name, config
|
|
64
|
+
logger.error("Error %s adding evaluator `%s` with config `%s`", e, name, config)
|
|
65
65
|
raise
|
|
66
66
|
|
|
67
67
|
@override
|
|
@@ -98,7 +98,7 @@ class WorkflowEvalBuilder(WorkflowBuilder, EvalBuilder):
|
|
|
98
98
|
try:
|
|
99
99
|
tools.append(tool_wrapper_reg.build_fn(fn_name, fn, self))
|
|
100
100
|
except Exception:
|
|
101
|
-
logger.exception("Error fetching tool `%s`", fn_name
|
|
101
|
+
logger.exception("Error fetching tool `%s`", fn_name)
|
|
102
102
|
|
|
103
103
|
return tools
|
|
104
104
|
|
nat/builder/function.py
CHANGED
|
@@ -155,8 +155,8 @@ class Function(FunctionBase[InputT, StreamingOutputT, SingleOutputT], ABC):
|
|
|
155
155
|
|
|
156
156
|
return result
|
|
157
157
|
except Exception as e:
|
|
158
|
-
logger.error("Error with ainvoke in function with input: %s.", value,
|
|
159
|
-
raise
|
|
158
|
+
logger.error("Error with ainvoke in function with input: %s. Error: %s", value, e)
|
|
159
|
+
raise
|
|
160
160
|
|
|
161
161
|
@typing.final
|
|
162
162
|
async def acall_invoke(self, *args, **kwargs):
|
|
@@ -186,14 +186,14 @@ class Function(FunctionBase[InputT, StreamingOutputT, SingleOutputT], ABC):
|
|
|
186
186
|
input_obj = self.input_schema(*args, **kwargs)
|
|
187
187
|
|
|
188
188
|
return await self.ainvoke(value=input_obj)
|
|
189
|
-
except Exception
|
|
189
|
+
except Exception:
|
|
190
190
|
logger.error(
|
|
191
191
|
"Error in acall_invoke() converting input to function schema. Both args and kwargs were "
|
|
192
192
|
"supplied which could not be converted to the input schema. args: %s\nkwargs: %s\nschema: %s",
|
|
193
193
|
args,
|
|
194
194
|
kwargs,
|
|
195
195
|
self.input_schema)
|
|
196
|
-
raise
|
|
196
|
+
raise
|
|
197
197
|
|
|
198
198
|
@abstractmethod
|
|
199
199
|
async def _astream(self, value: InputT) -> AsyncGenerator[StreamingOutputT]:
|
|
@@ -252,8 +252,8 @@ class Function(FunctionBase[InputT, StreamingOutputT, SingleOutputT], ABC):
|
|
|
252
252
|
manager.set_output(final_output)
|
|
253
253
|
|
|
254
254
|
except Exception as e:
|
|
255
|
-
logger.error("Error with astream in function with input: %s.", value,
|
|
256
|
-
raise
|
|
255
|
+
logger.error("Error with astream in function with input: %s. Error: %s", value, e)
|
|
256
|
+
raise
|
|
257
257
|
|
|
258
258
|
@typing.final
|
|
259
259
|
async def acall_stream(self, *args, **kwargs):
|
|
@@ -287,14 +287,14 @@ class Function(FunctionBase[InputT, StreamingOutputT, SingleOutputT], ABC):
|
|
|
287
287
|
|
|
288
288
|
async for x in self.astream(value=input_obj):
|
|
289
289
|
yield x
|
|
290
|
-
except Exception
|
|
290
|
+
except Exception:
|
|
291
291
|
logger.error(
|
|
292
292
|
"Error in acall_stream() converting input to function schema. Both args and kwargs were "
|
|
293
293
|
"supplied which could not be converted to the input schema. args: %s\nkwargs: %s\nschema: %s",
|
|
294
294
|
args,
|
|
295
295
|
kwargs,
|
|
296
296
|
self.input_schema)
|
|
297
|
-
raise
|
|
297
|
+
raise
|
|
298
298
|
|
|
299
299
|
|
|
300
300
|
class LambdaFunction(Function[InputT, StreamingOutputT, SingleOutputT]):
|
nat/builder/workflow.py
CHANGED
|
@@ -84,7 +84,11 @@ class Workflow(FunctionBase[InputT, StreamingOutputT, SingleOutputT]):
|
|
|
84
84
|
return self._entry_fn.has_single_output
|
|
85
85
|
|
|
86
86
|
async def get_all_exporters(self) -> dict[str, BaseExporter]:
|
|
87
|
-
return await self.
|
|
87
|
+
return await self.exporter_manager.get_all_exporters()
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def exporter_manager(self) -> ExporterManager:
|
|
91
|
+
return self._exporter_manager.get()
|
|
88
92
|
|
|
89
93
|
@asynccontextmanager
|
|
90
94
|
async def run(self, message: InputT):
|
|
@@ -96,7 +100,7 @@ class Workflow(FunctionBase[InputT, StreamingOutputT, SingleOutputT]):
|
|
|
96
100
|
async with Runner(input_message=message,
|
|
97
101
|
entry_fn=self._entry_fn,
|
|
98
102
|
context_state=self._context_state,
|
|
99
|
-
exporter_manager=self.
|
|
103
|
+
exporter_manager=self.exporter_manager) as runner:
|
|
100
104
|
|
|
101
105
|
# The caller can `yield runner` so they can do `runner.result()` or `runner.result_stream()`
|
|
102
106
|
yield runner
|
nat/builder/workflow_builder.py
CHANGED
|
@@ -420,8 +420,8 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
420
420
|
# Wrap in the correct wrapper
|
|
421
421
|
return tool_wrapper_reg.build_fn(fn_name, fn.instance, self)
|
|
422
422
|
except Exception as e:
|
|
423
|
-
logger.error("Error fetching tool `%s
|
|
424
|
-
raise
|
|
423
|
+
logger.error("Error fetching tool `%s`: %s", fn_name, e)
|
|
424
|
+
raise
|
|
425
425
|
|
|
426
426
|
@override
|
|
427
427
|
async def add_llm(self, name: str | LLMRef, config: LLMBaseConfig):
|
|
@@ -436,8 +436,8 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
436
436
|
|
|
437
437
|
self._llms[name] = ConfiguredLLM(config=config, instance=info_obj)
|
|
438
438
|
except Exception as e:
|
|
439
|
-
logger.error("Error adding llm `%s` with config `%s
|
|
440
|
-
raise
|
|
439
|
+
logger.error("Error adding llm `%s` with config `%s`: %s", name, config, e)
|
|
440
|
+
raise
|
|
441
441
|
|
|
442
442
|
@override
|
|
443
443
|
async def get_llm(self, llm_name: str | LLMRef, wrapper_type: LLMFrameworkEnum | str):
|
|
@@ -457,8 +457,8 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
457
457
|
# Return a frameworks specific client
|
|
458
458
|
return client
|
|
459
459
|
except Exception as e:
|
|
460
|
-
logger.error("Error getting llm `%s` with wrapper `%s
|
|
461
|
-
raise
|
|
460
|
+
logger.error("Error getting llm `%s` with wrapper `%s`: %s", llm_name, wrapper_type, e)
|
|
461
|
+
raise
|
|
462
462
|
|
|
463
463
|
@override
|
|
464
464
|
def get_llm_config(self, llm_name: str | LLMRef) -> LLMBaseConfig:
|
|
@@ -508,8 +508,8 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
508
508
|
|
|
509
509
|
return info_obj
|
|
510
510
|
except Exception as e:
|
|
511
|
-
logger.error("Error adding authentication `%s` with config `%s
|
|
512
|
-
raise
|
|
511
|
+
logger.error("Error adding authentication `%s` with config `%s`: %s", name, config, e)
|
|
512
|
+
raise
|
|
513
513
|
|
|
514
514
|
@override
|
|
515
515
|
async def get_auth_provider(self, auth_provider_name: str) -> AuthProviderBase:
|
|
@@ -552,9 +552,8 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
552
552
|
|
|
553
553
|
self._embedders[name] = ConfiguredEmbedder(config=config, instance=info_obj)
|
|
554
554
|
except Exception as e:
|
|
555
|
-
logger.error("Error adding embedder `%s` with config `%s
|
|
556
|
-
|
|
557
|
-
raise e
|
|
555
|
+
logger.error("Error adding embedder `%s` with config `%s`: %s", name, config, e)
|
|
556
|
+
raise
|
|
558
557
|
|
|
559
558
|
@override
|
|
560
559
|
async def get_embedder(self, embedder_name: str | EmbedderRef, wrapper_type: LLMFrameworkEnum | str):
|
|
@@ -574,8 +573,8 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
574
573
|
# Return a frameworks specific client
|
|
575
574
|
return client
|
|
576
575
|
except Exception as e:
|
|
577
|
-
logger.error("Error getting embedder `%s` with wrapper `%s
|
|
578
|
-
raise
|
|
576
|
+
logger.error("Error getting embedder `%s` with wrapper `%s`: %s", embedder_name, wrapper_type, e)
|
|
577
|
+
raise
|
|
579
578
|
|
|
580
579
|
@override
|
|
581
580
|
def get_embedder_config(self, embedder_name: str | EmbedderRef) -> EmbedderBaseConfig:
|
|
@@ -660,9 +659,8 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
660
659
|
self._retrievers[name] = ConfiguredRetriever(config=config, instance=info_obj)
|
|
661
660
|
|
|
662
661
|
except Exception as e:
|
|
663
|
-
logger.error("Error adding retriever `%s` with config `%s
|
|
664
|
-
|
|
665
|
-
raise e
|
|
662
|
+
logger.error("Error adding retriever `%s` with config `%s`: %s", name, config, e)
|
|
663
|
+
raise
|
|
666
664
|
|
|
667
665
|
# return info_obj
|
|
668
666
|
|
|
@@ -687,8 +685,8 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
687
685
|
# Return a frameworks specific client
|
|
688
686
|
return client
|
|
689
687
|
except Exception as e:
|
|
690
|
-
logger.error("Error getting retriever `%s` with wrapper `%s
|
|
691
|
-
raise
|
|
688
|
+
logger.error("Error getting retriever `%s` with wrapper `%s`: %s", retriever_name, wrapper_type, e)
|
|
689
|
+
raise
|
|
692
690
|
|
|
693
691
|
@override
|
|
694
692
|
async def get_retriever_config(self, retriever_name: str | RetrieverRef) -> RetrieverBaseConfig:
|
|
@@ -712,9 +710,8 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
712
710
|
self._ttc_strategies[name] = ConfiguredTTCStrategy(config=config, instance=info_obj)
|
|
713
711
|
|
|
714
712
|
except Exception as e:
|
|
715
|
-
logger.error("Error adding TTC strategy `%s` with config `%s
|
|
716
|
-
|
|
717
|
-
raise e
|
|
713
|
+
logger.error("Error adding TTC strategy `%s` with config `%s`: %s", name, config, e)
|
|
714
|
+
raise
|
|
718
715
|
|
|
719
716
|
@override
|
|
720
717
|
async def get_ttc_strategy(self,
|
|
@@ -742,8 +739,8 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
742
739
|
|
|
743
740
|
return instance
|
|
744
741
|
except Exception as e:
|
|
745
|
-
logger.error("Error getting TTC strategy `%s
|
|
746
|
-
raise
|
|
742
|
+
logger.error("Error getting TTC strategy `%s`: %s", strategy_name, e)
|
|
743
|
+
raise
|
|
747
744
|
|
|
748
745
|
@override
|
|
749
746
|
async def get_ttc_strategy_config(self,
|
|
@@ -820,7 +817,7 @@ class WorkflowBuilder(Builder, AbstractAsyncContextManager):
|
|
|
820
817
|
else:
|
|
821
818
|
logger.error("No remaining components to build")
|
|
822
819
|
|
|
823
|
-
logger.error("Original error:", exc_info=
|
|
820
|
+
logger.error("Original error: %s", original_error, exc_info=True)
|
|
824
821
|
|
|
825
822
|
def _log_build_failure_component(self,
|
|
826
823
|
failing_component: ComponentInstanceData,
|
|
@@ -213,7 +213,7 @@ def load_and_override_config(config_file: Path, overrides: tuple[tuple[str, str]
|
|
|
213
213
|
yaml.dump(effective_config, default_flow_style=False),
|
|
214
214
|
)
|
|
215
215
|
except Exception as e:
|
|
216
|
-
logger.
|
|
216
|
+
logger.error("Modified configuration failed validation: %s", e)
|
|
217
217
|
raise click.BadParameter(f"Modified configuration failed validation: {str(e)}")
|
|
218
218
|
finally:
|
|
219
219
|
# Clean up the temporary file
|