nvidia-nat 1.1.0a20251020__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 (480) hide show
  1. aiq/__init__.py +66 -0
  2. nat/agent/__init__.py +0 -0
  3. nat/agent/base.py +265 -0
  4. nat/agent/dual_node.py +72 -0
  5. nat/agent/prompt_optimizer/__init__.py +0 -0
  6. nat/agent/prompt_optimizer/prompt.py +68 -0
  7. nat/agent/prompt_optimizer/register.py +149 -0
  8. nat/agent/react_agent/__init__.py +0 -0
  9. nat/agent/react_agent/agent.py +394 -0
  10. nat/agent/react_agent/output_parser.py +104 -0
  11. nat/agent/react_agent/prompt.py +44 -0
  12. nat/agent/react_agent/register.py +168 -0
  13. nat/agent/reasoning_agent/__init__.py +0 -0
  14. nat/agent/reasoning_agent/reasoning_agent.py +227 -0
  15. nat/agent/register.py +23 -0
  16. nat/agent/rewoo_agent/__init__.py +0 -0
  17. nat/agent/rewoo_agent/agent.py +593 -0
  18. nat/agent/rewoo_agent/prompt.py +107 -0
  19. nat/agent/rewoo_agent/register.py +175 -0
  20. nat/agent/tool_calling_agent/__init__.py +0 -0
  21. nat/agent/tool_calling_agent/agent.py +246 -0
  22. nat/agent/tool_calling_agent/register.py +129 -0
  23. nat/authentication/__init__.py +14 -0
  24. nat/authentication/api_key/__init__.py +14 -0
  25. nat/authentication/api_key/api_key_auth_provider.py +96 -0
  26. nat/authentication/api_key/api_key_auth_provider_config.py +124 -0
  27. nat/authentication/api_key/register.py +26 -0
  28. nat/authentication/credential_validator/__init__.py +14 -0
  29. nat/authentication/credential_validator/bearer_token_validator.py +557 -0
  30. nat/authentication/exceptions/__init__.py +14 -0
  31. nat/authentication/exceptions/api_key_exceptions.py +38 -0
  32. nat/authentication/http_basic_auth/__init__.py +0 -0
  33. nat/authentication/http_basic_auth/http_basic_auth_provider.py +81 -0
  34. nat/authentication/http_basic_auth/register.py +30 -0
  35. nat/authentication/interfaces.py +96 -0
  36. nat/authentication/oauth2/__init__.py +14 -0
  37. nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +140 -0
  38. nat/authentication/oauth2/oauth2_auth_code_flow_provider_config.py +39 -0
  39. nat/authentication/oauth2/oauth2_resource_server_config.py +124 -0
  40. nat/authentication/oauth2/register.py +25 -0
  41. nat/authentication/register.py +20 -0
  42. nat/builder/__init__.py +0 -0
  43. nat/builder/builder.py +317 -0
  44. nat/builder/component_utils.py +320 -0
  45. nat/builder/context.py +321 -0
  46. nat/builder/embedder.py +24 -0
  47. nat/builder/eval_builder.py +166 -0
  48. nat/builder/evaluator.py +29 -0
  49. nat/builder/framework_enum.py +25 -0
  50. nat/builder/front_end.py +73 -0
  51. nat/builder/function.py +714 -0
  52. nat/builder/function_base.py +380 -0
  53. nat/builder/function_info.py +625 -0
  54. nat/builder/intermediate_step_manager.py +206 -0
  55. nat/builder/llm.py +25 -0
  56. nat/builder/retriever.py +25 -0
  57. nat/builder/user_interaction_manager.py +78 -0
  58. nat/builder/workflow.py +160 -0
  59. nat/builder/workflow_builder.py +1365 -0
  60. nat/cli/__init__.py +14 -0
  61. nat/cli/cli_utils/__init__.py +0 -0
  62. nat/cli/cli_utils/config_override.py +231 -0
  63. nat/cli/cli_utils/validation.py +37 -0
  64. nat/cli/commands/__init__.py +0 -0
  65. nat/cli/commands/configure/__init__.py +0 -0
  66. nat/cli/commands/configure/channel/__init__.py +0 -0
  67. nat/cli/commands/configure/channel/add.py +28 -0
  68. nat/cli/commands/configure/channel/channel.py +34 -0
  69. nat/cli/commands/configure/channel/remove.py +30 -0
  70. nat/cli/commands/configure/channel/update.py +30 -0
  71. nat/cli/commands/configure/configure.py +33 -0
  72. nat/cli/commands/evaluate.py +139 -0
  73. nat/cli/commands/info/__init__.py +14 -0
  74. nat/cli/commands/info/info.py +47 -0
  75. nat/cli/commands/info/list_channels.py +32 -0
  76. nat/cli/commands/info/list_components.py +128 -0
  77. nat/cli/commands/mcp/__init__.py +14 -0
  78. nat/cli/commands/mcp/mcp.py +986 -0
  79. nat/cli/commands/object_store/__init__.py +14 -0
  80. nat/cli/commands/object_store/object_store.py +227 -0
  81. nat/cli/commands/optimize.py +90 -0
  82. nat/cli/commands/registry/__init__.py +14 -0
  83. nat/cli/commands/registry/publish.py +88 -0
  84. nat/cli/commands/registry/pull.py +118 -0
  85. nat/cli/commands/registry/registry.py +36 -0
  86. nat/cli/commands/registry/remove.py +108 -0
  87. nat/cli/commands/registry/search.py +153 -0
  88. nat/cli/commands/sizing/__init__.py +14 -0
  89. nat/cli/commands/sizing/calc.py +297 -0
  90. nat/cli/commands/sizing/sizing.py +27 -0
  91. nat/cli/commands/start.py +257 -0
  92. nat/cli/commands/uninstall.py +81 -0
  93. nat/cli/commands/validate.py +47 -0
  94. nat/cli/commands/workflow/__init__.py +14 -0
  95. nat/cli/commands/workflow/templates/__init__.py.j2 +0 -0
  96. nat/cli/commands/workflow/templates/config.yml.j2 +17 -0
  97. nat/cli/commands/workflow/templates/pyproject.toml.j2 +25 -0
  98. nat/cli/commands/workflow/templates/register.py.j2 +4 -0
  99. nat/cli/commands/workflow/templates/workflow.py.j2 +50 -0
  100. nat/cli/commands/workflow/workflow.py +37 -0
  101. nat/cli/commands/workflow/workflow_commands.py +403 -0
  102. nat/cli/entrypoint.py +141 -0
  103. nat/cli/main.py +60 -0
  104. nat/cli/register_workflow.py +522 -0
  105. nat/cli/type_registry.py +1069 -0
  106. nat/control_flow/__init__.py +0 -0
  107. nat/control_flow/register.py +20 -0
  108. nat/control_flow/router_agent/__init__.py +0 -0
  109. nat/control_flow/router_agent/agent.py +329 -0
  110. nat/control_flow/router_agent/prompt.py +48 -0
  111. nat/control_flow/router_agent/register.py +91 -0
  112. nat/control_flow/sequential_executor.py +166 -0
  113. nat/data_models/__init__.py +14 -0
  114. nat/data_models/agent.py +34 -0
  115. nat/data_models/api_server.py +843 -0
  116. nat/data_models/authentication.py +245 -0
  117. nat/data_models/common.py +171 -0
  118. nat/data_models/component.py +60 -0
  119. nat/data_models/component_ref.py +179 -0
  120. nat/data_models/config.py +434 -0
  121. nat/data_models/dataset_handler.py +169 -0
  122. nat/data_models/discovery_metadata.py +305 -0
  123. nat/data_models/embedder.py +27 -0
  124. nat/data_models/evaluate.py +130 -0
  125. nat/data_models/evaluator.py +26 -0
  126. nat/data_models/front_end.py +26 -0
  127. nat/data_models/function.py +64 -0
  128. nat/data_models/function_dependencies.py +80 -0
  129. nat/data_models/gated_field_mixin.py +242 -0
  130. nat/data_models/interactive.py +246 -0
  131. nat/data_models/intermediate_step.py +302 -0
  132. nat/data_models/invocation_node.py +38 -0
  133. nat/data_models/llm.py +27 -0
  134. nat/data_models/logging.py +26 -0
  135. nat/data_models/memory.py +27 -0
  136. nat/data_models/object_store.py +44 -0
  137. nat/data_models/optimizable.py +119 -0
  138. nat/data_models/optimizer.py +149 -0
  139. nat/data_models/profiler.py +54 -0
  140. nat/data_models/registry_handler.py +26 -0
  141. nat/data_models/retriever.py +30 -0
  142. nat/data_models/retry_mixin.py +35 -0
  143. nat/data_models/span.py +228 -0
  144. nat/data_models/step_adaptor.py +64 -0
  145. nat/data_models/streaming.py +33 -0
  146. nat/data_models/swe_bench_model.py +54 -0
  147. nat/data_models/telemetry_exporter.py +26 -0
  148. nat/data_models/temperature_mixin.py +44 -0
  149. nat/data_models/thinking_mixin.py +86 -0
  150. nat/data_models/top_p_mixin.py +44 -0
  151. nat/data_models/ttc_strategy.py +30 -0
  152. nat/embedder/__init__.py +0 -0
  153. nat/embedder/azure_openai_embedder.py +46 -0
  154. nat/embedder/nim_embedder.py +59 -0
  155. nat/embedder/openai_embedder.py +42 -0
  156. nat/embedder/register.py +22 -0
  157. nat/eval/__init__.py +14 -0
  158. nat/eval/config.py +62 -0
  159. nat/eval/dataset_handler/__init__.py +0 -0
  160. nat/eval/dataset_handler/dataset_downloader.py +106 -0
  161. nat/eval/dataset_handler/dataset_filter.py +52 -0
  162. nat/eval/dataset_handler/dataset_handler.py +431 -0
  163. nat/eval/evaluate.py +565 -0
  164. nat/eval/evaluator/__init__.py +14 -0
  165. nat/eval/evaluator/base_evaluator.py +77 -0
  166. nat/eval/evaluator/evaluator_model.py +58 -0
  167. nat/eval/intermediate_step_adapter.py +99 -0
  168. nat/eval/rag_evaluator/__init__.py +0 -0
  169. nat/eval/rag_evaluator/evaluate.py +178 -0
  170. nat/eval/rag_evaluator/register.py +143 -0
  171. nat/eval/register.py +26 -0
  172. nat/eval/remote_workflow.py +133 -0
  173. nat/eval/runners/__init__.py +14 -0
  174. nat/eval/runners/config.py +39 -0
  175. nat/eval/runners/multi_eval_runner.py +54 -0
  176. nat/eval/runtime_evaluator/__init__.py +14 -0
  177. nat/eval/runtime_evaluator/evaluate.py +123 -0
  178. nat/eval/runtime_evaluator/register.py +100 -0
  179. nat/eval/runtime_event_subscriber.py +52 -0
  180. nat/eval/swe_bench_evaluator/__init__.py +0 -0
  181. nat/eval/swe_bench_evaluator/evaluate.py +215 -0
  182. nat/eval/swe_bench_evaluator/register.py +36 -0
  183. nat/eval/trajectory_evaluator/__init__.py +0 -0
  184. nat/eval/trajectory_evaluator/evaluate.py +75 -0
  185. nat/eval/trajectory_evaluator/register.py +40 -0
  186. nat/eval/tunable_rag_evaluator/__init__.py +0 -0
  187. nat/eval/tunable_rag_evaluator/evaluate.py +242 -0
  188. nat/eval/tunable_rag_evaluator/register.py +52 -0
  189. nat/eval/usage_stats.py +41 -0
  190. nat/eval/utils/__init__.py +0 -0
  191. nat/eval/utils/eval_trace_ctx.py +89 -0
  192. nat/eval/utils/output_uploader.py +140 -0
  193. nat/eval/utils/tqdm_position_registry.py +40 -0
  194. nat/eval/utils/weave_eval.py +193 -0
  195. nat/experimental/__init__.py +0 -0
  196. nat/experimental/decorators/__init__.py +0 -0
  197. nat/experimental/decorators/experimental_warning_decorator.py +154 -0
  198. nat/experimental/test_time_compute/__init__.py +0 -0
  199. nat/experimental/test_time_compute/editing/__init__.py +0 -0
  200. nat/experimental/test_time_compute/editing/iterative_plan_refinement_editor.py +147 -0
  201. nat/experimental/test_time_compute/editing/llm_as_a_judge_editor.py +204 -0
  202. nat/experimental/test_time_compute/editing/motivation_aware_summarization.py +107 -0
  203. nat/experimental/test_time_compute/functions/__init__.py +0 -0
  204. nat/experimental/test_time_compute/functions/execute_score_select_function.py +105 -0
  205. nat/experimental/test_time_compute/functions/plan_select_execute_function.py +228 -0
  206. nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +205 -0
  207. nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +146 -0
  208. nat/experimental/test_time_compute/models/__init__.py +0 -0
  209. nat/experimental/test_time_compute/models/editor_config.py +132 -0
  210. nat/experimental/test_time_compute/models/scoring_config.py +112 -0
  211. nat/experimental/test_time_compute/models/search_config.py +120 -0
  212. nat/experimental/test_time_compute/models/selection_config.py +154 -0
  213. nat/experimental/test_time_compute/models/stage_enums.py +43 -0
  214. nat/experimental/test_time_compute/models/strategy_base.py +67 -0
  215. nat/experimental/test_time_compute/models/tool_use_config.py +41 -0
  216. nat/experimental/test_time_compute/models/ttc_item.py +48 -0
  217. nat/experimental/test_time_compute/register.py +35 -0
  218. nat/experimental/test_time_compute/scoring/__init__.py +0 -0
  219. nat/experimental/test_time_compute/scoring/llm_based_agent_scorer.py +168 -0
  220. nat/experimental/test_time_compute/scoring/llm_based_plan_scorer.py +168 -0
  221. nat/experimental/test_time_compute/scoring/motivation_aware_scorer.py +111 -0
  222. nat/experimental/test_time_compute/search/__init__.py +0 -0
  223. nat/experimental/test_time_compute/search/multi_llm_planner.py +128 -0
  224. nat/experimental/test_time_compute/search/multi_query_retrieval_search.py +122 -0
  225. nat/experimental/test_time_compute/search/single_shot_multi_plan_planner.py +128 -0
  226. nat/experimental/test_time_compute/selection/__init__.py +0 -0
  227. nat/experimental/test_time_compute/selection/best_of_n_selector.py +63 -0
  228. nat/experimental/test_time_compute/selection/llm_based_agent_output_selector.py +131 -0
  229. nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +157 -0
  230. nat/experimental/test_time_compute/selection/llm_based_plan_selector.py +128 -0
  231. nat/experimental/test_time_compute/selection/threshold_selector.py +58 -0
  232. nat/front_ends/__init__.py +14 -0
  233. nat/front_ends/console/__init__.py +14 -0
  234. nat/front_ends/console/authentication_flow_handler.py +285 -0
  235. nat/front_ends/console/console_front_end_config.py +32 -0
  236. nat/front_ends/console/console_front_end_plugin.py +108 -0
  237. nat/front_ends/console/register.py +25 -0
  238. nat/front_ends/cron/__init__.py +14 -0
  239. nat/front_ends/fastapi/__init__.py +14 -0
  240. nat/front_ends/fastapi/auth_flow_handlers/__init__.py +0 -0
  241. nat/front_ends/fastapi/auth_flow_handlers/http_flow_handler.py +27 -0
  242. nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +142 -0
  243. nat/front_ends/fastapi/dask_client_mixin.py +65 -0
  244. nat/front_ends/fastapi/fastapi_front_end_config.py +272 -0
  245. nat/front_ends/fastapi/fastapi_front_end_controller.py +68 -0
  246. nat/front_ends/fastapi/fastapi_front_end_plugin.py +247 -0
  247. nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +1257 -0
  248. nat/front_ends/fastapi/html_snippets/__init__.py +14 -0
  249. nat/front_ends/fastapi/html_snippets/auth_code_grant_success.py +35 -0
  250. nat/front_ends/fastapi/intermediate_steps_subscriber.py +80 -0
  251. nat/front_ends/fastapi/job_store.py +602 -0
  252. nat/front_ends/fastapi/main.py +64 -0
  253. nat/front_ends/fastapi/message_handler.py +344 -0
  254. nat/front_ends/fastapi/message_validator.py +351 -0
  255. nat/front_ends/fastapi/register.py +25 -0
  256. nat/front_ends/fastapi/response_helpers.py +195 -0
  257. nat/front_ends/fastapi/step_adaptor.py +319 -0
  258. nat/front_ends/fastapi/utils.py +57 -0
  259. nat/front_ends/mcp/__init__.py +14 -0
  260. nat/front_ends/mcp/introspection_token_verifier.py +73 -0
  261. nat/front_ends/mcp/mcp_front_end_config.py +90 -0
  262. nat/front_ends/mcp/mcp_front_end_plugin.py +113 -0
  263. nat/front_ends/mcp/mcp_front_end_plugin_worker.py +268 -0
  264. nat/front_ends/mcp/memory_profiler.py +320 -0
  265. nat/front_ends/mcp/register.py +27 -0
  266. nat/front_ends/mcp/tool_converter.py +290 -0
  267. nat/front_ends/register.py +21 -0
  268. nat/front_ends/simple_base/__init__.py +14 -0
  269. nat/front_ends/simple_base/simple_front_end_plugin_base.py +56 -0
  270. nat/llm/__init__.py +0 -0
  271. nat/llm/aws_bedrock_llm.py +69 -0
  272. nat/llm/azure_openai_llm.py +57 -0
  273. nat/llm/litellm_llm.py +69 -0
  274. nat/llm/nim_llm.py +58 -0
  275. nat/llm/openai_llm.py +54 -0
  276. nat/llm/register.py +27 -0
  277. nat/llm/utils/__init__.py +14 -0
  278. nat/llm/utils/env_config_value.py +93 -0
  279. nat/llm/utils/error.py +17 -0
  280. nat/llm/utils/thinking.py +215 -0
  281. nat/memory/__init__.py +20 -0
  282. nat/memory/interfaces.py +183 -0
  283. nat/memory/models.py +112 -0
  284. nat/meta/pypi.md +58 -0
  285. nat/object_store/__init__.py +20 -0
  286. nat/object_store/in_memory_object_store.py +76 -0
  287. nat/object_store/interfaces.py +84 -0
  288. nat/object_store/models.py +38 -0
  289. nat/object_store/register.py +19 -0
  290. nat/observability/__init__.py +14 -0
  291. nat/observability/exporter/__init__.py +14 -0
  292. nat/observability/exporter/base_exporter.py +449 -0
  293. nat/observability/exporter/exporter.py +78 -0
  294. nat/observability/exporter/file_exporter.py +33 -0
  295. nat/observability/exporter/processing_exporter.py +550 -0
  296. nat/observability/exporter/raw_exporter.py +52 -0
  297. nat/observability/exporter/span_exporter.py +308 -0
  298. nat/observability/exporter_manager.py +335 -0
  299. nat/observability/mixin/__init__.py +14 -0
  300. nat/observability/mixin/batch_config_mixin.py +26 -0
  301. nat/observability/mixin/collector_config_mixin.py +23 -0
  302. nat/observability/mixin/file_mixin.py +288 -0
  303. nat/observability/mixin/file_mode.py +23 -0
  304. nat/observability/mixin/redaction_config_mixin.py +42 -0
  305. nat/observability/mixin/resource_conflict_mixin.py +134 -0
  306. nat/observability/mixin/serialize_mixin.py +61 -0
  307. nat/observability/mixin/tagging_config_mixin.py +62 -0
  308. nat/observability/mixin/type_introspection_mixin.py +496 -0
  309. nat/observability/processor/__init__.py +14 -0
  310. nat/observability/processor/batching_processor.py +308 -0
  311. nat/observability/processor/callback_processor.py +42 -0
  312. nat/observability/processor/falsy_batch_filter_processor.py +55 -0
  313. nat/observability/processor/intermediate_step_serializer.py +28 -0
  314. nat/observability/processor/processor.py +74 -0
  315. nat/observability/processor/processor_factory.py +70 -0
  316. nat/observability/processor/redaction/__init__.py +24 -0
  317. nat/observability/processor/redaction/contextual_redaction_processor.py +125 -0
  318. nat/observability/processor/redaction/contextual_span_redaction_processor.py +66 -0
  319. nat/observability/processor/redaction/redaction_processor.py +177 -0
  320. nat/observability/processor/redaction/span_header_redaction_processor.py +92 -0
  321. nat/observability/processor/span_tagging_processor.py +68 -0
  322. nat/observability/register.py +114 -0
  323. nat/observability/utils/__init__.py +14 -0
  324. nat/observability/utils/dict_utils.py +236 -0
  325. nat/observability/utils/time_utils.py +31 -0
  326. nat/plugins/.namespace +1 -0
  327. nat/profiler/__init__.py +0 -0
  328. nat/profiler/calc/__init__.py +14 -0
  329. nat/profiler/calc/calc_runner.py +626 -0
  330. nat/profiler/calc/calculations.py +288 -0
  331. nat/profiler/calc/data_models.py +188 -0
  332. nat/profiler/calc/plot.py +345 -0
  333. nat/profiler/callbacks/__init__.py +0 -0
  334. nat/profiler/callbacks/agno_callback_handler.py +295 -0
  335. nat/profiler/callbacks/base_callback_class.py +20 -0
  336. nat/profiler/callbacks/langchain_callback_handler.py +297 -0
  337. nat/profiler/callbacks/llama_index_callback_handler.py +205 -0
  338. nat/profiler/callbacks/semantic_kernel_callback_handler.py +238 -0
  339. nat/profiler/callbacks/token_usage_base_model.py +27 -0
  340. nat/profiler/data_frame_row.py +51 -0
  341. nat/profiler/data_models.py +24 -0
  342. nat/profiler/decorators/__init__.py +0 -0
  343. nat/profiler/decorators/framework_wrapper.py +180 -0
  344. nat/profiler/decorators/function_tracking.py +411 -0
  345. nat/profiler/forecasting/__init__.py +0 -0
  346. nat/profiler/forecasting/config.py +18 -0
  347. nat/profiler/forecasting/model_trainer.py +75 -0
  348. nat/profiler/forecasting/models/__init__.py +22 -0
  349. nat/profiler/forecasting/models/forecasting_base_model.py +42 -0
  350. nat/profiler/forecasting/models/linear_model.py +197 -0
  351. nat/profiler/forecasting/models/random_forest_regressor.py +269 -0
  352. nat/profiler/inference_metrics_model.py +28 -0
  353. nat/profiler/inference_optimization/__init__.py +0 -0
  354. nat/profiler/inference_optimization/bottleneck_analysis/__init__.py +0 -0
  355. nat/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +460 -0
  356. nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +258 -0
  357. nat/profiler/inference_optimization/data_models.py +386 -0
  358. nat/profiler/inference_optimization/experimental/__init__.py +0 -0
  359. nat/profiler/inference_optimization/experimental/concurrency_spike_analysis.py +468 -0
  360. nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +404 -0
  361. nat/profiler/inference_optimization/llm_metrics.py +212 -0
  362. nat/profiler/inference_optimization/prompt_caching.py +163 -0
  363. nat/profiler/inference_optimization/token_uniqueness.py +107 -0
  364. nat/profiler/inference_optimization/workflow_runtimes.py +72 -0
  365. nat/profiler/intermediate_property_adapter.py +102 -0
  366. nat/profiler/parameter_optimization/__init__.py +0 -0
  367. nat/profiler/parameter_optimization/optimizable_utils.py +93 -0
  368. nat/profiler/parameter_optimization/optimizer_runtime.py +67 -0
  369. nat/profiler/parameter_optimization/parameter_optimizer.py +153 -0
  370. nat/profiler/parameter_optimization/parameter_selection.py +107 -0
  371. nat/profiler/parameter_optimization/pareto_visualizer.py +380 -0
  372. nat/profiler/parameter_optimization/prompt_optimizer.py +384 -0
  373. nat/profiler/parameter_optimization/update_helpers.py +66 -0
  374. nat/profiler/profile_runner.py +478 -0
  375. nat/profiler/utils.py +186 -0
  376. nat/registry_handlers/__init__.py +0 -0
  377. nat/registry_handlers/local/__init__.py +0 -0
  378. nat/registry_handlers/local/local_handler.py +176 -0
  379. nat/registry_handlers/local/register_local.py +37 -0
  380. nat/registry_handlers/metadata_factory.py +60 -0
  381. nat/registry_handlers/package_utils.py +570 -0
  382. nat/registry_handlers/pypi/__init__.py +0 -0
  383. nat/registry_handlers/pypi/pypi_handler.py +248 -0
  384. nat/registry_handlers/pypi/register_pypi.py +40 -0
  385. nat/registry_handlers/register.py +20 -0
  386. nat/registry_handlers/registry_handler_base.py +157 -0
  387. nat/registry_handlers/rest/__init__.py +0 -0
  388. nat/registry_handlers/rest/register_rest.py +56 -0
  389. nat/registry_handlers/rest/rest_handler.py +236 -0
  390. nat/registry_handlers/schemas/__init__.py +0 -0
  391. nat/registry_handlers/schemas/headers.py +42 -0
  392. nat/registry_handlers/schemas/package.py +68 -0
  393. nat/registry_handlers/schemas/publish.py +68 -0
  394. nat/registry_handlers/schemas/pull.py +82 -0
  395. nat/registry_handlers/schemas/remove.py +36 -0
  396. nat/registry_handlers/schemas/search.py +91 -0
  397. nat/registry_handlers/schemas/status.py +47 -0
  398. nat/retriever/__init__.py +0 -0
  399. nat/retriever/interface.py +41 -0
  400. nat/retriever/milvus/__init__.py +14 -0
  401. nat/retriever/milvus/register.py +81 -0
  402. nat/retriever/milvus/retriever.py +228 -0
  403. nat/retriever/models.py +77 -0
  404. nat/retriever/nemo_retriever/__init__.py +14 -0
  405. nat/retriever/nemo_retriever/register.py +60 -0
  406. nat/retriever/nemo_retriever/retriever.py +190 -0
  407. nat/retriever/register.py +21 -0
  408. nat/runtime/__init__.py +14 -0
  409. nat/runtime/loader.py +220 -0
  410. nat/runtime/runner.py +292 -0
  411. nat/runtime/session.py +223 -0
  412. nat/runtime/user_metadata.py +130 -0
  413. nat/settings/__init__.py +0 -0
  414. nat/settings/global_settings.py +329 -0
  415. nat/test/.namespace +1 -0
  416. nat/tool/__init__.py +0 -0
  417. nat/tool/chat_completion.py +77 -0
  418. nat/tool/code_execution/README.md +151 -0
  419. nat/tool/code_execution/__init__.py +0 -0
  420. nat/tool/code_execution/code_sandbox.py +267 -0
  421. nat/tool/code_execution/local_sandbox/.gitignore +1 -0
  422. nat/tool/code_execution/local_sandbox/Dockerfile.sandbox +60 -0
  423. nat/tool/code_execution/local_sandbox/__init__.py +13 -0
  424. nat/tool/code_execution/local_sandbox/local_sandbox_server.py +198 -0
  425. nat/tool/code_execution/local_sandbox/sandbox.requirements.txt +6 -0
  426. nat/tool/code_execution/local_sandbox/start_local_sandbox.sh +50 -0
  427. nat/tool/code_execution/register.py +74 -0
  428. nat/tool/code_execution/test_code_execution_sandbox.py +414 -0
  429. nat/tool/code_execution/utils.py +100 -0
  430. nat/tool/datetime_tools.py +82 -0
  431. nat/tool/document_search.py +141 -0
  432. nat/tool/github_tools.py +450 -0
  433. nat/tool/memory_tools/__init__.py +0 -0
  434. nat/tool/memory_tools/add_memory_tool.py +79 -0
  435. nat/tool/memory_tools/delete_memory_tool.py +66 -0
  436. nat/tool/memory_tools/get_memory_tool.py +72 -0
  437. nat/tool/nvidia_rag.py +95 -0
  438. nat/tool/register.py +31 -0
  439. nat/tool/retriever.py +95 -0
  440. nat/tool/server_tools.py +66 -0
  441. nat/utils/__init__.py +0 -0
  442. nat/utils/callable_utils.py +70 -0
  443. nat/utils/data_models/__init__.py +0 -0
  444. nat/utils/data_models/schema_validator.py +58 -0
  445. nat/utils/debugging_utils.py +43 -0
  446. nat/utils/decorators.py +210 -0
  447. nat/utils/dump_distro_mapping.py +32 -0
  448. nat/utils/exception_handlers/__init__.py +0 -0
  449. nat/utils/exception_handlers/automatic_retries.py +342 -0
  450. nat/utils/exception_handlers/schemas.py +114 -0
  451. nat/utils/io/__init__.py +0 -0
  452. nat/utils/io/model_processing.py +28 -0
  453. nat/utils/io/yaml_tools.py +119 -0
  454. nat/utils/log_levels.py +25 -0
  455. nat/utils/log_utils.py +37 -0
  456. nat/utils/metadata_utils.py +74 -0
  457. nat/utils/optional_imports.py +142 -0
  458. nat/utils/producer_consumer_queue.py +178 -0
  459. nat/utils/reactive/__init__.py +0 -0
  460. nat/utils/reactive/base/__init__.py +0 -0
  461. nat/utils/reactive/base/observable_base.py +65 -0
  462. nat/utils/reactive/base/observer_base.py +55 -0
  463. nat/utils/reactive/base/subject_base.py +79 -0
  464. nat/utils/reactive/observable.py +59 -0
  465. nat/utils/reactive/observer.py +76 -0
  466. nat/utils/reactive/subject.py +131 -0
  467. nat/utils/reactive/subscription.py +49 -0
  468. nat/utils/settings/__init__.py +0 -0
  469. nat/utils/settings/global_settings.py +195 -0
  470. nat/utils/string_utils.py +38 -0
  471. nat/utils/type_converter.py +299 -0
  472. nat/utils/type_utils.py +488 -0
  473. nat/utils/url_utils.py +27 -0
  474. nvidia_nat-1.1.0a20251020.dist-info/METADATA +195 -0
  475. nvidia_nat-1.1.0a20251020.dist-info/RECORD +480 -0
  476. nvidia_nat-1.1.0a20251020.dist-info/WHEEL +5 -0
  477. nvidia_nat-1.1.0a20251020.dist-info/entry_points.txt +22 -0
  478. nvidia_nat-1.1.0a20251020.dist-info/licenses/LICENSE-3rd-party.txt +5478 -0
  479. nvidia_nat-1.1.0a20251020.dist-info/licenses/LICENSE.md +201 -0
  480. nvidia_nat-1.1.0a20251020.dist-info/top_level.txt +2 -0
@@ -0,0 +1,394 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import json
17
+ import logging
18
+ import re
19
+ import typing
20
+ from json import JSONDecodeError
21
+
22
+ from langchain_core.agents import AgentAction
23
+ from langchain_core.agents import AgentFinish
24
+ from langchain_core.callbacks.base import AsyncCallbackHandler
25
+ from langchain_core.language_models import BaseChatModel
26
+ from langchain_core.language_models import LanguageModelInput
27
+ from langchain_core.messages.ai import AIMessage
28
+ from langchain_core.messages.base import BaseMessage
29
+ from langchain_core.messages.human import HumanMessage
30
+ from langchain_core.messages.tool import ToolMessage
31
+ from langchain_core.prompts import ChatPromptTemplate
32
+ from langchain_core.prompts import MessagesPlaceholder
33
+ from langchain_core.runnables import Runnable
34
+ from langchain_core.runnables.config import RunnableConfig
35
+ from langchain_core.tools import BaseTool
36
+ from pydantic import BaseModel
37
+ from pydantic import Field
38
+
39
+ from nat.agent.base import AGENT_CALL_LOG_MESSAGE
40
+ from nat.agent.base import AGENT_LOG_PREFIX
41
+ from nat.agent.base import INPUT_SCHEMA_MESSAGE
42
+ from nat.agent.base import NO_INPUT_ERROR_MESSAGE
43
+ from nat.agent.base import TOOL_NOT_FOUND_ERROR_MESSAGE
44
+ from nat.agent.base import AgentDecision
45
+ from nat.agent.dual_node import DualNodeAgent
46
+ from nat.agent.react_agent.output_parser import ReActOutputParser
47
+ from nat.agent.react_agent.output_parser import ReActOutputParserException
48
+ from nat.agent.react_agent.prompt import SYSTEM_PROMPT
49
+ from nat.agent.react_agent.prompt import USER_PROMPT
50
+
51
+ if typing.TYPE_CHECKING:
52
+ from nat.agent.react_agent.register import ReActAgentWorkflowConfig
53
+
54
+ logger = logging.getLogger(__name__)
55
+
56
+
57
+ class ReActGraphState(BaseModel):
58
+ """State schema for the ReAct Agent Graph"""
59
+ messages: list[BaseMessage] = Field(default_factory=list) # input and output of the ReAct Agent
60
+ agent_scratchpad: list[AgentAction] = Field(default_factory=list) # agent thoughts / intermediate steps
61
+ tool_responses: list[BaseMessage] = Field(default_factory=list) # the responses from any tool calls
62
+ final_answer: str | None = Field(default=None) # the final answer from the ReAct Agent
63
+
64
+
65
+ class ReActAgentGraph(DualNodeAgent):
66
+ """Configurable LangGraph ReAct Agent. A ReAct Agent performs reasoning inbetween tool calls, and utilizes the tool
67
+ names and descriptions to select the optimal tool. Supports retrying on output parsing errors. Argument
68
+ "detailed_logs" toggles logging of inputs, outputs, and intermediate steps."""
69
+
70
+ def __init__(self,
71
+ llm: BaseChatModel,
72
+ prompt: ChatPromptTemplate,
73
+ tools: list[BaseTool],
74
+ use_tool_schema: bool = True,
75
+ callbacks: list[AsyncCallbackHandler] | None = None,
76
+ detailed_logs: bool = False,
77
+ log_response_max_chars: int = 1000,
78
+ retry_agent_response_parsing_errors: bool = True,
79
+ parse_agent_response_max_retries: int = 1,
80
+ tool_call_max_retries: int = 1,
81
+ pass_tool_call_errors_to_agent: bool = True,
82
+ normalize_tool_input_quotes: bool = True):
83
+ super().__init__(llm=llm,
84
+ tools=tools,
85
+ callbacks=callbacks,
86
+ detailed_logs=detailed_logs,
87
+ log_response_max_chars=log_response_max_chars)
88
+ self.parse_agent_response_max_retries = (parse_agent_response_max_retries
89
+ if retry_agent_response_parsing_errors else 1)
90
+ self.tool_call_max_retries = tool_call_max_retries
91
+ self.pass_tool_call_errors_to_agent = pass_tool_call_errors_to_agent
92
+ self.normalize_tool_input_quotes = normalize_tool_input_quotes
93
+ logger.debug(
94
+ "%s Filling the prompt variables 'tools' and 'tool_names', using the tools provided in the config.",
95
+ AGENT_LOG_PREFIX)
96
+ tool_names = ",".join([tool.name for tool in tools[:-1]]) + ',' + tools[-1].name # prevent trailing ","
97
+ if not use_tool_schema:
98
+ tool_names_and_descriptions = "\n".join(
99
+ [f"{tool.name}: {tool.description}"
100
+ for tool in tools[:-1]]) + "\n" + f"{tools[-1].name}: {tools[-1].description}" # prevent trailing "\n"
101
+ else:
102
+ logger.debug("%s Adding the tools' input schema to the tools' description", AGENT_LOG_PREFIX)
103
+ tool_names_and_descriptions = "\n".join([
104
+ f"{tool.name}: {tool.description}. {INPUT_SCHEMA_MESSAGE.format(schema=tool.input_schema.model_fields)}"
105
+ for tool in tools[:-1]
106
+ ]) + "\n" + (f"{tools[-1].name}: {tools[-1].description}. "
107
+ f"{INPUT_SCHEMA_MESSAGE.format(schema=tools[-1].input_schema.model_fields)}")
108
+ prompt = prompt.partial(tools=tool_names_and_descriptions, tool_names=tool_names)
109
+ # construct the ReAct Agent
110
+ self.agent = prompt | self._maybe_bind_llm_and_yield()
111
+ self.tools_dict = {tool.name: tool for tool in tools}
112
+ logger.debug("%s Initialized ReAct Agent Graph", AGENT_LOG_PREFIX)
113
+
114
+ def _maybe_bind_llm_and_yield(self) -> Runnable[LanguageModelInput, BaseMessage]:
115
+ """
116
+ Bind additional parameters to the LLM if needed
117
+ - if the LLM is a smart model, no need to bind any additional parameters
118
+ - if the LLM is a non-smart model, bind a stop sequence to the LLM
119
+
120
+ Returns:
121
+ Runnable[LanguageModelInput, BaseMessage]: The LLM with any additional parameters bound.
122
+ """
123
+ # models that don't need (or don't support)a stop sequence
124
+ smart_models = re.compile(r"gpt-?5", re.IGNORECASE)
125
+ if any(smart_models.search(getattr(self.llm, model, "")) for model in ["model", "model_name"]):
126
+ # no need to bind any additional parameters to the LLM
127
+ return self.llm
128
+ # add a stop sequence to the LLM
129
+ return self.llm.bind(stop=["Observation:"])
130
+
131
+ def _get_tool(self, tool_name: str):
132
+ try:
133
+ return self.tools_dict.get(tool_name)
134
+ except Exception as ex:
135
+ logger.error("%s Unable to find tool with the name %s\n%s", AGENT_LOG_PREFIX, tool_name, ex)
136
+ raise
137
+
138
+ async def agent_node(self, state: ReActGraphState):
139
+ try:
140
+ logger.debug("%s Starting the ReAct Agent Node", AGENT_LOG_PREFIX)
141
+ # keeping a working state allows us to resolve parsing errors without polluting the agent scratchpad
142
+ # the agent "forgets" about the parsing error after solving it - prevents hallucinations in next cycles
143
+ working_state = []
144
+ # Starting from attempt 1 instead of 0 for logging
145
+ for attempt in range(1, self.parse_agent_response_max_retries + 1):
146
+ # the first time we are invoking the ReAct Agent, it won't have any intermediate steps / agent thoughts
147
+ if len(state.agent_scratchpad) == 0 and len(working_state) == 0:
148
+ # the user input comes from the "messages" state channel
149
+ if len(state.messages) == 0:
150
+ raise RuntimeError('No input received in state: "messages"')
151
+ # to check is any human input passed or not, if no input passed Agent will return the state
152
+ content = str(state.messages[-1].content)
153
+ if content.strip() == "":
154
+ logger.error("%s No human input passed to the agent.", AGENT_LOG_PREFIX)
155
+ state.messages += [AIMessage(content=NO_INPUT_ERROR_MESSAGE)]
156
+ return state
157
+ question = content
158
+ logger.debug("%s Querying agent, attempt: %s", AGENT_LOG_PREFIX, attempt)
159
+ chat_history = self._get_chat_history(state.messages)
160
+ output_message = await self._stream_llm(
161
+ self.agent,
162
+ {
163
+ "question": question, "chat_history": chat_history
164
+ },
165
+ RunnableConfig(callbacks=self.callbacks) # type: ignore
166
+ )
167
+
168
+ if self.detailed_logs:
169
+ logger.info(AGENT_CALL_LOG_MESSAGE, question, output_message.content)
170
+ else:
171
+ # ReAct Agents require agentic cycles
172
+ # in an agentic cycle, preserve the agent's thoughts from the previous cycles,
173
+ # and give the agent the response from the tool it called
174
+ agent_scratchpad = []
175
+ for index, intermediate_step in enumerate(state.agent_scratchpad):
176
+ agent_thoughts = AIMessage(content=intermediate_step.log)
177
+ agent_scratchpad.append(agent_thoughts)
178
+ tool_response_content = str(state.tool_responses[index].content)
179
+ tool_response = HumanMessage(content=tool_response_content)
180
+ agent_scratchpad.append(tool_response)
181
+ agent_scratchpad += working_state
182
+ chat_history = self._get_chat_history(state.messages)
183
+ question = str(state.messages[-1].content)
184
+ logger.debug("%s Querying agent, attempt: %s", AGENT_LOG_PREFIX, attempt)
185
+
186
+ output_message = await self._stream_llm(
187
+ self.agent, {
188
+ "question": question, "agent_scratchpad": agent_scratchpad, "chat_history": chat_history
189
+ },
190
+ RunnableConfig(callbacks=self.callbacks))
191
+
192
+ if self.detailed_logs:
193
+ logger.info(AGENT_CALL_LOG_MESSAGE, question, output_message.content)
194
+ logger.debug("%s The agent's scratchpad (with tool result) was:\n%s",
195
+ AGENT_LOG_PREFIX,
196
+ agent_scratchpad)
197
+ try:
198
+ # check if the agent has the final answer yet
199
+ logger.debug("%s Successfully obtained agent response. Parsing agent's response", AGENT_LOG_PREFIX)
200
+ agent_output = await ReActOutputParser().aparse(output_message.content)
201
+ logger.debug("%s Successfully parsed agent response after %s attempts", AGENT_LOG_PREFIX, attempt)
202
+ if isinstance(agent_output, AgentFinish):
203
+ final_answer = agent_output.return_values.get('output', output_message.content)
204
+ logger.debug("%s The agent has finished, and has the final answer", AGENT_LOG_PREFIX)
205
+ # this is where we handle the final output of the Agent, we can clean-up/format/postprocess here
206
+ # the final answer goes in the "messages" state channel
207
+ state.messages += [AIMessage(content=final_answer)]
208
+ state.final_answer = final_answer
209
+ else:
210
+ # the agent wants to call a tool, ensure the thoughts are preserved for the next agentic cycle
211
+ agent_output.log = output_message.content
212
+ logger.debug("%s The agent wants to call a tool: %s", AGENT_LOG_PREFIX, agent_output.tool)
213
+ state.agent_scratchpad += [agent_output]
214
+
215
+ return state
216
+ except ReActOutputParserException as ex:
217
+ # the agent output did not meet the expected ReAct output format. This can happen for a few reasons:
218
+ # the agent mentioned a tool, but already has the final answer, this can happen with Llama models
219
+ # - the ReAct Agent already has the answer, and is reflecting on how it obtained the answer
220
+ # the agent might have also missed Action or Action Input in its output
221
+ logger.debug("%s Error parsing agent output\nObservation:%s\nAgent Output:\n%s",
222
+ AGENT_LOG_PREFIX,
223
+ ex.observation,
224
+ output_message.content)
225
+ if attempt == self.parse_agent_response_max_retries:
226
+ logger.warning(
227
+ "%s Failed to parse agent output after %d attempts, consider enabling or "
228
+ "increasing parse_agent_response_max_retries",
229
+ AGENT_LOG_PREFIX,
230
+ attempt)
231
+ # the final answer goes in the "messages" state channel
232
+ combined_content = str(ex.observation) + '\n' + str(output_message.content)
233
+ output_message.content = combined_content
234
+ state.messages += [output_message]
235
+ return state
236
+ # retry parsing errors, if configured
237
+ logger.info("%s Retrying ReAct Agent, including output parsing Observation", AGENT_LOG_PREFIX)
238
+ working_state.append(output_message)
239
+ working_state.append(HumanMessage(content=str(ex.observation)))
240
+ except Exception as ex:
241
+ logger.error("%s Failed to call agent_node: %s", AGENT_LOG_PREFIX, ex)
242
+ raise
243
+
244
+ async def conditional_edge(self, state: ReActGraphState):
245
+ try:
246
+ logger.debug("%s Starting the ReAct Conditional Edge", AGENT_LOG_PREFIX)
247
+ if state.final_answer:
248
+ # the ReAct Agent has finished executing
249
+ logger.debug("%s Final answer:\n%s", AGENT_LOG_PREFIX, state.final_answer)
250
+ return AgentDecision.END
251
+ # else the agent wants to call a tool
252
+ agent_output = state.agent_scratchpad[-1]
253
+ logger.debug("%s The agent wants to call: %s with input: %s",
254
+ AGENT_LOG_PREFIX,
255
+ agent_output.tool,
256
+ agent_output.tool_input)
257
+ return AgentDecision.TOOL
258
+ except Exception as ex:
259
+ logger.exception("Failed to determine whether agent is calling a tool: %s", ex)
260
+ logger.warning("%s Ending graph traversal", AGENT_LOG_PREFIX)
261
+ return AgentDecision.END
262
+
263
+ async def tool_node(self, state: ReActGraphState):
264
+
265
+ logger.debug("%s Starting the Tool Call Node", AGENT_LOG_PREFIX)
266
+ if len(state.agent_scratchpad) == 0:
267
+ raise RuntimeError('No tool input received in state: "agent_scratchpad"')
268
+ agent_thoughts = state.agent_scratchpad[-1]
269
+ # the agent can run any installed tool, simply install the tool and add it to the config file
270
+ requested_tool = self._get_tool(agent_thoughts.tool)
271
+ if not requested_tool:
272
+ configured_tool_names = list(self.tools_dict.keys())
273
+ logger.warning(
274
+ "%s ReAct Agent wants to call tool %s. In the ReAct Agent's configuration within the config file,"
275
+ "there is no tool with that name: %s",
276
+ AGENT_LOG_PREFIX,
277
+ agent_thoughts.tool,
278
+ configured_tool_names)
279
+ tool_response = ToolMessage(name='agent_error',
280
+ tool_call_id='agent_error',
281
+ content=TOOL_NOT_FOUND_ERROR_MESSAGE.format(tool_name=agent_thoughts.tool,
282
+ tools=configured_tool_names))
283
+ state.tool_responses += [tool_response]
284
+ return state
285
+
286
+ logger.debug("%s Calling tool %s with input: %s",
287
+ AGENT_LOG_PREFIX,
288
+ requested_tool.name,
289
+ agent_thoughts.tool_input)
290
+
291
+ # Run the tool. Try to use structured input, if possible.
292
+ tool_input_str = agent_thoughts.tool_input.strip()
293
+
294
+ try:
295
+ tool_input = json.loads(tool_input_str) if tool_input_str != 'None' else tool_input_str
296
+ logger.debug("%s Successfully parsed structured tool input from Action Input", AGENT_LOG_PREFIX)
297
+
298
+ except JSONDecodeError as original_ex:
299
+ if self.normalize_tool_input_quotes:
300
+ # If initial JSON parsing fails, try with quote normalization as a fallback
301
+ normalized_str = tool_input_str.replace("'", '"')
302
+ try:
303
+ tool_input = json.loads(normalized_str)
304
+ logger.debug("%s Successfully parsed structured tool input after quote normalization",
305
+ AGENT_LOG_PREFIX)
306
+ except JSONDecodeError:
307
+ # the quote normalization failed, use raw string input
308
+ logger.debug(
309
+ "%s Unable to parse structured tool input after quote normalization. Using Action Input as is."
310
+ "\nParsing error: %s",
311
+ AGENT_LOG_PREFIX,
312
+ original_ex)
313
+ tool_input = tool_input_str
314
+ else:
315
+ # use raw string input
316
+ logger.debug(
317
+ "%s Unable to parse structured tool input from Action Input. Using Action Input as is."
318
+ "\nParsing error: %s",
319
+ AGENT_LOG_PREFIX,
320
+ original_ex)
321
+ tool_input = tool_input_str
322
+
323
+ # Call tool once with the determined input (either parsed dict or raw string)
324
+ tool_response = await self._call_tool(requested_tool,
325
+ tool_input,
326
+ RunnableConfig(callbacks=self.callbacks),
327
+ max_retries=self.tool_call_max_retries)
328
+
329
+ if self.detailed_logs:
330
+ self._log_tool_response(requested_tool.name, tool_input, str(tool_response.content))
331
+
332
+ if not self.pass_tool_call_errors_to_agent:
333
+ if tool_response.status == "error":
334
+ logger.error("%s Tool %s failed: %s", AGENT_LOG_PREFIX, requested_tool.name, tool_response.content)
335
+ raise RuntimeError("Tool call failed: " + str(tool_response.content))
336
+
337
+ state.tool_responses += [tool_response]
338
+ return state
339
+
340
+ async def build_graph(self):
341
+ try:
342
+ await super()._build_graph(state_schema=ReActGraphState)
343
+ logger.debug("%s ReAct Graph built and compiled successfully", AGENT_LOG_PREFIX)
344
+ return self.graph
345
+ except Exception as ex:
346
+ logger.error("%s Failed to build ReAct Graph: %s", AGENT_LOG_PREFIX, ex)
347
+ raise
348
+
349
+ @staticmethod
350
+ def validate_system_prompt(system_prompt: str) -> bool:
351
+ errors = []
352
+ if not system_prompt:
353
+ errors.append("The system prompt cannot be empty.")
354
+ required_prompt_variables = {
355
+ "{tools}": "The system prompt must contain {tools} so the agent knows about configured tools.",
356
+ "{tool_names}": "The system prompt must contain {tool_names} so the agent knows tool names."
357
+ }
358
+ for variable_name, error_message in required_prompt_variables.items():
359
+ if variable_name not in system_prompt:
360
+ errors.append(error_message)
361
+ if errors:
362
+ error_text = "\n".join(errors)
363
+ logger.error("%s %s", AGENT_LOG_PREFIX, error_text)
364
+ return False
365
+ return True
366
+
367
+
368
+ def create_react_agent_prompt(config: "ReActAgentWorkflowConfig") -> ChatPromptTemplate:
369
+ """
370
+ Create a ReAct Agent prompt from the config.
371
+
372
+ Args:
373
+ config (ReActAgentWorkflowConfig): The config to use for the prompt.
374
+
375
+ Returns:
376
+ ChatPromptTemplate: The ReAct Agent prompt.
377
+ """
378
+ # the ReAct Agent prompt can be customized via config option system_prompt and additional_instructions.
379
+
380
+ if config.system_prompt:
381
+ prompt_str = config.system_prompt
382
+ else:
383
+ prompt_str = SYSTEM_PROMPT
384
+
385
+ if config.additional_instructions:
386
+ prompt_str += f" {config.additional_instructions}"
387
+
388
+ valid_prompt = ReActAgentGraph.validate_system_prompt(prompt_str)
389
+ if not valid_prompt:
390
+ logger.error("%s Invalid system_prompt", AGENT_LOG_PREFIX)
391
+ raise ValueError("Invalid system_prompt")
392
+ prompt = ChatPromptTemplate([("system", prompt_str), ("user", USER_PROMPT),
393
+ MessagesPlaceholder(variable_name='agent_scratchpad', optional=True)])
394
+ return prompt
@@ -0,0 +1,104 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import re
17
+
18
+ from langchain.agents.agent import AgentOutputParser
19
+ from langchain_core.agents import AgentAction
20
+ from langchain_core.agents import AgentFinish
21
+ from langchain_core.exceptions import LangChainException
22
+
23
+ from .prompt import SYSTEM_PROMPT
24
+
25
+ FINAL_ANSWER_ACTION = "Final Answer:"
26
+ MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE = "Invalid Format: Missing 'Action:' after 'Thought:'"
27
+ MISSING_ACTION_INPUT_AFTER_ACTION_ERROR_MESSAGE = "Invalid Format: Missing 'Action Input:' after 'Action:'"
28
+ FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE = ("Parsing LLM output produced both a final answer and a parse-able "
29
+ "action:")
30
+
31
+
32
+ class ReActOutputParserException(ValueError, LangChainException):
33
+
34
+ def __init__(self,
35
+ observation=None,
36
+ missing_action=False,
37
+ missing_action_input=False,
38
+ final_answer_and_action=False):
39
+ self.observation = observation
40
+ self.missing_action = missing_action
41
+ self.missing_action_input = missing_action_input
42
+ self.final_answer_and_action = final_answer_and_action
43
+
44
+
45
+ class ReActOutputParser(AgentOutputParser):
46
+ """Parses ReAct-style LLM calls that have a single tool input.
47
+
48
+ Expects output to be in one of two formats.
49
+
50
+ If the output signals that an action should be taken,
51
+ should be in the below format. This will result in an AgentAction
52
+ being returned.
53
+
54
+ ```
55
+ Thought: agent thought here
56
+ Action: search
57
+ Action Input: what is the temperature in SF?
58
+ Observation: Waiting for the tool response...
59
+ ```
60
+
61
+ If the output signals that a final answer should be given,
62
+ should be in the below format. This will result in an AgentFinish
63
+ being returned.
64
+
65
+ ```
66
+ Thought: agent thought here
67
+ Final Answer: The temperature is 100 degrees
68
+ ```
69
+
70
+ """
71
+
72
+ def get_format_instructions(self) -> str:
73
+ return SYSTEM_PROMPT
74
+
75
+ def parse(self, text: str) -> AgentAction | AgentFinish:
76
+ includes_answer = FINAL_ANSWER_ACTION in text
77
+ regex = r"Action\s*\d*\s*:[\s]*(.*?)\s*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*?)(?=\s*[\n|\s]\s*Observation\b|$)"
78
+ action_match = re.search(regex, text, re.DOTALL)
79
+ if action_match:
80
+ if includes_answer:
81
+ raise ReActOutputParserException(
82
+ final_answer_and_action=True,
83
+ observation=f"{FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE}: {text}")
84
+ action = action_match.group(1).strip()
85
+ action_input = action_match.group(2)
86
+ tool_input = action_input.strip(" ")
87
+ tool_input = tool_input.strip('"')
88
+
89
+ return AgentAction(action, tool_input, text)
90
+
91
+ if includes_answer:
92
+ return AgentFinish({"output": text.split(FINAL_ANSWER_ACTION)[-1].strip()}, text)
93
+
94
+ if not re.search(r"Action\s*\d*\s*:[\s]*(.*?)", text, re.DOTALL):
95
+ raise ReActOutputParserException(observation=MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE,
96
+ missing_action=True)
97
+ if not re.search(r"[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)", text, re.DOTALL):
98
+ raise ReActOutputParserException(observation=MISSING_ACTION_INPUT_AFTER_ACTION_ERROR_MESSAGE,
99
+ missing_action_input=True)
100
+ raise ReActOutputParserException(f"Could not parse LLM output: `{text}`")
101
+
102
+ @property
103
+ def _type(self) -> str:
104
+ return "react-input"
@@ -0,0 +1,44 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ # flake8: noqa
17
+
18
+ SYSTEM_PROMPT = """
19
+ Answer the following questions as best you can. You may ask the human to use the following tools:
20
+
21
+ {tools}
22
+
23
+ You may respond in one of two formats.
24
+ Use the following format exactly to ask the human to use a tool:
25
+
26
+ Question: the input question you must answer
27
+ Thought: you should always think about what to do
28
+ Action: the action to take, should be one of [{tool_names}]
29
+ Action Input: the input to the action (if there is no required input, include "Action Input: None")
30
+ Observation: wait for the human to respond with the result from the tool, do not assume the response
31
+
32
+ ... (this Thought/Action/Action Input/Observation can repeat N times. If you do not need to use a tool, or after asking the human to use any tools and waiting for the human to respond, you might know the final answer.)
33
+ Use the following format once you have the final answer:
34
+
35
+ Thought: I now know the final answer
36
+ Final Answer: the final answer to the original input question
37
+ """
38
+
39
+ USER_PROMPT = """
40
+ Previous conversation history:
41
+ {chat_history}
42
+
43
+ Question: {question}
44
+ """