aiqtoolkit 1.1.0a20250516__py3-none-any.whl → 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.

Potentially problematic release.


This version of aiqtoolkit might be problematic. Click here for more details.

Files changed (319) hide show
  1. aiqtoolkit-1.1.0a20251020.dist-info/METADATA +37 -0
  2. aiqtoolkit-1.1.0a20251020.dist-info/RECORD +4 -0
  3. {aiqtoolkit-1.1.0a20250516.dist-info → aiqtoolkit-1.1.0a20251020.dist-info}/WHEEL +1 -1
  4. aiqtoolkit-1.1.0a20251020.dist-info/top_level.txt +1 -0
  5. aiq/agent/__init__.py +0 -0
  6. aiq/agent/base.py +0 -76
  7. aiq/agent/dual_node.py +0 -67
  8. aiq/agent/react_agent/__init__.py +0 -0
  9. aiq/agent/react_agent/agent.py +0 -322
  10. aiq/agent/react_agent/output_parser.py +0 -104
  11. aiq/agent/react_agent/prompt.py +0 -46
  12. aiq/agent/react_agent/register.py +0 -148
  13. aiq/agent/reasoning_agent/__init__.py +0 -0
  14. aiq/agent/reasoning_agent/reasoning_agent.py +0 -224
  15. aiq/agent/register.py +0 -23
  16. aiq/agent/rewoo_agent/__init__.py +0 -0
  17. aiq/agent/rewoo_agent/agent.py +0 -410
  18. aiq/agent/rewoo_agent/prompt.py +0 -108
  19. aiq/agent/rewoo_agent/register.py +0 -158
  20. aiq/agent/tool_calling_agent/__init__.py +0 -0
  21. aiq/agent/tool_calling_agent/agent.py +0 -123
  22. aiq/agent/tool_calling_agent/register.py +0 -105
  23. aiq/builder/__init__.py +0 -0
  24. aiq/builder/builder.py +0 -223
  25. aiq/builder/component_utils.py +0 -303
  26. aiq/builder/context.py +0 -227
  27. aiq/builder/embedder.py +0 -24
  28. aiq/builder/eval_builder.py +0 -120
  29. aiq/builder/evaluator.py +0 -29
  30. aiq/builder/framework_enum.py +0 -24
  31. aiq/builder/front_end.py +0 -73
  32. aiq/builder/function.py +0 -297
  33. aiq/builder/function_base.py +0 -376
  34. aiq/builder/function_info.py +0 -627
  35. aiq/builder/intermediate_step_manager.py +0 -176
  36. aiq/builder/llm.py +0 -25
  37. aiq/builder/retriever.py +0 -25
  38. aiq/builder/user_interaction_manager.py +0 -71
  39. aiq/builder/workflow.py +0 -143
  40. aiq/builder/workflow_builder.py +0 -757
  41. aiq/cli/__init__.py +0 -14
  42. aiq/cli/cli_utils/__init__.py +0 -0
  43. aiq/cli/cli_utils/config_override.py +0 -231
  44. aiq/cli/cli_utils/validation.py +0 -37
  45. aiq/cli/commands/__init__.py +0 -0
  46. aiq/cli/commands/configure/__init__.py +0 -0
  47. aiq/cli/commands/configure/channel/__init__.py +0 -0
  48. aiq/cli/commands/configure/channel/add.py +0 -28
  49. aiq/cli/commands/configure/channel/channel.py +0 -36
  50. aiq/cli/commands/configure/channel/remove.py +0 -30
  51. aiq/cli/commands/configure/channel/update.py +0 -30
  52. aiq/cli/commands/configure/configure.py +0 -33
  53. aiq/cli/commands/evaluate.py +0 -139
  54. aiq/cli/commands/info/__init__.py +0 -14
  55. aiq/cli/commands/info/info.py +0 -39
  56. aiq/cli/commands/info/list_channels.py +0 -32
  57. aiq/cli/commands/info/list_components.py +0 -129
  58. aiq/cli/commands/info/list_mcp.py +0 -126
  59. aiq/cli/commands/registry/__init__.py +0 -14
  60. aiq/cli/commands/registry/publish.py +0 -88
  61. aiq/cli/commands/registry/pull.py +0 -118
  62. aiq/cli/commands/registry/registry.py +0 -38
  63. aiq/cli/commands/registry/remove.py +0 -108
  64. aiq/cli/commands/registry/search.py +0 -155
  65. aiq/cli/commands/start.py +0 -250
  66. aiq/cli/commands/uninstall.py +0 -83
  67. aiq/cli/commands/validate.py +0 -47
  68. aiq/cli/commands/workflow/__init__.py +0 -14
  69. aiq/cli/commands/workflow/templates/__init__.py.j2 +0 -0
  70. aiq/cli/commands/workflow/templates/config.yml.j2 +0 -16
  71. aiq/cli/commands/workflow/templates/pyproject.toml.j2 +0 -22
  72. aiq/cli/commands/workflow/templates/register.py.j2 +0 -5
  73. aiq/cli/commands/workflow/templates/workflow.py.j2 +0 -36
  74. aiq/cli/commands/workflow/workflow.py +0 -37
  75. aiq/cli/commands/workflow/workflow_commands.py +0 -313
  76. aiq/cli/entrypoint.py +0 -133
  77. aiq/cli/main.py +0 -44
  78. aiq/cli/register_workflow.py +0 -408
  79. aiq/cli/type_registry.py +0 -879
  80. aiq/data_models/__init__.py +0 -14
  81. aiq/data_models/api_server.py +0 -588
  82. aiq/data_models/common.py +0 -143
  83. aiq/data_models/component.py +0 -46
  84. aiq/data_models/component_ref.py +0 -135
  85. aiq/data_models/config.py +0 -349
  86. aiq/data_models/dataset_handler.py +0 -122
  87. aiq/data_models/discovery_metadata.py +0 -286
  88. aiq/data_models/embedder.py +0 -26
  89. aiq/data_models/evaluate.py +0 -104
  90. aiq/data_models/evaluator.py +0 -26
  91. aiq/data_models/front_end.py +0 -26
  92. aiq/data_models/function.py +0 -30
  93. aiq/data_models/function_dependencies.py +0 -64
  94. aiq/data_models/interactive.py +0 -237
  95. aiq/data_models/intermediate_step.py +0 -269
  96. aiq/data_models/invocation_node.py +0 -38
  97. aiq/data_models/llm.py +0 -26
  98. aiq/data_models/logging.py +0 -26
  99. aiq/data_models/memory.py +0 -26
  100. aiq/data_models/profiler.py +0 -53
  101. aiq/data_models/registry_handler.py +0 -26
  102. aiq/data_models/retriever.py +0 -30
  103. aiq/data_models/step_adaptor.py +0 -64
  104. aiq/data_models/streaming.py +0 -33
  105. aiq/data_models/swe_bench_model.py +0 -54
  106. aiq/data_models/telemetry_exporter.py +0 -26
  107. aiq/embedder/__init__.py +0 -0
  108. aiq/embedder/langchain_client.py +0 -41
  109. aiq/embedder/nim_embedder.py +0 -58
  110. aiq/embedder/openai_embedder.py +0 -42
  111. aiq/embedder/register.py +0 -24
  112. aiq/eval/__init__.py +0 -14
  113. aiq/eval/config.py +0 -42
  114. aiq/eval/dataset_handler/__init__.py +0 -0
  115. aiq/eval/dataset_handler/dataset_downloader.py +0 -106
  116. aiq/eval/dataset_handler/dataset_filter.py +0 -52
  117. aiq/eval/dataset_handler/dataset_handler.py +0 -169
  118. aiq/eval/evaluate.py +0 -325
  119. aiq/eval/evaluator/__init__.py +0 -14
  120. aiq/eval/evaluator/evaluator_model.py +0 -44
  121. aiq/eval/intermediate_step_adapter.py +0 -93
  122. aiq/eval/rag_evaluator/__init__.py +0 -0
  123. aiq/eval/rag_evaluator/evaluate.py +0 -138
  124. aiq/eval/rag_evaluator/register.py +0 -138
  125. aiq/eval/register.py +0 -23
  126. aiq/eval/remote_workflow.py +0 -128
  127. aiq/eval/runtime_event_subscriber.py +0 -52
  128. aiq/eval/swe_bench_evaluator/__init__.py +0 -0
  129. aiq/eval/swe_bench_evaluator/evaluate.py +0 -215
  130. aiq/eval/swe_bench_evaluator/register.py +0 -36
  131. aiq/eval/trajectory_evaluator/__init__.py +0 -0
  132. aiq/eval/trajectory_evaluator/evaluate.py +0 -118
  133. aiq/eval/trajectory_evaluator/register.py +0 -40
  134. aiq/eval/tunable_rag_evaluator/__init__.py +0 -0
  135. aiq/eval/tunable_rag_evaluator/evaluate.py +0 -263
  136. aiq/eval/tunable_rag_evaluator/register.py +0 -50
  137. aiq/eval/utils/__init__.py +0 -0
  138. aiq/eval/utils/output_uploader.py +0 -131
  139. aiq/eval/utils/tqdm_position_registry.py +0 -40
  140. aiq/front_ends/__init__.py +0 -14
  141. aiq/front_ends/console/__init__.py +0 -14
  142. aiq/front_ends/console/console_front_end_config.py +0 -32
  143. aiq/front_ends/console/console_front_end_plugin.py +0 -107
  144. aiq/front_ends/console/register.py +0 -25
  145. aiq/front_ends/cron/__init__.py +0 -14
  146. aiq/front_ends/fastapi/__init__.py +0 -14
  147. aiq/front_ends/fastapi/fastapi_front_end_config.py +0 -150
  148. aiq/front_ends/fastapi/fastapi_front_end_plugin.py +0 -103
  149. aiq/front_ends/fastapi/fastapi_front_end_plugin_worker.py +0 -607
  150. aiq/front_ends/fastapi/intermediate_steps_subscriber.py +0 -80
  151. aiq/front_ends/fastapi/job_store.py +0 -161
  152. aiq/front_ends/fastapi/main.py +0 -70
  153. aiq/front_ends/fastapi/message_handler.py +0 -279
  154. aiq/front_ends/fastapi/message_validator.py +0 -345
  155. aiq/front_ends/fastapi/register.py +0 -25
  156. aiq/front_ends/fastapi/response_helpers.py +0 -195
  157. aiq/front_ends/fastapi/step_adaptor.py +0 -320
  158. aiq/front_ends/fastapi/websocket.py +0 -148
  159. aiq/front_ends/mcp/__init__.py +0 -14
  160. aiq/front_ends/mcp/mcp_front_end_config.py +0 -32
  161. aiq/front_ends/mcp/mcp_front_end_plugin.py +0 -93
  162. aiq/front_ends/mcp/register.py +0 -27
  163. aiq/front_ends/mcp/tool_converter.py +0 -242
  164. aiq/front_ends/register.py +0 -22
  165. aiq/front_ends/simple_base/__init__.py +0 -14
  166. aiq/front_ends/simple_base/simple_front_end_plugin_base.py +0 -52
  167. aiq/llm/__init__.py +0 -0
  168. aiq/llm/nim_llm.py +0 -45
  169. aiq/llm/openai_llm.py +0 -45
  170. aiq/llm/register.py +0 -22
  171. aiq/llm/utils/__init__.py +0 -14
  172. aiq/llm/utils/env_config_value.py +0 -94
  173. aiq/llm/utils/error.py +0 -17
  174. aiq/memory/__init__.py +0 -20
  175. aiq/memory/interfaces.py +0 -183
  176. aiq/memory/models.py +0 -112
  177. aiq/meta/module_to_distro.json +0 -3
  178. aiq/meta/pypi.md +0 -58
  179. aiq/observability/__init__.py +0 -0
  180. aiq/observability/async_otel_listener.py +0 -429
  181. aiq/observability/register.py +0 -99
  182. aiq/plugins/.namespace +0 -1
  183. aiq/profiler/__init__.py +0 -0
  184. aiq/profiler/callbacks/__init__.py +0 -0
  185. aiq/profiler/callbacks/agno_callback_handler.py +0 -295
  186. aiq/profiler/callbacks/base_callback_class.py +0 -20
  187. aiq/profiler/callbacks/langchain_callback_handler.py +0 -278
  188. aiq/profiler/callbacks/llama_index_callback_handler.py +0 -205
  189. aiq/profiler/callbacks/semantic_kernel_callback_handler.py +0 -238
  190. aiq/profiler/callbacks/token_usage_base_model.py +0 -27
  191. aiq/profiler/data_frame_row.py +0 -51
  192. aiq/profiler/decorators/__init__.py +0 -0
  193. aiq/profiler/decorators/framework_wrapper.py +0 -131
  194. aiq/profiler/decorators/function_tracking.py +0 -254
  195. aiq/profiler/forecasting/__init__.py +0 -0
  196. aiq/profiler/forecasting/config.py +0 -18
  197. aiq/profiler/forecasting/model_trainer.py +0 -75
  198. aiq/profiler/forecasting/models/__init__.py +0 -22
  199. aiq/profiler/forecasting/models/forecasting_base_model.py +0 -40
  200. aiq/profiler/forecasting/models/linear_model.py +0 -196
  201. aiq/profiler/forecasting/models/random_forest_regressor.py +0 -268
  202. aiq/profiler/inference_metrics_model.py +0 -25
  203. aiq/profiler/inference_optimization/__init__.py +0 -0
  204. aiq/profiler/inference_optimization/bottleneck_analysis/__init__.py +0 -0
  205. aiq/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +0 -452
  206. aiq/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +0 -258
  207. aiq/profiler/inference_optimization/data_models.py +0 -386
  208. aiq/profiler/inference_optimization/experimental/__init__.py +0 -0
  209. aiq/profiler/inference_optimization/experimental/concurrency_spike_analysis.py +0 -468
  210. aiq/profiler/inference_optimization/experimental/prefix_span_analysis.py +0 -405
  211. aiq/profiler/inference_optimization/llm_metrics.py +0 -212
  212. aiq/profiler/inference_optimization/prompt_caching.py +0 -163
  213. aiq/profiler/inference_optimization/token_uniqueness.py +0 -107
  214. aiq/profiler/inference_optimization/workflow_runtimes.py +0 -72
  215. aiq/profiler/intermediate_property_adapter.py +0 -102
  216. aiq/profiler/profile_runner.py +0 -433
  217. aiq/profiler/utils.py +0 -184
  218. aiq/registry_handlers/__init__.py +0 -0
  219. aiq/registry_handlers/local/__init__.py +0 -0
  220. aiq/registry_handlers/local/local_handler.py +0 -176
  221. aiq/registry_handlers/local/register_local.py +0 -37
  222. aiq/registry_handlers/metadata_factory.py +0 -60
  223. aiq/registry_handlers/package_utils.py +0 -198
  224. aiq/registry_handlers/pypi/__init__.py +0 -0
  225. aiq/registry_handlers/pypi/pypi_handler.py +0 -251
  226. aiq/registry_handlers/pypi/register_pypi.py +0 -40
  227. aiq/registry_handlers/register.py +0 -21
  228. aiq/registry_handlers/registry_handler_base.py +0 -157
  229. aiq/registry_handlers/rest/__init__.py +0 -0
  230. aiq/registry_handlers/rest/register_rest.py +0 -56
  231. aiq/registry_handlers/rest/rest_handler.py +0 -237
  232. aiq/registry_handlers/schemas/__init__.py +0 -0
  233. aiq/registry_handlers/schemas/headers.py +0 -42
  234. aiq/registry_handlers/schemas/package.py +0 -68
  235. aiq/registry_handlers/schemas/publish.py +0 -63
  236. aiq/registry_handlers/schemas/pull.py +0 -82
  237. aiq/registry_handlers/schemas/remove.py +0 -36
  238. aiq/registry_handlers/schemas/search.py +0 -91
  239. aiq/registry_handlers/schemas/status.py +0 -47
  240. aiq/retriever/__init__.py +0 -0
  241. aiq/retriever/interface.py +0 -37
  242. aiq/retriever/milvus/__init__.py +0 -14
  243. aiq/retriever/milvus/register.py +0 -81
  244. aiq/retriever/milvus/retriever.py +0 -228
  245. aiq/retriever/models.py +0 -74
  246. aiq/retriever/nemo_retriever/__init__.py +0 -14
  247. aiq/retriever/nemo_retriever/register.py +0 -60
  248. aiq/retriever/nemo_retriever/retriever.py +0 -190
  249. aiq/retriever/register.py +0 -22
  250. aiq/runtime/__init__.py +0 -14
  251. aiq/runtime/loader.py +0 -188
  252. aiq/runtime/runner.py +0 -176
  253. aiq/runtime/session.py +0 -140
  254. aiq/runtime/user_metadata.py +0 -131
  255. aiq/settings/__init__.py +0 -0
  256. aiq/settings/global_settings.py +0 -318
  257. aiq/test/.namespace +0 -1
  258. aiq/tool/__init__.py +0 -0
  259. aiq/tool/code_execution/__init__.py +0 -0
  260. aiq/tool/code_execution/code_sandbox.py +0 -188
  261. aiq/tool/code_execution/local_sandbox/Dockerfile.sandbox +0 -60
  262. aiq/tool/code_execution/local_sandbox/__init__.py +0 -13
  263. aiq/tool/code_execution/local_sandbox/local_sandbox_server.py +0 -83
  264. aiq/tool/code_execution/local_sandbox/sandbox.requirements.txt +0 -4
  265. aiq/tool/code_execution/local_sandbox/start_local_sandbox.sh +0 -25
  266. aiq/tool/code_execution/register.py +0 -70
  267. aiq/tool/code_execution/utils.py +0 -100
  268. aiq/tool/datetime_tools.py +0 -42
  269. aiq/tool/document_search.py +0 -141
  270. aiq/tool/github_tools/__init__.py +0 -0
  271. aiq/tool/github_tools/create_github_commit.py +0 -133
  272. aiq/tool/github_tools/create_github_issue.py +0 -87
  273. aiq/tool/github_tools/create_github_pr.py +0 -106
  274. aiq/tool/github_tools/get_github_file.py +0 -106
  275. aiq/tool/github_tools/get_github_issue.py +0 -166
  276. aiq/tool/github_tools/get_github_pr.py +0 -256
  277. aiq/tool/github_tools/update_github_issue.py +0 -100
  278. aiq/tool/mcp/__init__.py +0 -14
  279. aiq/tool/mcp/mcp_client.py +0 -220
  280. aiq/tool/mcp/mcp_tool.py +0 -95
  281. aiq/tool/memory_tools/__init__.py +0 -0
  282. aiq/tool/memory_tools/add_memory_tool.py +0 -79
  283. aiq/tool/memory_tools/delete_memory_tool.py +0 -67
  284. aiq/tool/memory_tools/get_memory_tool.py +0 -72
  285. aiq/tool/nvidia_rag.py +0 -95
  286. aiq/tool/register.py +0 -37
  287. aiq/tool/retriever.py +0 -89
  288. aiq/tool/server_tools.py +0 -63
  289. aiq/utils/__init__.py +0 -0
  290. aiq/utils/data_models/__init__.py +0 -0
  291. aiq/utils/data_models/schema_validator.py +0 -58
  292. aiq/utils/debugging_utils.py +0 -43
  293. aiq/utils/exception_handlers/__init__.py +0 -0
  294. aiq/utils/exception_handlers/schemas.py +0 -114
  295. aiq/utils/io/__init__.py +0 -0
  296. aiq/utils/io/yaml_tools.py +0 -119
  297. aiq/utils/metadata_utils.py +0 -74
  298. aiq/utils/optional_imports.py +0 -142
  299. aiq/utils/producer_consumer_queue.py +0 -178
  300. aiq/utils/reactive/__init__.py +0 -0
  301. aiq/utils/reactive/base/__init__.py +0 -0
  302. aiq/utils/reactive/base/observable_base.py +0 -65
  303. aiq/utils/reactive/base/observer_base.py +0 -55
  304. aiq/utils/reactive/base/subject_base.py +0 -79
  305. aiq/utils/reactive/observable.py +0 -59
  306. aiq/utils/reactive/observer.py +0 -76
  307. aiq/utils/reactive/subject.py +0 -131
  308. aiq/utils/reactive/subscription.py +0 -49
  309. aiq/utils/settings/__init__.py +0 -0
  310. aiq/utils/settings/global_settings.py +0 -197
  311. aiq/utils/type_converter.py +0 -232
  312. aiq/utils/type_utils.py +0 -397
  313. aiq/utils/url_utils.py +0 -27
  314. aiqtoolkit-1.1.0a20250516.dist-info/METADATA +0 -331
  315. aiqtoolkit-1.1.0a20250516.dist-info/RECORD +0 -316
  316. aiqtoolkit-1.1.0a20250516.dist-info/entry_points.txt +0 -17
  317. aiqtoolkit-1.1.0a20250516.dist-info/licenses/LICENSE-3rd-party.txt +0 -3686
  318. aiqtoolkit-1.1.0a20250516.dist-info/licenses/LICENSE.md +0 -201
  319. aiqtoolkit-1.1.0a20250516.dist-info/top_level.txt +0 -1
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: aiqtoolkit
3
+ Version: 1.1.0a20251020
4
+ Summary: Transitional package for nvidia-nat, this package is deprecated and will be removed in the future.
5
+ Author: NVIDIA Corporation
6
+ Maintainer: NVIDIA Corporation
7
+ License: Apache-2.0
8
+ Project-URL: documentation, https://docs.nvidia.com/nemo/agent-toolkit/latest/
9
+ Project-URL: source, https://github.com/NVIDIA/NeMo-Agent-Toolkit
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: nvidia-nat==v1.1.0a20251020
16
+
17
+ <!--
18
+ SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
19
+ SPDX-License-Identifier: Apache-2.0
20
+
21
+ Licensed under the Apache License, Version 2.0 (the "License");
22
+ you may not use this file except in compliance with the License.
23
+ You may obtain a copy of the License at
24
+
25
+ http://www.apache.org/licenses/LICENSE-2.0
26
+
27
+ Unless required by applicable law or agreed to in writing, software
28
+ distributed under the License is distributed on an "AS IS" BASIS,
29
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30
+ See the License for the specific language governing permissions and
31
+ limitations under the License.
32
+ -->
33
+
34
+ # Transitional Package for `nvidia-nat`
35
+ This is a transitional package for `nvidia-nat` to help ease the migration to `nvidia-nat`, and will be removed in a future release. It is recommended to use `nvidia-nat` directly for new projects.
36
+
37
+ For more information about the NVIDIA NeMo Agent toolkit, please visit the [NeMo Agent toolkit package](https://pypi.org/project/nvidia-nat/).
@@ -0,0 +1,4 @@
1
+ aiqtoolkit-1.1.0a20251020.dist-info/METADATA,sha256=019slW-p7s6tk7vp_sACfSTPprEEE6AIonqq0ywtcSQ,1721
2
+ aiqtoolkit-1.1.0a20251020.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3
+ aiqtoolkit-1.1.0a20251020.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
4
+ aiqtoolkit-1.1.0a20251020.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
aiq/agent/__init__.py DELETED
File without changes
aiq/agent/base.py DELETED
@@ -1,76 +0,0 @@
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 logging
17
- from abc import ABC
18
- from abc import abstractmethod
19
- from enum import Enum
20
-
21
- from colorama import Fore
22
- from langchain_core.callbacks import AsyncCallbackHandler
23
- from langchain_core.language_models import BaseChatModel
24
- from langchain_core.tools import BaseTool
25
- from langgraph.graph.graph import CompiledGraph
26
-
27
- log = logging.getLogger(__name__)
28
-
29
- TOOL_NOT_FOUND_ERROR_MESSAGE = "There is no tool named {tool_name}. Tool must be one of {tools}."
30
- INPUT_SCHEMA_MESSAGE = ". Arguments must be provided as a valid JSON object following this format: {schema}"
31
- NO_INPUT_ERROR_MESSAGE = "No human input recieved to the agent, Please ask a valid question."
32
-
33
- AGENT_LOG_PREFIX = "[AGENT]"
34
- AGENT_RESPONSE_LOG_MESSAGE = f"\n{'-' * 30}\n" + \
35
- AGENT_LOG_PREFIX + "\n" + \
36
- Fore.YELLOW + \
37
- "Agent input: %s\n" + \
38
- Fore.CYAN + \
39
- "Agent's thoughts: \n%s" + \
40
- Fore.RESET + \
41
- f"\n{'-' * 30}"
42
-
43
- TOOL_RESPONSE_LOG_MESSAGE = f"\n{'-' * 30}\n" + \
44
- AGENT_LOG_PREFIX + "\n" + \
45
- Fore.WHITE + \
46
- "Calling tools: %s\n" + \
47
- Fore.YELLOW + \
48
- "Tool's input: %s\n" + \
49
- Fore.CYAN + \
50
- "Tool's response: \n%s" + \
51
- Fore.RESET + \
52
- f"\n{'-' * 30}"
53
-
54
-
55
- class AgentDecision(Enum):
56
- TOOL = "tool"
57
- END = "finished"
58
-
59
-
60
- class BaseAgent(ABC):
61
-
62
- def __init__(self,
63
- llm: BaseChatModel,
64
- tools: list[BaseTool],
65
- callbacks: list[AsyncCallbackHandler] = None,
66
- detailed_logs: bool = False):
67
- log.debug("Initializing Agent Graph")
68
- self.llm = llm
69
- self.tools = tools
70
- self.callbacks = callbacks or []
71
- self.detailed_logs = detailed_logs
72
- self.graph = None
73
-
74
- @abstractmethod
75
- async def _build_graph(self, state_schema) -> CompiledGraph:
76
- pass
aiq/agent/dual_node.py DELETED
@@ -1,67 +0,0 @@
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 logging
17
- from abc import abstractmethod
18
-
19
- from langchain_core.callbacks import AsyncCallbackHandler
20
- from langchain_core.language_models import BaseChatModel
21
- from langchain_core.tools import BaseTool
22
- from langgraph.graph import StateGraph
23
- from langgraph.graph.graph import CompiledGraph
24
- from pydantic import BaseModel
25
-
26
- from .base import AgentDecision
27
- from .base import BaseAgent
28
-
29
- log = logging.getLogger(__name__)
30
-
31
-
32
- class DualNodeAgent(BaseAgent):
33
-
34
- def __init__(self,
35
- llm: BaseChatModel,
36
- tools: list[BaseTool],
37
- callbacks: list[AsyncCallbackHandler] = None,
38
- detailed_logs: bool = False):
39
- super().__init__(llm=llm, tools=tools, callbacks=callbacks, detailed_logs=detailed_logs)
40
-
41
- @abstractmethod
42
- async def agent_node(self, state: BaseModel) -> BaseModel:
43
- pass
44
-
45
- @abstractmethod
46
- async def tool_node(self, state: BaseModel) -> BaseModel:
47
- pass
48
-
49
- @abstractmethod
50
- async def conditional_edge(self, state: BaseModel) -> str:
51
- pass
52
-
53
- async def _build_graph(self, state_schema) -> CompiledGraph:
54
- log.debug("Building and compiling the Agent Graph")
55
-
56
- graph = StateGraph(state_schema)
57
- graph.add_node("agent", self.agent_node)
58
- graph.add_node("tool", self.tool_node)
59
- graph.add_edge("tool", "agent")
60
-
61
- conditional_edge_possible_outputs = {AgentDecision.TOOL: "tool", AgentDecision.END: "__end__"}
62
- graph.add_conditional_edges("agent", self.conditional_edge, conditional_edge_possible_outputs)
63
-
64
- graph.set_entry_point("agent")
65
- self.graph = graph.compile()
66
-
67
- return self.graph
File without changes
@@ -1,322 +0,0 @@
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
- # pylint: disable=R0917
18
- import logging
19
- from json import JSONDecodeError
20
-
21
- from langchain_core.agents import AgentAction
22
- from langchain_core.agents import AgentFinish
23
- from langchain_core.callbacks.base import AsyncCallbackHandler
24
- from langchain_core.language_models import BaseChatModel
25
- from langchain_core.messages.ai import AIMessage
26
- from langchain_core.messages.base import BaseMessage
27
- from langchain_core.messages.human import HumanMessage
28
- from langchain_core.messages.tool import ToolMessage
29
- from langchain_core.prompts.chat import ChatPromptTemplate
30
- from langchain_core.runnables.config import RunnableConfig
31
- from langchain_core.tools import BaseTool
32
- from pydantic import BaseModel
33
- from pydantic import Field
34
-
35
- from aiq.agent.base import AGENT_LOG_PREFIX
36
- from aiq.agent.base import AGENT_RESPONSE_LOG_MESSAGE
37
- from aiq.agent.base import INPUT_SCHEMA_MESSAGE
38
- from aiq.agent.base import NO_INPUT_ERROR_MESSAGE
39
- from aiq.agent.base import TOOL_NOT_FOUND_ERROR_MESSAGE
40
- from aiq.agent.base import TOOL_RESPONSE_LOG_MESSAGE
41
- from aiq.agent.base import AgentDecision
42
- from aiq.agent.dual_node import DualNodeAgent
43
- from aiq.agent.react_agent.output_parser import ReActOutputParser
44
- from aiq.agent.react_agent.output_parser import ReActOutputParserException
45
-
46
- logger = logging.getLogger(__name__)
47
-
48
-
49
- class ReActGraphState(BaseModel):
50
- """State schema for the ReAct Agent Graph"""
51
- messages: list[BaseMessage] = Field(default_factory=list) # input and output of the ReAct Agent
52
- agent_scratchpad: list[AgentAction] = Field(default_factory=list) # agent thoughts / intermediate steps
53
- tool_responses: list[BaseMessage] = Field(default_factory=list) # the responses from any tool calls
54
-
55
-
56
- class ReActAgentGraph(DualNodeAgent):
57
- """Configurable LangGraph ReAct Agent. A ReAct Agent performs reasoning inbetween tool calls, and utilizes the tool
58
- names and descriptions to select the optimal tool. Supports retrying on output parsing errors. Argument
59
- "detailed_logs" toggles logging of inputs, outputs, and intermediate steps."""
60
-
61
- def __init__(self,
62
- llm: BaseChatModel,
63
- prompt: ChatPromptTemplate,
64
- tools: list[BaseTool],
65
- use_tool_schema: bool = True,
66
- callbacks: list[AsyncCallbackHandler] = None,
67
- detailed_logs: bool = False,
68
- retry_parsing_errors: bool = True,
69
- max_retries: int = 1):
70
- super().__init__(llm=llm, tools=tools, callbacks=callbacks, detailed_logs=detailed_logs)
71
- self.retry_parsing_errors = retry_parsing_errors
72
- self.max_tries = (max_retries + 1) if retry_parsing_errors else 1
73
- logger.debug(
74
- "%s Filling the prompt variables 'tools' and 'tool_names', using the tools provided in the config.",
75
- AGENT_LOG_PREFIX)
76
- tool_names = ",".join([tool.name for tool in tools[:-1]]) + ',' + tools[-1].name # prevent trailing ","
77
- if not use_tool_schema:
78
- tool_names_and_descriptions = "\n".join(
79
- [f"{tool.name}: {tool.description}"
80
- for tool in tools[:-1]]) + "\n" + f"{tools[-1].name}: {tools[-1].description}" # prevent trailing "\n"
81
- else:
82
- logger.debug("%s Adding the tools' input schema to the tools' description", AGENT_LOG_PREFIX)
83
- tool_names_and_descriptions = "\n".join([
84
- f"{tool.name}: {tool.description}. {INPUT_SCHEMA_MESSAGE.format(schema=tool.input_schema.model_fields)}"
85
- for tool in tools[:-1]
86
- ]) + "\n" + (f"{tools[-1].name}: {tools[-1].description}. "
87
- f"{INPUT_SCHEMA_MESSAGE.format(schema=tools[-1].input_schema.model_fields)}")
88
- prompt = prompt.partial(tools=tool_names_and_descriptions, tool_names=tool_names)
89
- # construct the ReAct Agent
90
- llm = llm.bind(stop=["Observation:"])
91
- self.agent = prompt | llm
92
- self.tools_dict = {tool.name: tool for tool in tools}
93
- logger.debug("%s Initialized ReAct Agent Graph", AGENT_LOG_PREFIX)
94
-
95
- def _get_tool(self, tool_name):
96
- try:
97
- return self.tools_dict.get(tool_name)
98
- except Exception as ex:
99
- logger.exception("%s Unable to find tool with the name %s\n%s",
100
- AGENT_LOG_PREFIX,
101
- tool_name,
102
- ex,
103
- exc_info=True)
104
- raise ex
105
-
106
- async def agent_node(self, state: ReActGraphState):
107
- try:
108
- logger.debug("%s Starting the ReAct Agent Node", AGENT_LOG_PREFIX)
109
- # keeping a working state allows us to resolve parsing errors without polluting the agent scratchpad
110
- # the agent "forgets" about the parsing error after solving it - prevents hallucinations in next cycles
111
- working_state = []
112
- for attempt in range(1, self.max_tries + 1):
113
- # the first time we are invoking the ReAct Agent, it won't have any intermediate steps / agent thoughts
114
- if len(state.agent_scratchpad) == 0 and len(working_state) == 0:
115
- # the user input comes from the "messages" state channel
116
- if len(state.messages) == 0:
117
- raise RuntimeError('No input received in state: "messages"')
118
- # to check is any human input passed or not, if no input passed Agent will return the state
119
- if state.messages[0].content.strip() == "":
120
- logger.error("%s No human input passed to the agent.", AGENT_LOG_PREFIX)
121
- state.messages += [AIMessage(content=NO_INPUT_ERROR_MESSAGE)]
122
- return state
123
- question = state.messages[0].content
124
- logger.debug("%s Querying agent, attempt: %s", AGENT_LOG_PREFIX, attempt)
125
- output_message = ""
126
- async for event in self.agent.astream({"question": question},
127
- config=RunnableConfig(callbacks=self.callbacks)):
128
- output_message += event.content
129
- output_message = AIMessage(content=output_message)
130
- if self.detailed_logs:
131
- logger.info(AGENT_RESPONSE_LOG_MESSAGE, question, output_message.content)
132
- else:
133
- # ReAct Agents require agentic cycles
134
- # in an agentic cycle, preserve the agent's thoughts from the previous cycles,
135
- # and give the agent the response from the tool it called
136
- agent_scratchpad = []
137
- for index, intermediate_step in enumerate(state.agent_scratchpad):
138
- agent_thoughts = AIMessage(content=intermediate_step.log)
139
- agent_scratchpad.append(agent_thoughts)
140
- tool_response = HumanMessage(content=state.tool_responses[index].content)
141
- agent_scratchpad.append(tool_response)
142
- agent_scratchpad += working_state
143
- question = state.messages[0].content
144
- logger.debug("%s Querying agent, attempt: %s", AGENT_LOG_PREFIX, attempt)
145
- output_message = ""
146
- async for event in self.agent.astream({
147
- "question": question, "agent_scratchpad": agent_scratchpad
148
- },
149
- config=RunnableConfig(callbacks=self.callbacks)):
150
- output_message += event.content
151
- output_message = AIMessage(content=output_message)
152
- if self.detailed_logs:
153
- logger.info(AGENT_RESPONSE_LOG_MESSAGE, question, output_message.content)
154
- logger.debug("%s The agent's scratchpad (with tool result) was:\n%s",
155
- AGENT_LOG_PREFIX,
156
- agent_scratchpad)
157
- try:
158
- # check if the agent has the final answer yet
159
- logger.debug("%s Successfully obtained agent response. Parsing agent's response", AGENT_LOG_PREFIX)
160
- agent_output = await ReActOutputParser().aparse(output_message.content)
161
- logger.debug("%s Successfully parsed agent's response", AGENT_LOG_PREFIX)
162
- if attempt > 1:
163
- logger.debug("%s Successfully parsed agent response after %s attempts",
164
- AGENT_LOG_PREFIX,
165
- attempt)
166
- if isinstance(agent_output, AgentFinish):
167
- final_answer = agent_output.return_values.get('output', output_message.content)
168
- logger.debug("%s The agent has finished, and has the final answer", AGENT_LOG_PREFIX)
169
- # this is where we handle the final output of the Agent, we can clean-up/format/postprocess here
170
- # the final answer goes in the "messages" state channel
171
- state.messages += [AIMessage(content=final_answer)]
172
- else:
173
- # the agent wants to call a tool, ensure the thoughts are preserved for the next agentic cycle
174
- agent_output.log = output_message.content
175
- logger.debug("%s The agent wants to call a tool: %s", AGENT_LOG_PREFIX, agent_output.tool)
176
- state.agent_scratchpad += [agent_output]
177
- return state
178
- except ReActOutputParserException as ex:
179
- # the agent output did not meet the expected ReAct output format. This can happen for a few reasons:
180
- # the agent mentioned a tool, but already has the final answer, this can happen with Llama models
181
- # - the ReAct Agent already has the answer, and is reflecting on how it obtained the answer
182
- # the agent might have also missed Action or Action Input in its output
183
- logger.warning("%s Error parsing agent output\nObservation:%s\nAgent Output:\n%s",
184
- AGENT_LOG_PREFIX,
185
- ex.observation,
186
- output_message.content)
187
- if attempt == self.max_tries:
188
- logger.exception(
189
- "%s Failed to parse agent output after %d attempts, consider enabling or "
190
- "increasing max_retries",
191
- AGENT_LOG_PREFIX,
192
- attempt,
193
- exc_info=True)
194
- # the final answer goes in the "messages" state channel
195
- output_message.content = ex.observation + '\n' + output_message.content
196
- state.messages += [output_message]
197
- return state
198
- # retry parsing errors, if configured
199
- logger.info("%s Retrying ReAct Agent, including output parsing Observation", AGENT_LOG_PREFIX)
200
- working_state.append(output_message)
201
- working_state.append(HumanMessage(content=ex.observation))
202
- except Exception as ex:
203
- logger.exception("%s Failed to call agent_node: %s", AGENT_LOG_PREFIX, ex, exc_info=True)
204
- raise ex
205
-
206
- async def conditional_edge(self, state: ReActGraphState):
207
- try:
208
- logger.debug("%s Starting the ReAct Conditional Edge", AGENT_LOG_PREFIX)
209
- if len(state.messages) > 1:
210
- # the ReAct Agent has finished executing, the last agent output was AgentFinish
211
- logger.debug("%s Final answer:\n%s", AGENT_LOG_PREFIX, state.messages[-1].content)
212
- return AgentDecision.END
213
- # else the agent wants to call a tool
214
- agent_output = state.agent_scratchpad[-1]
215
- logger.debug("%s The agent wants to call: %s with input: %s",
216
- AGENT_LOG_PREFIX,
217
- agent_output.tool,
218
- agent_output.tool_input)
219
- return AgentDecision.TOOL
220
- except Exception as ex:
221
- logger.exception("Failed to determine whether agent is calling a tool: %s", ex, exc_info=True)
222
- logger.warning("%s Ending graph traversal", AGENT_LOG_PREFIX)
223
- return AgentDecision.END
224
-
225
- async def tool_node(self, state: ReActGraphState):
226
- try:
227
- logger.debug("%s Starting the Tool Call Node", AGENT_LOG_PREFIX)
228
- if len(state.agent_scratchpad) == 0:
229
- raise RuntimeError('No tool input received in state: "agent_scratchpad"')
230
- agent_thoughts = state.agent_scratchpad[-1]
231
- # the agent can run any installed tool, simply install the tool and add it to the config file
232
- requested_tool = self._get_tool(agent_thoughts.tool)
233
- if not requested_tool:
234
- configured_tool_names = list(self.tools_dict.keys())
235
- logger.warning(
236
- "%s ReAct Agent wants to call tool %s. In the ReAct Agent's configuration within the config file,"
237
- "there is no tool with that name: %s",
238
- AGENT_LOG_PREFIX,
239
- agent_thoughts.tool,
240
- configured_tool_names)
241
- tool_response = ToolMessage(name='agent_error',
242
- tool_call_id='agent_error',
243
- content=TOOL_NOT_FOUND_ERROR_MESSAGE.format(tool_name=agent_thoughts.tool,
244
- tools=configured_tool_names))
245
- state.tool_responses += [tool_response]
246
- return state
247
-
248
- logger.debug("%s Calling tool %s with input: %s",
249
- AGENT_LOG_PREFIX,
250
- requested_tool.name,
251
- agent_thoughts.tool_input)
252
-
253
- # Run the tool. Try to use structured input, if possible.
254
- try:
255
- tool_input_str = agent_thoughts.tool_input.strip().replace("'", '"')
256
- tool_input_dict = json.loads(tool_input_str) if tool_input_str != 'None' else tool_input_str
257
- logger.debug("%s Successfully parsed structured tool input from Action Input", AGENT_LOG_PREFIX)
258
- tool_response = await requested_tool.ainvoke(tool_input_dict,
259
- config=RunnableConfig(callbacks=self.callbacks))
260
- if self.detailed_logs:
261
- # The tool response can be very large, so we log only the first 1000 characters
262
- tool_response_str = str(tool_response)
263
- tool_response_str = tool_response_str[:1000] + "..." if len(
264
- tool_response_str) > 1000 else tool_response_str
265
- tool_response_log_message = TOOL_RESPONSE_LOG_MESSAGE % (
266
- requested_tool.name, tool_input_str, tool_response_str)
267
- logger.info(tool_response_log_message)
268
- except JSONDecodeError as ex:
269
- logger.warning(
270
- "%s Unable to parse structured tool input from Action Input. Using Action Input as is."
271
- "\nParsing error: %s",
272
- AGENT_LOG_PREFIX,
273
- ex,
274
- exc_info=True)
275
- tool_input_str = agent_thoughts.tool_input
276
- tool_response = await requested_tool.ainvoke(tool_input_str,
277
- config=RunnableConfig(callbacks=self.callbacks))
278
-
279
- # some tools, such as Wikipedia, will return an empty response when no search results are found
280
- if tool_response is None or tool_response == "":
281
- tool_response = "The tool provided an empty response.\n"
282
- # put the tool response in the graph state
283
- tool_response = ToolMessage(name=agent_thoughts.tool,
284
- tool_call_id=agent_thoughts.tool,
285
- content=tool_response)
286
- logger.debug("%s Called tool %s with input: %s\nThe tool returned: %s",
287
- AGENT_LOG_PREFIX,
288
- requested_tool.name,
289
- agent_thoughts.tool_input,
290
- tool_response.content)
291
- state.tool_responses += [tool_response]
292
- return state
293
- except Exception as ex:
294
- logger.exception("%s Failed to call tool_node: %s", AGENT_LOG_PREFIX, ex, exc_info=ex)
295
- raise ex
296
-
297
- async def build_graph(self):
298
- try:
299
- await super()._build_graph(state_schema=ReActGraphState)
300
- logger.debug("%s ReAct Graph built and compiled successfully", AGENT_LOG_PREFIX)
301
- return self.graph
302
- except Exception as ex:
303
- logger.exception("%s Failed to build ReAct Graph: %s", AGENT_LOG_PREFIX, ex, exc_info=ex)
304
- raise ex
305
-
306
- @staticmethod
307
- def validate_system_prompt(system_prompt: str) -> bool:
308
- errors = []
309
- if not system_prompt:
310
- errors.append("The system prompt cannot be empty.")
311
- required_prompt_variables = {
312
- "{tools}": "The system prompt must contain {tools} so the agent knows about configured tools.",
313
- "{tool_names}": "The system prompt must contain {tool_names} so the agent knows tool names."
314
- }
315
- for variable_name, error_message in required_prompt_variables.items():
316
- if variable_name not in system_prompt:
317
- errors.append(error_message)
318
- if errors:
319
- error_text = "\n".join(errors)
320
- logger.exception("%s %s", AGENT_LOG_PREFIX, error_text)
321
- raise ValueError(error_text)
322
- return True
@@ -1,104 +0,0 @@
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"