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.
Files changed (82) hide show
  1. nat/agent/base.py +6 -1
  2. nat/agent/react_agent/agent.py +46 -38
  3. nat/agent/react_agent/register.py +7 -2
  4. nat/agent/rewoo_agent/agent.py +16 -30
  5. nat/agent/rewoo_agent/register.py +3 -3
  6. nat/agent/tool_calling_agent/agent.py +9 -19
  7. nat/agent/tool_calling_agent/register.py +2 -2
  8. nat/builder/eval_builder.py +2 -2
  9. nat/builder/function.py +8 -8
  10. nat/builder/workflow.py +6 -2
  11. nat/builder/workflow_builder.py +21 -24
  12. nat/cli/cli_utils/config_override.py +1 -1
  13. nat/cli/commands/info/list_channels.py +1 -1
  14. nat/cli/commands/info/list_mcp.py +183 -47
  15. nat/cli/commands/registry/publish.py +2 -2
  16. nat/cli/commands/registry/pull.py +2 -2
  17. nat/cli/commands/registry/remove.py +2 -2
  18. nat/cli/commands/registry/search.py +1 -1
  19. nat/cli/commands/start.py +15 -3
  20. nat/cli/commands/uninstall.py +1 -1
  21. nat/cli/commands/workflow/workflow_commands.py +4 -4
  22. nat/data_models/discovery_metadata.py +4 -4
  23. nat/data_models/thinking_mixin.py +27 -8
  24. nat/eval/evaluate.py +6 -6
  25. nat/eval/intermediate_step_adapter.py +1 -1
  26. nat/eval/rag_evaluator/evaluate.py +2 -2
  27. nat/eval/rag_evaluator/register.py +1 -1
  28. nat/eval/remote_workflow.py +3 -3
  29. nat/eval/swe_bench_evaluator/evaluate.py +5 -5
  30. nat/eval/trajectory_evaluator/evaluate.py +1 -1
  31. nat/eval/tunable_rag_evaluator/evaluate.py +3 -3
  32. nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +2 -2
  33. nat/front_ends/fastapi/fastapi_front_end_controller.py +4 -4
  34. nat/front_ends/fastapi/fastapi_front_end_plugin.py +1 -1
  35. nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +3 -3
  36. nat/front_ends/fastapi/message_handler.py +2 -2
  37. nat/front_ends/fastapi/message_validator.py +8 -10
  38. nat/front_ends/fastapi/response_helpers.py +4 -4
  39. nat/front_ends/fastapi/step_adaptor.py +1 -1
  40. nat/front_ends/mcp/mcp_front_end_config.py +5 -0
  41. nat/front_ends/mcp/mcp_front_end_plugin.py +8 -2
  42. nat/front_ends/mcp/mcp_front_end_plugin_worker.py +2 -2
  43. nat/front_ends/mcp/tool_converter.py +40 -13
  44. nat/observability/exporter/base_exporter.py +1 -1
  45. nat/observability/exporter/processing_exporter.py +8 -9
  46. nat/observability/exporter_manager.py +5 -5
  47. nat/observability/mixin/file_mixin.py +7 -7
  48. nat/observability/processor/batching_processor.py +4 -6
  49. nat/observability/register.py +3 -1
  50. nat/profiler/calc/calc_runner.py +3 -4
  51. nat/profiler/callbacks/agno_callback_handler.py +1 -1
  52. nat/profiler/callbacks/langchain_callback_handler.py +5 -5
  53. nat/profiler/callbacks/llama_index_callback_handler.py +3 -3
  54. nat/profiler/callbacks/semantic_kernel_callback_handler.py +2 -2
  55. nat/profiler/profile_runner.py +1 -1
  56. nat/profiler/utils.py +1 -1
  57. nat/registry_handlers/local/local_handler.py +2 -2
  58. nat/registry_handlers/package_utils.py +1 -1
  59. nat/registry_handlers/pypi/pypi_handler.py +3 -3
  60. nat/registry_handlers/rest/rest_handler.py +4 -4
  61. nat/retriever/milvus/retriever.py +1 -1
  62. nat/retriever/nemo_retriever/retriever.py +1 -1
  63. nat/runtime/loader.py +1 -1
  64. nat/runtime/runner.py +2 -2
  65. nat/settings/global_settings.py +1 -1
  66. nat/tool/code_execution/local_sandbox/local_sandbox_server.py +1 -1
  67. nat/tool/mcp/{mcp_client.py → mcp_client_base.py} +197 -46
  68. nat/tool/mcp/mcp_client_impl.py +229 -0
  69. nat/tool/mcp/mcp_tool.py +79 -42
  70. nat/tool/nvidia_rag.py +1 -1
  71. nat/tool/register.py +1 -0
  72. nat/tool/retriever.py +3 -2
  73. nat/utils/io/yaml_tools.py +1 -1
  74. nat/utils/reactive/observer.py +2 -2
  75. nat/utils/settings/global_settings.py +2 -2
  76. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/METADATA +3 -3
  77. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/RECORD +82 -81
  78. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/WHEEL +0 -0
  79. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/entry_points.txt +0 -0
  80. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
  81. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/licenses/LICENSE.md +0 -0
  82. {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:
@@ -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.exception("%s Unable to find tool with the name %s\n%s",
133
- AGENT_LOG_PREFIX,
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.exception("%s Failed to call agent_node: %s", AGENT_LOG_PREFIX, ex, exc_info=True)
242
- raise ex
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, exc_info=True)
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
- tool_input_str = str(agent_thoughts.tool_input).strip().replace("'", '"')
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
- tool_response = await self._call_tool(requested_tool,
299
- tool_input_dict,
300
- RunnableConfig(callbacks=self.callbacks),
301
- max_retries=self.tool_call_max_retries)
302
-
303
- if self.detailed_logs:
304
- self._log_tool_response(requested_tool.name, tool_input_dict, str(tool_response.content))
305
-
306
- except JSONDecodeError as ex:
307
- logger.debug(
308
- "%s Unable to parse structured tool input from Action Input. Using Action Input as is."
309
- "\nParsing error: %s",
310
- AGENT_LOG_PREFIX,
311
- ex,
312
- exc_info=True)
313
- tool_input_str = str(agent_thoughts.tool_input)
314
-
315
- tool_response = await self._call_tool(requested_tool,
316
- tool_input_str,
317
- RunnableConfig(callbacks=self.callbacks),
318
- max_retries=self.tool_call_max_retries)
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, tool_input_str, str(tool_response.content))
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.exception("%s Failed to build ReAct Graph: %s", AGENT_LOG_PREFIX, ex, exc_info=ex)
338
- raise ex
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.exception("%s %s", AGENT_LOG_PREFIX, error_text)
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.exception("%s Invalid system_prompt", AGENT_LOG_PREFIX)
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).build_graph()
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, exc_info=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))
@@ -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.exception("%s Unable to find tool with the name %s\n%s",
104
- AGENT_LOG_PREFIX,
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.exception("%s Failed to call planner_node: %s", AGENT_LOG_PREFIX, ex, exc_info=True)
211
- raise ex
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] = tool_response_message
276
+ intermediate_results[placeholder] = tool_response
288
277
  return {"intermediate_results": intermediate_results}
289
278
 
290
279
  except Exception as ex:
291
- logger.exception("%s Failed to call executor_node: %s", AGENT_LOG_PREFIX, ex, exc_info=True)
292
- raise ex
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.exception("%s Failed to call solver_node: %s", AGENT_LOG_PREFIX, ex, exc_info=True)
336
- raise ex
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.exception("%s Failed to build ReWOO Graph: %s", AGENT_LOG_PREFIX, ex, exc_info=ex)
381
- raise ex
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.exception("%s Failed to build ReWOO Graph: %s", AGENT_LOG_PREFIX, ex, exc_info=ex)
390
- raise ex
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.exception("%s %s", AGENT_LOG_PREFIX, error_text)
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.exception("%s %s", AGENT_LOG_PREFIX, error_text)
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.exception("Invalid planner prompt")
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.exception("Invalid solver prompt")
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, exc_info=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, exc_info=True)
73
- raise ex
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.exception("%s Failed to call agent_node: %s", AGENT_LOG_PREFIX, ex, exc_info=True)
109
- raise ex
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.exception("%s Failed to call tool_node: %s", AGENT_LOG_PREFIX, ex, exc_info=ex)
152
- raise ex
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.exception(
164
- "%s Failed to build Tool Calling Agent Graph: %s",
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, exc_info=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, exc_info=True)
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)
@@ -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, exc_info=True)
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, exc_info=True)
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, exc_info=True)
159
- raise e
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 as e:
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 e
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, exc_info=True)
256
- raise e
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 as e:
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 e
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._exporter_manager.get_all_exporters()
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._exporter_manager.get()) as runner:
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
@@ -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`", fn_name, exc_info=True)
424
- raise e
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`", name, config, exc_info=True)
440
- raise e
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`", llm_name, wrapper_type, exc_info=True)
461
- raise e
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`", name, config, exc_info=True)
512
- raise e
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`", name, config, exc_info=True)
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`", embedder_name, wrapper_type, exc_info=True)
578
- raise e
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`", name, config, exc_info=True)
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`", retriever_name, wrapper_type, exc_info=True)
691
- raise e
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`", name, config, exc_info=True)
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`", strategy_name, exc_info=True)
746
- raise e
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=original_error)
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.exception("Modified configuration failed validation: %s", e, exc_info=True)
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
@@ -29,4 +29,4 @@ def list_channels(channel_type: str):
29
29
  try:
30
30
  settings.print_channel_settings(channel_type=channel_type)
31
31
  except Exception as e:
32
- logger.exception("Error listing channels: %s", e, exc_info=True)
32
+ logger.exception("Error listing channels: %s", e)