nvidia-nat 1.3.0a20250827__py3-none-any.whl → 1.3.0a20250829__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 +12 -7
- nat/agent/dual_node.py +7 -2
- nat/agent/react_agent/agent.py +15 -14
- nat/agent/react_agent/register.py +5 -1
- nat/agent/rewoo_agent/agent.py +23 -32
- nat/agent/rewoo_agent/register.py +8 -4
- nat/agent/tool_calling_agent/agent.py +15 -20
- nat/agent/tool_calling_agent/register.py +6 -2
- nat/builder/context.py +7 -2
- nat/builder/eval_builder.py +2 -2
- nat/builder/function.py +8 -8
- 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/object_store/__init__.py +14 -0
- nat/cli/commands/object_store/object_store.py +227 -0
- 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 +1 -1
- nat/cli/commands/uninstall.py +1 -1
- nat/cli/commands/workflow/workflow_commands.py +4 -4
- nat/cli/entrypoint.py +3 -1
- nat/data_models/discovery_metadata.py +4 -4
- nat/data_models/gated_field_mixin.py +12 -14
- nat/data_models/temperature_mixin.py +1 -1
- nat/data_models/thinking_mixin.py +68 -0
- nat/data_models/top_p_mixin.py +1 -1
- 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/llm/aws_bedrock_llm.py +10 -9
- nat/llm/azure_openai_llm.py +9 -1
- nat/llm/nim_llm.py +2 -1
- nat/llm/openai_llm.py +2 -1
- nat/llm/utils/thinking.py +215 -0
- 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/processor/falsy_batch_filter_processor.py +55 -0
- nat/observability/processor/processor_factory.py +70 -0
- 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/decorators/function_tracking.py +125 -0
- 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/nvidia_rag.py +1 -1
- 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.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/METADATA +3 -1
- {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/RECORD +87 -81
- {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/WHEEL +0 -0
- {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/entry_points.txt +0 -0
- {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
- {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/licenses/LICENSE.md +0 -0
- {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/top_level.txt +0 -0
nat/agent/base.py
CHANGED
|
@@ -70,12 +70,14 @@ class BaseAgent(ABC):
|
|
|
70
70
|
llm: BaseChatModel,
|
|
71
71
|
tools: list[BaseTool],
|
|
72
72
|
callbacks: list[AsyncCallbackHandler] | None = None,
|
|
73
|
-
detailed_logs: bool = False
|
|
73
|
+
detailed_logs: bool = False,
|
|
74
|
+
log_response_max_chars: int = 1000) -> None:
|
|
74
75
|
logger.debug("Initializing Agent Graph")
|
|
75
76
|
self.llm = llm
|
|
76
77
|
self.tools = tools
|
|
77
78
|
self.callbacks = callbacks or []
|
|
78
79
|
self.detailed_logs = detailed_logs
|
|
80
|
+
self.log_response_max_chars = log_response_max_chars
|
|
79
81
|
self.graph = None
|
|
80
82
|
|
|
81
83
|
async def _stream_llm(self,
|
|
@@ -158,6 +160,11 @@ class BaseAgent(ABC):
|
|
|
158
160
|
tool_call_id=tool.name,
|
|
159
161
|
content=f"The tool {tool.name} provided an empty response.")
|
|
160
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
|
+
|
|
161
168
|
return ToolMessage(name=tool.name, tool_call_id=tool.name, content=response)
|
|
162
169
|
|
|
163
170
|
except Exception as e:
|
|
@@ -181,10 +188,10 @@ class BaseAgent(ABC):
|
|
|
181
188
|
|
|
182
189
|
# All retries exhausted, return error message
|
|
183
190
|
error_content = "Tool call failed after all retry attempts. Last error: %s" % str(last_exception)
|
|
184
|
-
logger.error("%s %s", AGENT_LOG_PREFIX, error_content)
|
|
191
|
+
logger.error("%s %s", AGENT_LOG_PREFIX, error_content, exc_info=True)
|
|
185
192
|
return ToolMessage(name=tool.name, tool_call_id=tool.name, content=error_content, status="error")
|
|
186
193
|
|
|
187
|
-
def _log_tool_response(self, tool_name: str, tool_input: Any, tool_response: str
|
|
194
|
+
def _log_tool_response(self, tool_name: str, tool_input: Any, tool_response: str) -> None:
|
|
188
195
|
"""
|
|
189
196
|
Log tool response with consistent formatting and length limits.
|
|
190
197
|
|
|
@@ -196,13 +203,11 @@ class BaseAgent(ABC):
|
|
|
196
203
|
The input that was passed to the tool
|
|
197
204
|
tool_response : str
|
|
198
205
|
The response from the tool
|
|
199
|
-
max_chars : int
|
|
200
|
-
Maximum number of characters to log (default: 1000)
|
|
201
206
|
"""
|
|
202
207
|
if self.detailed_logs:
|
|
203
208
|
# Truncate tool response if too long
|
|
204
|
-
display_response = tool_response[:
|
|
205
|
-
tool_response) >
|
|
209
|
+
display_response = tool_response[:self.log_response_max_chars] + "...(rest of response truncated)" if len(
|
|
210
|
+
tool_response) > self.log_response_max_chars else tool_response
|
|
206
211
|
|
|
207
212
|
# Format the tool input for display
|
|
208
213
|
tool_input_str = str(tool_input)
|
nat/agent/dual_node.py
CHANGED
|
@@ -35,8 +35,13 @@ class DualNodeAgent(BaseAgent):
|
|
|
35
35
|
llm: BaseChatModel,
|
|
36
36
|
tools: list[BaseTool],
|
|
37
37
|
callbacks: list[AsyncCallbackHandler] | None = None,
|
|
38
|
-
detailed_logs: bool = False
|
|
39
|
-
|
|
38
|
+
detailed_logs: bool = False,
|
|
39
|
+
log_response_max_chars: int = 1000):
|
|
40
|
+
super().__init__(llm=llm,
|
|
41
|
+
tools=tools,
|
|
42
|
+
callbacks=callbacks,
|
|
43
|
+
detailed_logs=detailed_logs,
|
|
44
|
+
log_response_max_chars=log_response_max_chars)
|
|
40
45
|
|
|
41
46
|
@abstractmethod
|
|
42
47
|
async def agent_node(self, state: BaseModel) -> BaseModel:
|
nat/agent/react_agent/agent.py
CHANGED
|
@@ -73,11 +73,16 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
73
73
|
use_tool_schema: bool = True,
|
|
74
74
|
callbacks: list[AsyncCallbackHandler] | None = None,
|
|
75
75
|
detailed_logs: bool = False,
|
|
76
|
+
log_response_max_chars: int = 1000,
|
|
76
77
|
retry_agent_response_parsing_errors: bool = True,
|
|
77
78
|
parse_agent_response_max_retries: int = 1,
|
|
78
79
|
tool_call_max_retries: int = 1,
|
|
79
80
|
pass_tool_call_errors_to_agent: bool = True):
|
|
80
|
-
super().__init__(llm=llm,
|
|
81
|
+
super().__init__(llm=llm,
|
|
82
|
+
tools=tools,
|
|
83
|
+
callbacks=callbacks,
|
|
84
|
+
detailed_logs=detailed_logs,
|
|
85
|
+
log_response_max_chars=log_response_max_chars)
|
|
81
86
|
self.parse_agent_response_max_retries = (parse_agent_response_max_retries
|
|
82
87
|
if retry_agent_response_parsing_errors else 1)
|
|
83
88
|
self.tool_call_max_retries = tool_call_max_retries
|
|
@@ -124,12 +129,8 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
124
129
|
try:
|
|
125
130
|
return self.tools_dict.get(tool_name)
|
|
126
131
|
except Exception as ex:
|
|
127
|
-
logger.
|
|
128
|
-
|
|
129
|
-
tool_name,
|
|
130
|
-
ex,
|
|
131
|
-
exc_info=True)
|
|
132
|
-
raise ex
|
|
132
|
+
logger.error("%s Unable to find tool with the name %s\n%s", AGENT_LOG_PREFIX, tool_name, ex)
|
|
133
|
+
raise
|
|
133
134
|
|
|
134
135
|
async def agent_node(self, state: ReActGraphState):
|
|
135
136
|
try:
|
|
@@ -233,8 +234,8 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
233
234
|
working_state.append(output_message)
|
|
234
235
|
working_state.append(HumanMessage(content=str(ex.observation)))
|
|
235
236
|
except Exception as ex:
|
|
236
|
-
logger.
|
|
237
|
-
raise
|
|
237
|
+
logger.error("%s Failed to call agent_node: %s", AGENT_LOG_PREFIX, ex)
|
|
238
|
+
raise
|
|
238
239
|
|
|
239
240
|
async def conditional_edge(self, state: ReActGraphState):
|
|
240
241
|
try:
|
|
@@ -252,7 +253,7 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
252
253
|
agent_output.tool_input)
|
|
253
254
|
return AgentDecision.TOOL
|
|
254
255
|
except Exception as ex:
|
|
255
|
-
logger.exception("Failed to determine whether agent is calling a tool: %s", ex
|
|
256
|
+
logger.exception("Failed to determine whether agent is calling a tool: %s", ex)
|
|
256
257
|
logger.warning("%s Ending graph traversal", AGENT_LOG_PREFIX)
|
|
257
258
|
return AgentDecision.END
|
|
258
259
|
|
|
@@ -329,8 +330,8 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
329
330
|
logger.debug("%s ReAct Graph built and compiled successfully", AGENT_LOG_PREFIX)
|
|
330
331
|
return self.graph
|
|
331
332
|
except Exception as ex:
|
|
332
|
-
logger.
|
|
333
|
-
raise
|
|
333
|
+
logger.error("%s Failed to build ReAct Graph: %s", AGENT_LOG_PREFIX, ex)
|
|
334
|
+
raise
|
|
334
335
|
|
|
335
336
|
@staticmethod
|
|
336
337
|
def validate_system_prompt(system_prompt: str) -> bool:
|
|
@@ -346,7 +347,7 @@ class ReActAgentGraph(DualNodeAgent):
|
|
|
346
347
|
errors.append(error_message)
|
|
347
348
|
if errors:
|
|
348
349
|
error_text = "\n".join(errors)
|
|
349
|
-
logger.
|
|
350
|
+
logger.error("%s %s", AGENT_LOG_PREFIX, error_text)
|
|
350
351
|
raise ValueError(error_text)
|
|
351
352
|
return True
|
|
352
353
|
|
|
@@ -373,7 +374,7 @@ def create_react_agent_prompt(config: "ReActAgentWorkflowConfig") -> ChatPromptT
|
|
|
373
374
|
|
|
374
375
|
valid_prompt = ReActAgentGraph.validate_system_prompt(prompt_str)
|
|
375
376
|
if not valid_prompt:
|
|
376
|
-
logger.
|
|
377
|
+
logger.error("%s Invalid system_prompt", AGENT_LOG_PREFIX)
|
|
377
378
|
raise ValueError("Invalid system_prompt")
|
|
378
379
|
prompt = ChatPromptTemplate([("system", prompt_str), ("user", USER_PROMPT),
|
|
379
380
|
MessagesPlaceholder(variable_name='agent_scratchpad', optional=True)])
|
|
@@ -17,6 +17,7 @@ import logging
|
|
|
17
17
|
|
|
18
18
|
from pydantic import AliasChoices
|
|
19
19
|
from pydantic import Field
|
|
20
|
+
from pydantic import PositiveInt
|
|
20
21
|
|
|
21
22
|
from nat.builder.builder import Builder
|
|
22
23
|
from nat.builder.framework_enum import LLMFrameworkEnum
|
|
@@ -65,6 +66,8 @@ class ReActAgentWorkflowConfig(FunctionBaseConfig, name="react_agent"):
|
|
|
65
66
|
default=None,
|
|
66
67
|
description="Provides the SYSTEM_PROMPT to use with the agent") # defaults to SYSTEM_PROMPT in prompt.py
|
|
67
68
|
max_history: int = Field(default=15, description="Maximum number of messages to keep in the conversation history.")
|
|
69
|
+
log_response_max_chars: PositiveInt = Field(
|
|
70
|
+
default=1000, description="Maximum number of characters to display in logs when logging tool responses.")
|
|
68
71
|
use_openai_api: bool = Field(default=False,
|
|
69
72
|
description=("Use OpenAI API for the input/output types to the function. "
|
|
70
73
|
"If False, strings will be used."))
|
|
@@ -100,6 +103,7 @@ async def react_agent_workflow(config: ReActAgentWorkflowConfig, builder: Builde
|
|
|
100
103
|
tools=tools,
|
|
101
104
|
use_tool_schema=config.include_tool_input_schema_in_tool_description,
|
|
102
105
|
detailed_logs=config.verbose,
|
|
106
|
+
log_response_max_chars=config.log_response_max_chars,
|
|
103
107
|
retry_agent_response_parsing_errors=config.retry_agent_response_parsing_errors,
|
|
104
108
|
parse_agent_response_max_retries=config.parse_agent_response_max_retries,
|
|
105
109
|
tool_call_max_retries=config.tool_call_max_retries,
|
|
@@ -129,7 +133,7 @@ async def react_agent_workflow(config: ReActAgentWorkflowConfig, builder: Builde
|
|
|
129
133
|
return ChatResponse.from_string(str(output_message.content))
|
|
130
134
|
|
|
131
135
|
except Exception as ex:
|
|
132
|
-
logger.exception("%s ReAct Agent failed with exception: %s", AGENT_LOG_PREFIX, ex
|
|
136
|
+
logger.exception("%s ReAct Agent failed with exception: %s", AGENT_LOG_PREFIX, ex)
|
|
133
137
|
# here, we can implement custom error messages
|
|
134
138
|
if config.verbose:
|
|
135
139
|
return ChatResponse.from_string(str(ex))
|
nat/agent/rewoo_agent/agent.py
CHANGED
|
@@ -66,8 +66,13 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
66
66
|
tools: list[BaseTool],
|
|
67
67
|
use_tool_schema: bool = True,
|
|
68
68
|
callbacks: list[AsyncCallbackHandler] | None = None,
|
|
69
|
-
detailed_logs: bool = False
|
|
70
|
-
|
|
69
|
+
detailed_logs: bool = False,
|
|
70
|
+
log_response_max_chars: int = 1000):
|
|
71
|
+
super().__init__(llm=llm,
|
|
72
|
+
tools=tools,
|
|
73
|
+
callbacks=callbacks,
|
|
74
|
+
detailed_logs=detailed_logs,
|
|
75
|
+
log_response_max_chars=log_response_max_chars)
|
|
71
76
|
|
|
72
77
|
logger.debug(
|
|
73
78
|
"%s Filling the prompt variables 'tools' and 'tool_names', using the tools provided in the config.",
|
|
@@ -95,12 +100,8 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
95
100
|
try:
|
|
96
101
|
return self.tools_dict.get(tool_name)
|
|
97
102
|
except Exception as ex:
|
|
98
|
-
logger.
|
|
99
|
-
|
|
100
|
-
tool_name,
|
|
101
|
-
ex,
|
|
102
|
-
exc_info=True)
|
|
103
|
-
raise ex
|
|
103
|
+
logger.error("%s Unable to find tool with the name %s\n%s", AGENT_LOG_PREFIX, tool_name, ex)
|
|
104
|
+
raise
|
|
104
105
|
|
|
105
106
|
@staticmethod
|
|
106
107
|
def _get_current_step(state: ReWOOGraphState) -> int:
|
|
@@ -202,8 +203,8 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
202
203
|
return {"plan": plan, "steps": steps}
|
|
203
204
|
|
|
204
205
|
except Exception as ex:
|
|
205
|
-
logger.
|
|
206
|
-
raise
|
|
206
|
+
logger.error("%s Failed to call planner_node: %s", AGENT_LOG_PREFIX, ex)
|
|
207
|
+
raise
|
|
207
208
|
|
|
208
209
|
async def executor_node(self, state: ReWOOGraphState):
|
|
209
210
|
try:
|
|
@@ -269,22 +270,15 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
269
270
|
RunnableConfig(callbacks=self.callbacks),
|
|
270
271
|
max_retries=3)
|
|
271
272
|
|
|
272
|
-
# ToolMessage only accepts str or list[str | dict] as content.
|
|
273
|
-
# Convert into list if the response is a dict.
|
|
274
|
-
if isinstance(tool_response, dict):
|
|
275
|
-
tool_response = [tool_response]
|
|
276
|
-
|
|
277
|
-
tool_response_message = ToolMessage(name=tool, tool_call_id=tool, content=tool_response)
|
|
278
|
-
|
|
279
273
|
if self.detailed_logs:
|
|
280
274
|
self._log_tool_response(requested_tool.name, tool_input_parsed, str(tool_response))
|
|
281
275
|
|
|
282
|
-
intermediate_results[placeholder] =
|
|
276
|
+
intermediate_results[placeholder] = tool_response
|
|
283
277
|
return {"intermediate_results": intermediate_results}
|
|
284
278
|
|
|
285
279
|
except Exception as ex:
|
|
286
|
-
logger.
|
|
287
|
-
raise
|
|
280
|
+
logger.error("%s Failed to call executor_node: %s", AGENT_LOG_PREFIX, ex)
|
|
281
|
+
raise
|
|
288
282
|
|
|
289
283
|
async def solver_node(self, state: ReWOOGraphState):
|
|
290
284
|
try:
|
|
@@ -327,8 +321,8 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
327
321
|
return {"result": output_message}
|
|
328
322
|
|
|
329
323
|
except Exception as ex:
|
|
330
|
-
logger.
|
|
331
|
-
raise
|
|
324
|
+
logger.error("%s Failed to call solver_node: %s", AGENT_LOG_PREFIX, ex)
|
|
325
|
+
raise
|
|
332
326
|
|
|
333
327
|
async def conditional_edge(self, state: ReWOOGraphState):
|
|
334
328
|
try:
|
|
@@ -343,10 +337,7 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
343
337
|
return AgentDecision.TOOL
|
|
344
338
|
|
|
345
339
|
except Exception as ex:
|
|
346
|
-
logger.exception("%s Failed to determine whether agent is calling a tool: %s",
|
|
347
|
-
AGENT_LOG_PREFIX,
|
|
348
|
-
ex,
|
|
349
|
-
exc_info=True)
|
|
340
|
+
logger.exception("%s Failed to determine whether agent is calling a tool: %s", AGENT_LOG_PREFIX, ex)
|
|
350
341
|
logger.warning("%s Ending graph traversal", AGENT_LOG_PREFIX)
|
|
351
342
|
return AgentDecision.END
|
|
352
343
|
|
|
@@ -372,8 +363,8 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
372
363
|
return self.graph
|
|
373
364
|
|
|
374
365
|
except Exception as ex:
|
|
375
|
-
logger.
|
|
376
|
-
raise
|
|
366
|
+
logger.error("%s Failed to build ReWOO Graph: %s", AGENT_LOG_PREFIX, ex)
|
|
367
|
+
raise
|
|
377
368
|
|
|
378
369
|
async def build_graph(self):
|
|
379
370
|
try:
|
|
@@ -381,8 +372,8 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
381
372
|
logger.debug("%s ReWOO Graph built and compiled successfully", AGENT_LOG_PREFIX)
|
|
382
373
|
return self.graph
|
|
383
374
|
except Exception as ex:
|
|
384
|
-
logger.
|
|
385
|
-
raise
|
|
375
|
+
logger.error("%s Failed to build ReWOO Graph: %s", AGENT_LOG_PREFIX, ex)
|
|
376
|
+
raise
|
|
386
377
|
|
|
387
378
|
@staticmethod
|
|
388
379
|
def validate_planner_prompt(planner_prompt: str) -> bool:
|
|
@@ -398,7 +389,7 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
398
389
|
errors.append(error_message)
|
|
399
390
|
if errors:
|
|
400
391
|
error_text = "\n".join(errors)
|
|
401
|
-
logger.
|
|
392
|
+
logger.error("%s %s", AGENT_LOG_PREFIX, error_text)
|
|
402
393
|
raise ValueError(error_text)
|
|
403
394
|
return True
|
|
404
395
|
|
|
@@ -409,6 +400,6 @@ class ReWOOAgentGraph(BaseAgent):
|
|
|
409
400
|
errors.append("The solver prompt cannot be empty.")
|
|
410
401
|
if errors:
|
|
411
402
|
error_text = "\n".join(errors)
|
|
412
|
-
logger.
|
|
403
|
+
logger.error("%s %s", AGENT_LOG_PREFIX, error_text)
|
|
413
404
|
raise ValueError(error_text)
|
|
414
405
|
return True
|
|
@@ -17,6 +17,7 @@ import logging
|
|
|
17
17
|
|
|
18
18
|
from pydantic import AliasChoices
|
|
19
19
|
from pydantic import Field
|
|
20
|
+
from pydantic import PositiveInt
|
|
20
21
|
|
|
21
22
|
from nat.builder.builder import Builder
|
|
22
23
|
from nat.builder.framework_enum import LLMFrameworkEnum
|
|
@@ -52,6 +53,8 @@ class ReWOOAgentWorkflowConfig(FunctionBaseConfig, name="rewoo_agent"):
|
|
|
52
53
|
default=None,
|
|
53
54
|
description="Provides the SOLVER_PROMPT to use with the agent") # defaults to SOLVER_PROMPT in prompt.py
|
|
54
55
|
max_history: int = Field(default=15, description="Maximum number of messages to keep in the conversation history.")
|
|
56
|
+
log_response_max_chars: PositiveInt = Field(
|
|
57
|
+
default=1000, description="Maximum number of characters to display in logs when logging tool responses.")
|
|
55
58
|
use_openai_api: bool = Field(default=False,
|
|
56
59
|
description=("Use OpenAI API for the input/output types to the function. "
|
|
57
60
|
"If False, strings will be used."))
|
|
@@ -86,7 +89,7 @@ async def rewoo_agent_workflow(config: ReWOOAgentWorkflowConfig, builder: Builde
|
|
|
86
89
|
if config.additional_planner_instructions:
|
|
87
90
|
planner_system_prompt += f"{config.additional_planner_instructions}"
|
|
88
91
|
if not ReWOOAgentGraph.validate_planner_prompt(planner_system_prompt):
|
|
89
|
-
logger.
|
|
92
|
+
logger.error("Invalid planner prompt")
|
|
90
93
|
raise ValueError("Invalid planner prompt")
|
|
91
94
|
planner_prompt = ChatPromptTemplate([("system", planner_system_prompt), ("user", PLANNER_USER_PROMPT)])
|
|
92
95
|
|
|
@@ -94,7 +97,7 @@ async def rewoo_agent_workflow(config: ReWOOAgentWorkflowConfig, builder: Builde
|
|
|
94
97
|
if config.additional_solver_instructions:
|
|
95
98
|
solver_system_prompt += f"{config.additional_solver_instructions}"
|
|
96
99
|
if not ReWOOAgentGraph.validate_solver_prompt(solver_system_prompt):
|
|
97
|
-
logger.
|
|
100
|
+
logger.error("Invalid solver prompt")
|
|
98
101
|
raise ValueError("Invalid solver prompt")
|
|
99
102
|
solver_prompt = ChatPromptTemplate([("system", solver_system_prompt), ("user", SOLVER_USER_PROMPT)])
|
|
100
103
|
|
|
@@ -113,7 +116,8 @@ async def rewoo_agent_workflow(config: ReWOOAgentWorkflowConfig, builder: Builde
|
|
|
113
116
|
solver_prompt=solver_prompt,
|
|
114
117
|
tools=tools,
|
|
115
118
|
use_tool_schema=config.include_tool_input_schema_in_tool_description,
|
|
116
|
-
detailed_logs=config.verbose
|
|
119
|
+
detailed_logs=config.verbose,
|
|
120
|
+
log_response_max_chars=config.log_response_max_chars).build_graph()
|
|
117
121
|
|
|
118
122
|
async def _response_fn(input_message: ChatRequest) -> ChatResponse:
|
|
119
123
|
try:
|
|
@@ -137,7 +141,7 @@ async def rewoo_agent_workflow(config: ReWOOAgentWorkflowConfig, builder: Builde
|
|
|
137
141
|
return ChatResponse.from_string(output_message)
|
|
138
142
|
|
|
139
143
|
except Exception as ex:
|
|
140
|
-
logger.exception("ReWOO Agent failed with exception: %s", ex
|
|
144
|
+
logger.exception("ReWOO Agent failed with exception: %s", ex)
|
|
141
145
|
# here, we can implement custom error messages
|
|
142
146
|
if config.verbose:
|
|
143
147
|
return ChatResponse.from_string(str(ex))
|
|
@@ -55,17 +55,22 @@ class ToolCallAgentGraph(DualNodeAgent):
|
|
|
55
55
|
prompt: str | None = None,
|
|
56
56
|
callbacks: list[AsyncCallbackHandler] = None,
|
|
57
57
|
detailed_logs: bool = False,
|
|
58
|
+
log_response_max_chars: int = 1000,
|
|
58
59
|
handle_tool_errors: bool = True,
|
|
59
60
|
):
|
|
60
|
-
super().__init__(llm=llm,
|
|
61
|
+
super().__init__(llm=llm,
|
|
62
|
+
tools=tools,
|
|
63
|
+
callbacks=callbacks,
|
|
64
|
+
detailed_logs=detailed_logs,
|
|
65
|
+
log_response_max_chars=log_response_max_chars)
|
|
61
66
|
# some LLMs support tool calling
|
|
62
67
|
# these models accept the tool's input schema and decide when to use a tool based on the input's relevance
|
|
63
68
|
try:
|
|
64
69
|
# in tool calling agents, we bind the tools to the LLM, to pass the tools' input schemas at runtime
|
|
65
70
|
self.bound_llm = llm.bind_tools(tools)
|
|
66
71
|
except NotImplementedError as ex:
|
|
67
|
-
logger.error("%s Failed to bind tools: %s", AGENT_LOG_PREFIX, ex
|
|
68
|
-
raise
|
|
72
|
+
logger.error("%s Failed to bind tools: %s", AGENT_LOG_PREFIX, ex)
|
|
73
|
+
raise
|
|
69
74
|
|
|
70
75
|
if prompt is not None:
|
|
71
76
|
system_prompt = SystemMessage(content=prompt)
|
|
@@ -100,8 +105,8 @@ class ToolCallAgentGraph(DualNodeAgent):
|
|
|
100
105
|
state.messages += [response]
|
|
101
106
|
return state
|
|
102
107
|
except Exception as ex:
|
|
103
|
-
logger.
|
|
104
|
-
raise
|
|
108
|
+
logger.error("%s Failed to call agent_node: %s", AGENT_LOG_PREFIX, ex)
|
|
109
|
+
raise
|
|
105
110
|
|
|
106
111
|
async def conditional_edge(self, state: ToolCallAgentGraphState):
|
|
107
112
|
try:
|
|
@@ -115,12 +120,7 @@ class ToolCallAgentGraph(DualNodeAgent):
|
|
|
115
120
|
logger.debug("%s Final answer:\n%s", AGENT_LOG_PREFIX, state.messages[-1].content)
|
|
116
121
|
return AgentDecision.END
|
|
117
122
|
except Exception as ex:
|
|
118
|
-
logger.exception(
|
|
119
|
-
"%s Failed to determine whether agent is calling a tool: %s",
|
|
120
|
-
AGENT_LOG_PREFIX,
|
|
121
|
-
ex,
|
|
122
|
-
exc_info=True,
|
|
123
|
-
)
|
|
123
|
+
logger.exception("%s Failed to determine whether agent is calling a tool: %s", AGENT_LOG_PREFIX, ex)
|
|
124
124
|
logger.warning("%s Ending graph traversal", AGENT_LOG_PREFIX)
|
|
125
125
|
return AgentDecision.END
|
|
126
126
|
|
|
@@ -143,8 +143,8 @@ class ToolCallAgentGraph(DualNodeAgent):
|
|
|
143
143
|
|
|
144
144
|
return state
|
|
145
145
|
except Exception as ex:
|
|
146
|
-
logger.
|
|
147
|
-
raise
|
|
146
|
+
logger.error("%s Failed to call tool_node: %s", AGENT_LOG_PREFIX, ex)
|
|
147
|
+
raise
|
|
148
148
|
|
|
149
149
|
async def build_graph(self):
|
|
150
150
|
try:
|
|
@@ -155,13 +155,8 @@ class ToolCallAgentGraph(DualNodeAgent):
|
|
|
155
155
|
)
|
|
156
156
|
return self.graph
|
|
157
157
|
except Exception as ex:
|
|
158
|
-
logger.
|
|
159
|
-
|
|
160
|
-
AGENT_LOG_PREFIX,
|
|
161
|
-
ex,
|
|
162
|
-
exc_info=ex,
|
|
163
|
-
)
|
|
164
|
-
raise ex
|
|
158
|
+
logger.error("%s Failed to build Tool Calling Agent Graph: %s", AGENT_LOG_PREFIX, ex)
|
|
159
|
+
raise
|
|
165
160
|
|
|
166
161
|
|
|
167
162
|
def create_tool_calling_agent_prompt(config: "ToolCallAgentWorkflowConfig") -> str | None:
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import logging
|
|
17
17
|
|
|
18
18
|
from pydantic import Field
|
|
19
|
+
from pydantic import PositiveInt
|
|
19
20
|
|
|
20
21
|
from nat.builder.builder import Builder
|
|
21
22
|
from nat.builder.framework_enum import LLMFrameworkEnum
|
|
@@ -41,6 +42,8 @@ class ToolCallAgentWorkflowConfig(FunctionBaseConfig, name="tool_calling_agent")
|
|
|
41
42
|
handle_tool_errors: bool = Field(default=True, description="Specify ability to handle tool calling errors.")
|
|
42
43
|
description: str = Field(default="Tool Calling Agent Workflow", description="Description of this functions use.")
|
|
43
44
|
max_iterations: int = Field(default=15, description="Number of tool calls before stoping the tool calling agent.")
|
|
45
|
+
log_response_max_chars: PositiveInt = Field(
|
|
46
|
+
default=1000, description="Maximum number of characters to display in logs when logging tool responses.")
|
|
44
47
|
system_prompt: str | None = Field(default=None, description="Provides the system prompt to use with the agent.")
|
|
45
48
|
additional_instructions: str | None = Field(default=None,
|
|
46
49
|
description="Additional instructions appended to the system prompt.")
|
|
@@ -70,6 +73,7 @@ async def tool_calling_agent_workflow(config: ToolCallAgentWorkflowConfig, build
|
|
|
70
73
|
tools=tools,
|
|
71
74
|
prompt=prompt,
|
|
72
75
|
detailed_logs=config.verbose,
|
|
76
|
+
log_response_max_chars=config.log_response_max_chars,
|
|
73
77
|
handle_tool_errors=config.handle_tool_errors).build_graph()
|
|
74
78
|
|
|
75
79
|
async def _response_fn(input_message: str) -> str:
|
|
@@ -89,7 +93,7 @@ async def tool_calling_agent_workflow(config: ToolCallAgentWorkflowConfig, build
|
|
|
89
93
|
output_message = state.messages[-1]
|
|
90
94
|
return output_message.content
|
|
91
95
|
except Exception as ex:
|
|
92
|
-
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)
|
|
93
97
|
if config.verbose:
|
|
94
98
|
return str(ex)
|
|
95
99
|
return "I seem to be having a problem."
|
|
@@ -97,6 +101,6 @@ async def tool_calling_agent_workflow(config: ToolCallAgentWorkflowConfig, build
|
|
|
97
101
|
try:
|
|
98
102
|
yield FunctionInfo.from_fn(_response_fn, description=config.description)
|
|
99
103
|
except GeneratorExit:
|
|
100
|
-
logger.exception("%s Workflow exited early!", AGENT_LOG_PREFIX
|
|
104
|
+
logger.exception("%s Workflow exited early!", AGENT_LOG_PREFIX)
|
|
101
105
|
finally:
|
|
102
106
|
logger.debug("%s Cleaning up react_agent workflow.", AGENT_LOG_PREFIX)
|
nat/builder/context.py
CHANGED
|
@@ -31,6 +31,7 @@ from nat.data_models.intermediate_step import IntermediateStep
|
|
|
31
31
|
from nat.data_models.intermediate_step import IntermediateStepPayload
|
|
32
32
|
from nat.data_models.intermediate_step import IntermediateStepType
|
|
33
33
|
from nat.data_models.intermediate_step import StreamEventData
|
|
34
|
+
from nat.data_models.intermediate_step import TraceMetadata
|
|
34
35
|
from nat.data_models.invocation_node import InvocationNode
|
|
35
36
|
from nat.runtime.user_metadata import RequestAttributes
|
|
36
37
|
from nat.utils.reactive.subject import Subject
|
|
@@ -174,7 +175,10 @@ class Context:
|
|
|
174
175
|
return self._context_state.user_message_id.get()
|
|
175
176
|
|
|
176
177
|
@contextmanager
|
|
177
|
-
def push_active_function(self,
|
|
178
|
+
def push_active_function(self,
|
|
179
|
+
function_name: str,
|
|
180
|
+
input_data: typing.Any | None,
|
|
181
|
+
metadata: dict[str, typing.Any] | TraceMetadata | None = None):
|
|
178
182
|
"""
|
|
179
183
|
Set the 'active_function' in context, push an invocation node,
|
|
180
184
|
AND create an OTel child span for that function call.
|
|
@@ -195,7 +199,8 @@ class Context:
|
|
|
195
199
|
IntermediateStepPayload(UUID=current_function_id,
|
|
196
200
|
event_type=IntermediateStepType.FUNCTION_START,
|
|
197
201
|
name=function_name,
|
|
198
|
-
data=StreamEventData(input=input_data)
|
|
202
|
+
data=StreamEventData(input=input_data),
|
|
203
|
+
metadata=metadata))
|
|
199
204
|
|
|
200
205
|
manager = ActiveFunctionContextManager()
|
|
201
206
|
|
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]):
|