aiqtoolkit 1.1.0a20250429__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 (309) hide show
  1. aiq/agent/__init__.py +0 -0
  2. aiq/agent/base.py +76 -0
  3. aiq/agent/dual_node.py +67 -0
  4. aiq/agent/react_agent/__init__.py +0 -0
  5. aiq/agent/react_agent/agent.py +322 -0
  6. aiq/agent/react_agent/output_parser.py +104 -0
  7. aiq/agent/react_agent/prompt.py +46 -0
  8. aiq/agent/react_agent/register.py +148 -0
  9. aiq/agent/reasoning_agent/__init__.py +0 -0
  10. aiq/agent/reasoning_agent/reasoning_agent.py +224 -0
  11. aiq/agent/register.py +23 -0
  12. aiq/agent/rewoo_agent/__init__.py +0 -0
  13. aiq/agent/rewoo_agent/agent.py +410 -0
  14. aiq/agent/rewoo_agent/prompt.py +108 -0
  15. aiq/agent/rewoo_agent/register.py +158 -0
  16. aiq/agent/tool_calling_agent/__init__.py +0 -0
  17. aiq/agent/tool_calling_agent/agent.py +123 -0
  18. aiq/agent/tool_calling_agent/register.py +105 -0
  19. aiq/builder/__init__.py +0 -0
  20. aiq/builder/builder.py +223 -0
  21. aiq/builder/component_utils.py +303 -0
  22. aiq/builder/context.py +198 -0
  23. aiq/builder/embedder.py +24 -0
  24. aiq/builder/eval_builder.py +116 -0
  25. aiq/builder/evaluator.py +29 -0
  26. aiq/builder/framework_enum.py +24 -0
  27. aiq/builder/front_end.py +73 -0
  28. aiq/builder/function.py +297 -0
  29. aiq/builder/function_base.py +372 -0
  30. aiq/builder/function_info.py +627 -0
  31. aiq/builder/intermediate_step_manager.py +125 -0
  32. aiq/builder/llm.py +25 -0
  33. aiq/builder/retriever.py +25 -0
  34. aiq/builder/user_interaction_manager.py +71 -0
  35. aiq/builder/workflow.py +134 -0
  36. aiq/builder/workflow_builder.py +733 -0
  37. aiq/cli/__init__.py +14 -0
  38. aiq/cli/cli_utils/__init__.py +0 -0
  39. aiq/cli/cli_utils/config_override.py +233 -0
  40. aiq/cli/cli_utils/validation.py +37 -0
  41. aiq/cli/commands/__init__.py +0 -0
  42. aiq/cli/commands/configure/__init__.py +0 -0
  43. aiq/cli/commands/configure/channel/__init__.py +0 -0
  44. aiq/cli/commands/configure/channel/add.py +28 -0
  45. aiq/cli/commands/configure/channel/channel.py +34 -0
  46. aiq/cli/commands/configure/channel/remove.py +30 -0
  47. aiq/cli/commands/configure/channel/update.py +30 -0
  48. aiq/cli/commands/configure/configure.py +33 -0
  49. aiq/cli/commands/evaluate.py +139 -0
  50. aiq/cli/commands/info/__init__.py +14 -0
  51. aiq/cli/commands/info/info.py +37 -0
  52. aiq/cli/commands/info/list_channels.py +32 -0
  53. aiq/cli/commands/info/list_components.py +129 -0
  54. aiq/cli/commands/registry/__init__.py +14 -0
  55. aiq/cli/commands/registry/publish.py +88 -0
  56. aiq/cli/commands/registry/pull.py +118 -0
  57. aiq/cli/commands/registry/registry.py +36 -0
  58. aiq/cli/commands/registry/remove.py +108 -0
  59. aiq/cli/commands/registry/search.py +155 -0
  60. aiq/cli/commands/start.py +250 -0
  61. aiq/cli/commands/uninstall.py +83 -0
  62. aiq/cli/commands/validate.py +47 -0
  63. aiq/cli/commands/workflow/__init__.py +14 -0
  64. aiq/cli/commands/workflow/templates/__init__.py.j2 +0 -0
  65. aiq/cli/commands/workflow/templates/config.yml.j2 +16 -0
  66. aiq/cli/commands/workflow/templates/pyproject.toml.j2 +22 -0
  67. aiq/cli/commands/workflow/templates/register.py.j2 +5 -0
  68. aiq/cli/commands/workflow/templates/workflow.py.j2 +36 -0
  69. aiq/cli/commands/workflow/workflow.py +37 -0
  70. aiq/cli/commands/workflow/workflow_commands.py +307 -0
  71. aiq/cli/entrypoint.py +133 -0
  72. aiq/cli/main.py +44 -0
  73. aiq/cli/register_workflow.py +408 -0
  74. aiq/cli/type_registry.py +869 -0
  75. aiq/data_models/__init__.py +14 -0
  76. aiq/data_models/api_server.py +550 -0
  77. aiq/data_models/common.py +143 -0
  78. aiq/data_models/component.py +46 -0
  79. aiq/data_models/component_ref.py +135 -0
  80. aiq/data_models/config.py +349 -0
  81. aiq/data_models/dataset_handler.py +122 -0
  82. aiq/data_models/discovery_metadata.py +269 -0
  83. aiq/data_models/embedder.py +26 -0
  84. aiq/data_models/evaluate.py +101 -0
  85. aiq/data_models/evaluator.py +26 -0
  86. aiq/data_models/front_end.py +26 -0
  87. aiq/data_models/function.py +30 -0
  88. aiq/data_models/function_dependencies.py +64 -0
  89. aiq/data_models/interactive.py +237 -0
  90. aiq/data_models/intermediate_step.py +269 -0
  91. aiq/data_models/invocation_node.py +38 -0
  92. aiq/data_models/llm.py +26 -0
  93. aiq/data_models/logging.py +26 -0
  94. aiq/data_models/memory.py +26 -0
  95. aiq/data_models/profiler.py +53 -0
  96. aiq/data_models/registry_handler.py +26 -0
  97. aiq/data_models/retriever.py +30 -0
  98. aiq/data_models/step_adaptor.py +64 -0
  99. aiq/data_models/streaming.py +33 -0
  100. aiq/data_models/swe_bench_model.py +54 -0
  101. aiq/data_models/telemetry_exporter.py +26 -0
  102. aiq/embedder/__init__.py +0 -0
  103. aiq/embedder/langchain_client.py +41 -0
  104. aiq/embedder/nim_embedder.py +58 -0
  105. aiq/embedder/openai_embedder.py +42 -0
  106. aiq/embedder/register.py +24 -0
  107. aiq/eval/__init__.py +14 -0
  108. aiq/eval/config.py +42 -0
  109. aiq/eval/dataset_handler/__init__.py +0 -0
  110. aiq/eval/dataset_handler/dataset_downloader.py +106 -0
  111. aiq/eval/dataset_handler/dataset_filter.py +52 -0
  112. aiq/eval/dataset_handler/dataset_handler.py +164 -0
  113. aiq/eval/evaluate.py +322 -0
  114. aiq/eval/evaluator/__init__.py +14 -0
  115. aiq/eval/evaluator/evaluator_model.py +44 -0
  116. aiq/eval/intermediate_step_adapter.py +93 -0
  117. aiq/eval/rag_evaluator/__init__.py +0 -0
  118. aiq/eval/rag_evaluator/evaluate.py +138 -0
  119. aiq/eval/rag_evaluator/register.py +138 -0
  120. aiq/eval/register.py +22 -0
  121. aiq/eval/remote_workflow.py +128 -0
  122. aiq/eval/runtime_event_subscriber.py +52 -0
  123. aiq/eval/swe_bench_evaluator/__init__.py +0 -0
  124. aiq/eval/swe_bench_evaluator/evaluate.py +215 -0
  125. aiq/eval/swe_bench_evaluator/register.py +36 -0
  126. aiq/eval/trajectory_evaluator/__init__.py +0 -0
  127. aiq/eval/trajectory_evaluator/evaluate.py +118 -0
  128. aiq/eval/trajectory_evaluator/register.py +40 -0
  129. aiq/eval/utils/__init__.py +0 -0
  130. aiq/eval/utils/output_uploader.py +131 -0
  131. aiq/eval/utils/tqdm_position_registry.py +40 -0
  132. aiq/front_ends/__init__.py +14 -0
  133. aiq/front_ends/console/__init__.py +14 -0
  134. aiq/front_ends/console/console_front_end_config.py +32 -0
  135. aiq/front_ends/console/console_front_end_plugin.py +107 -0
  136. aiq/front_ends/console/register.py +25 -0
  137. aiq/front_ends/cron/__init__.py +14 -0
  138. aiq/front_ends/fastapi/__init__.py +14 -0
  139. aiq/front_ends/fastapi/fastapi_front_end_config.py +150 -0
  140. aiq/front_ends/fastapi/fastapi_front_end_plugin.py +103 -0
  141. aiq/front_ends/fastapi/fastapi_front_end_plugin_worker.py +574 -0
  142. aiq/front_ends/fastapi/intermediate_steps_subscriber.py +80 -0
  143. aiq/front_ends/fastapi/job_store.py +161 -0
  144. aiq/front_ends/fastapi/main.py +70 -0
  145. aiq/front_ends/fastapi/message_handler.py +279 -0
  146. aiq/front_ends/fastapi/message_validator.py +345 -0
  147. aiq/front_ends/fastapi/register.py +25 -0
  148. aiq/front_ends/fastapi/response_helpers.py +181 -0
  149. aiq/front_ends/fastapi/step_adaptor.py +315 -0
  150. aiq/front_ends/fastapi/websocket.py +148 -0
  151. aiq/front_ends/mcp/__init__.py +14 -0
  152. aiq/front_ends/mcp/mcp_front_end_config.py +32 -0
  153. aiq/front_ends/mcp/mcp_front_end_plugin.py +93 -0
  154. aiq/front_ends/mcp/register.py +27 -0
  155. aiq/front_ends/mcp/tool_converter.py +242 -0
  156. aiq/front_ends/register.py +22 -0
  157. aiq/front_ends/simple_base/__init__.py +14 -0
  158. aiq/front_ends/simple_base/simple_front_end_plugin_base.py +52 -0
  159. aiq/llm/__init__.py +0 -0
  160. aiq/llm/nim_llm.py +45 -0
  161. aiq/llm/openai_llm.py +45 -0
  162. aiq/llm/register.py +22 -0
  163. aiq/llm/utils/__init__.py +14 -0
  164. aiq/llm/utils/env_config_value.py +94 -0
  165. aiq/llm/utils/error.py +17 -0
  166. aiq/memory/__init__.py +20 -0
  167. aiq/memory/interfaces.py +183 -0
  168. aiq/memory/models.py +102 -0
  169. aiq/meta/module_to_distro.json +3 -0
  170. aiq/meta/pypi.md +59 -0
  171. aiq/observability/__init__.py +0 -0
  172. aiq/observability/async_otel_listener.py +270 -0
  173. aiq/observability/register.py +97 -0
  174. aiq/plugins/.namespace +1 -0
  175. aiq/profiler/__init__.py +0 -0
  176. aiq/profiler/callbacks/__init__.py +0 -0
  177. aiq/profiler/callbacks/agno_callback_handler.py +295 -0
  178. aiq/profiler/callbacks/base_callback_class.py +20 -0
  179. aiq/profiler/callbacks/langchain_callback_handler.py +278 -0
  180. aiq/profiler/callbacks/llama_index_callback_handler.py +205 -0
  181. aiq/profiler/callbacks/semantic_kernel_callback_handler.py +238 -0
  182. aiq/profiler/callbacks/token_usage_base_model.py +27 -0
  183. aiq/profiler/data_frame_row.py +51 -0
  184. aiq/profiler/decorators/__init__.py +0 -0
  185. aiq/profiler/decorators/framework_wrapper.py +131 -0
  186. aiq/profiler/decorators/function_tracking.py +254 -0
  187. aiq/profiler/forecasting/__init__.py +0 -0
  188. aiq/profiler/forecasting/config.py +18 -0
  189. aiq/profiler/forecasting/model_trainer.py +75 -0
  190. aiq/profiler/forecasting/models/__init__.py +22 -0
  191. aiq/profiler/forecasting/models/forecasting_base_model.py +40 -0
  192. aiq/profiler/forecasting/models/linear_model.py +196 -0
  193. aiq/profiler/forecasting/models/random_forest_regressor.py +268 -0
  194. aiq/profiler/inference_metrics_model.py +25 -0
  195. aiq/profiler/inference_optimization/__init__.py +0 -0
  196. aiq/profiler/inference_optimization/bottleneck_analysis/__init__.py +0 -0
  197. aiq/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +452 -0
  198. aiq/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +258 -0
  199. aiq/profiler/inference_optimization/data_models.py +386 -0
  200. aiq/profiler/inference_optimization/experimental/__init__.py +0 -0
  201. aiq/profiler/inference_optimization/experimental/concurrency_spike_analysis.py +468 -0
  202. aiq/profiler/inference_optimization/experimental/prefix_span_analysis.py +405 -0
  203. aiq/profiler/inference_optimization/llm_metrics.py +212 -0
  204. aiq/profiler/inference_optimization/prompt_caching.py +163 -0
  205. aiq/profiler/inference_optimization/token_uniqueness.py +107 -0
  206. aiq/profiler/inference_optimization/workflow_runtimes.py +72 -0
  207. aiq/profiler/intermediate_property_adapter.py +102 -0
  208. aiq/profiler/profile_runner.py +433 -0
  209. aiq/profiler/utils.py +184 -0
  210. aiq/registry_handlers/__init__.py +0 -0
  211. aiq/registry_handlers/local/__init__.py +0 -0
  212. aiq/registry_handlers/local/local_handler.py +176 -0
  213. aiq/registry_handlers/local/register_local.py +37 -0
  214. aiq/registry_handlers/metadata_factory.py +60 -0
  215. aiq/registry_handlers/package_utils.py +198 -0
  216. aiq/registry_handlers/pypi/__init__.py +0 -0
  217. aiq/registry_handlers/pypi/pypi_handler.py +251 -0
  218. aiq/registry_handlers/pypi/register_pypi.py +40 -0
  219. aiq/registry_handlers/register.py +21 -0
  220. aiq/registry_handlers/registry_handler_base.py +157 -0
  221. aiq/registry_handlers/rest/__init__.py +0 -0
  222. aiq/registry_handlers/rest/register_rest.py +56 -0
  223. aiq/registry_handlers/rest/rest_handler.py +237 -0
  224. aiq/registry_handlers/schemas/__init__.py +0 -0
  225. aiq/registry_handlers/schemas/headers.py +42 -0
  226. aiq/registry_handlers/schemas/package.py +68 -0
  227. aiq/registry_handlers/schemas/publish.py +63 -0
  228. aiq/registry_handlers/schemas/pull.py +81 -0
  229. aiq/registry_handlers/schemas/remove.py +36 -0
  230. aiq/registry_handlers/schemas/search.py +91 -0
  231. aiq/registry_handlers/schemas/status.py +47 -0
  232. aiq/retriever/__init__.py +0 -0
  233. aiq/retriever/interface.py +37 -0
  234. aiq/retriever/milvus/__init__.py +14 -0
  235. aiq/retriever/milvus/register.py +81 -0
  236. aiq/retriever/milvus/retriever.py +228 -0
  237. aiq/retriever/models.py +74 -0
  238. aiq/retriever/nemo_retriever/__init__.py +14 -0
  239. aiq/retriever/nemo_retriever/register.py +60 -0
  240. aiq/retriever/nemo_retriever/retriever.py +190 -0
  241. aiq/retriever/register.py +22 -0
  242. aiq/runtime/__init__.py +14 -0
  243. aiq/runtime/loader.py +188 -0
  244. aiq/runtime/runner.py +176 -0
  245. aiq/runtime/session.py +116 -0
  246. aiq/settings/__init__.py +0 -0
  247. aiq/settings/global_settings.py +318 -0
  248. aiq/test/.namespace +1 -0
  249. aiq/tool/__init__.py +0 -0
  250. aiq/tool/code_execution/__init__.py +0 -0
  251. aiq/tool/code_execution/code_sandbox.py +188 -0
  252. aiq/tool/code_execution/local_sandbox/Dockerfile.sandbox +60 -0
  253. aiq/tool/code_execution/local_sandbox/__init__.py +13 -0
  254. aiq/tool/code_execution/local_sandbox/local_sandbox_server.py +79 -0
  255. aiq/tool/code_execution/local_sandbox/sandbox.requirements.txt +4 -0
  256. aiq/tool/code_execution/local_sandbox/start_local_sandbox.sh +25 -0
  257. aiq/tool/code_execution/register.py +70 -0
  258. aiq/tool/code_execution/utils.py +100 -0
  259. aiq/tool/datetime_tools.py +42 -0
  260. aiq/tool/document_search.py +141 -0
  261. aiq/tool/github_tools/__init__.py +0 -0
  262. aiq/tool/github_tools/create_github_commit.py +133 -0
  263. aiq/tool/github_tools/create_github_issue.py +87 -0
  264. aiq/tool/github_tools/create_github_pr.py +106 -0
  265. aiq/tool/github_tools/get_github_file.py +106 -0
  266. aiq/tool/github_tools/get_github_issue.py +166 -0
  267. aiq/tool/github_tools/get_github_pr.py +256 -0
  268. aiq/tool/github_tools/update_github_issue.py +100 -0
  269. aiq/tool/mcp/__init__.py +14 -0
  270. aiq/tool/mcp/mcp_client.py +220 -0
  271. aiq/tool/mcp/mcp_tool.py +75 -0
  272. aiq/tool/memory_tools/__init__.py +0 -0
  273. aiq/tool/memory_tools/add_memory_tool.py +67 -0
  274. aiq/tool/memory_tools/delete_memory_tool.py +67 -0
  275. aiq/tool/memory_tools/get_memory_tool.py +72 -0
  276. aiq/tool/nvidia_rag.py +95 -0
  277. aiq/tool/register.py +36 -0
  278. aiq/tool/retriever.py +89 -0
  279. aiq/utils/__init__.py +0 -0
  280. aiq/utils/data_models/__init__.py +0 -0
  281. aiq/utils/data_models/schema_validator.py +58 -0
  282. aiq/utils/debugging_utils.py +43 -0
  283. aiq/utils/exception_handlers/__init__.py +0 -0
  284. aiq/utils/exception_handlers/schemas.py +114 -0
  285. aiq/utils/io/__init__.py +0 -0
  286. aiq/utils/io/yaml_tools.py +50 -0
  287. aiq/utils/metadata_utils.py +74 -0
  288. aiq/utils/producer_consumer_queue.py +178 -0
  289. aiq/utils/reactive/__init__.py +0 -0
  290. aiq/utils/reactive/base/__init__.py +0 -0
  291. aiq/utils/reactive/base/observable_base.py +65 -0
  292. aiq/utils/reactive/base/observer_base.py +55 -0
  293. aiq/utils/reactive/base/subject_base.py +79 -0
  294. aiq/utils/reactive/observable.py +59 -0
  295. aiq/utils/reactive/observer.py +76 -0
  296. aiq/utils/reactive/subject.py +131 -0
  297. aiq/utils/reactive/subscription.py +49 -0
  298. aiq/utils/settings/__init__.py +0 -0
  299. aiq/utils/settings/global_settings.py +197 -0
  300. aiq/utils/type_converter.py +232 -0
  301. aiq/utils/type_utils.py +397 -0
  302. aiq/utils/url_utils.py +27 -0
  303. aiqtoolkit-1.1.0a20250429.dist-info/METADATA +326 -0
  304. aiqtoolkit-1.1.0a20250429.dist-info/RECORD +309 -0
  305. aiqtoolkit-1.1.0a20250429.dist-info/WHEEL +5 -0
  306. aiqtoolkit-1.1.0a20250429.dist-info/entry_points.txt +17 -0
  307. aiqtoolkit-1.1.0a20250429.dist-info/licenses/LICENSE-3rd-party.txt +3686 -0
  308. aiqtoolkit-1.1.0a20250429.dist-info/licenses/LICENSE.md +201 -0
  309. aiqtoolkit-1.1.0a20250429.dist-info/top_level.txt +1 -0
@@ -0,0 +1,148 @@
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
+
18
+ from pydantic import Field
19
+
20
+ from aiq.agent.base import AGENT_LOG_PREFIX
21
+ from aiq.builder.builder import Builder
22
+ from aiq.builder.framework_enum import LLMFrameworkEnum
23
+ from aiq.builder.function_info import FunctionInfo
24
+ from aiq.cli.register_workflow import register_function
25
+ from aiq.data_models.api_server import AIQChatRequest
26
+ from aiq.data_models.api_server import AIQChatResponse
27
+ from aiq.data_models.component_ref import FunctionRef
28
+ from aiq.data_models.component_ref import LLMRef
29
+ from aiq.data_models.function import FunctionBaseConfig
30
+ from aiq.utils.type_converter import GlobalTypeConverter
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ class ReActAgentWorkflowConfig(FunctionBaseConfig, name="react_agent"):
36
+ """
37
+ Defines an AgentIQ function that uses a ReAct Agent performs reasoning inbetween tool calls, and utilizes the tool
38
+ names and descriptions to select the optimal tool.
39
+ """
40
+
41
+ tool_names: list[FunctionRef] = Field(default_factory=list,
42
+ description="The list of tools to provide to the react agent.")
43
+ llm_name: LLMRef = Field(description="The LLM model to use with the react agent.")
44
+ verbose: bool = Field(default=False, description="Set the verbosity of the react agent's logging.")
45
+ retry_parsing_errors: bool = Field(default=True, description="Specify retrying when encountering parsing errors.")
46
+ max_retries: int = Field(default=1, description="Sent the number of retries before raising a parsing error.")
47
+ include_tool_input_schema_in_tool_description: bool = Field(
48
+ default=True, description="Specify inclusion of tool input schemas in the prompt.")
49
+ max_iterations: int = Field(default=15, description="Number of tool calls before stoping the react agent.")
50
+ description: str = Field(default="ReAct Agent Workflow", description="The description of this functions use.")
51
+ system_prompt: str | None = Field(
52
+ default=None,
53
+ description="Provides the SYSTEM_PROMPT to use with the agent") # defaults to SYSTEM_PROMPT in prompt.py
54
+ max_history: int = Field(default=15, description="Maximum number of messages to keep in the conversation history.")
55
+ use_openai_api: bool = Field(default=False,
56
+ description=("Use OpenAI API for the input/output types to the function. "
57
+ "If False, strings will be used."))
58
+ additional_instructions: str | None = Field(
59
+ default=None, description="Additional instructions to provide to the agent in addition to the base prompt.")
60
+
61
+
62
+ @register_function(config_type=ReActAgentWorkflowConfig, framework_wrappers=[LLMFrameworkEnum.LANGCHAIN])
63
+ async def react_agent_workflow(config: ReActAgentWorkflowConfig, builder: Builder):
64
+ from langchain.schema import BaseMessage
65
+ from langchain_core.messages import trim_messages
66
+ from langchain_core.prompts import ChatPromptTemplate
67
+ from langchain_core.prompts import MessagesPlaceholder
68
+ from langgraph.graph.graph import CompiledGraph
69
+
70
+ from aiq.agent.react_agent.prompt import USER_PROMPT
71
+
72
+ from .agent import ReActAgentGraph
73
+ from .agent import ReActGraphState
74
+ from .prompt import react_agent_prompt
75
+
76
+ # the ReAct Agent prompt comes from prompt.py, and can be customized there or via config option system_prompt.
77
+ if config.system_prompt:
78
+ _prompt_str = config.system_prompt
79
+ if config.additional_instructions:
80
+ _prompt_str += f" {config.additional_instructions}"
81
+ valid_prompt = ReActAgentGraph.validate_system_prompt(config.system_prompt)
82
+ if not valid_prompt:
83
+ logger.exception("%s Invalid system_prompt", AGENT_LOG_PREFIX)
84
+ raise ValueError("Invalid system_prompt")
85
+ prompt = ChatPromptTemplate([("system", config.system_prompt), ("user", USER_PROMPT),
86
+ MessagesPlaceholder(variable_name='agent_scratchpad', optional=True)])
87
+ else:
88
+ prompt = react_agent_prompt
89
+
90
+ # we can choose an LLM for the ReAct agent in the config file
91
+ llm = await builder.get_llm(config.llm_name, wrapper_type=LLMFrameworkEnum.LANGCHAIN)
92
+ # the agent can run any installed tool, simply install the tool and add it to the config file
93
+ # the sample tool provided can easily be copied or changed
94
+ tools = builder.get_tools(tool_names=config.tool_names, wrapper_type=LLMFrameworkEnum.LANGCHAIN)
95
+ if not tools:
96
+ raise ValueError(f"No tools specified for ReAct Agent '{config.llm_name}'")
97
+ # configure callbacks, for sending intermediate steps
98
+ # construct the ReAct Agent Graph from the configured llm, prompt, and tools
99
+ graph: CompiledGraph = await ReActAgentGraph(llm=llm,
100
+ prompt=prompt,
101
+ tools=tools,
102
+ use_tool_schema=config.include_tool_input_schema_in_tool_description,
103
+ detailed_logs=config.verbose,
104
+ retry_parsing_errors=config.retry_parsing_errors,
105
+ max_retries=config.max_retries).build_graph()
106
+
107
+ async def _response_fn(input_message: AIQChatRequest) -> AIQChatResponse:
108
+ try:
109
+ # initialize the starting state with the user query
110
+ messages: list[BaseMessage] = trim_messages(messages=[m.model_dump() for m in input_message.messages],
111
+ max_tokens=config.max_history,
112
+ strategy="last",
113
+ token_counter=len,
114
+ start_on="human",
115
+ include_system=True)
116
+
117
+ state = ReActGraphState(messages=messages)
118
+
119
+ # run the ReAct Agent Graph
120
+ state = await graph.ainvoke(state, config={'recursion_limit': (config.max_iterations + 1) * 2})
121
+ # setting recursion_limit: 4 allows 1 tool call
122
+ # - allows the ReAct Agent to perform 1 cycle / call 1 single tool,
123
+ # - but stops the agent when it tries to call a tool a second time
124
+
125
+ # get and return the output from the state
126
+ state = ReActGraphState(**state)
127
+ output_message = state.messages[-1] # pylint: disable=E1136
128
+ return AIQChatResponse.from_string(output_message.content)
129
+
130
+ except Exception as ex:
131
+ logger.exception("%s ReAct Agent failed with exception: %s", AGENT_LOG_PREFIX, ex, exc_info=ex)
132
+ # here, we can implement custom error messages
133
+ if config.verbose:
134
+ return AIQChatResponse.from_string(str(ex))
135
+ return AIQChatResponse.from_string("I seem to be having a problem.")
136
+
137
+ if (config.use_openai_api):
138
+ yield FunctionInfo.from_fn(_response_fn, description=config.description)
139
+ else:
140
+
141
+ async def _str_api_fn(input_message: str) -> str:
142
+ oai_input = GlobalTypeConverter.get().convert(input_message, to_type=AIQChatRequest)
143
+
144
+ oai_output = await _response_fn(oai_input)
145
+
146
+ return GlobalTypeConverter.get().convert(oai_output, to_type=str)
147
+
148
+ yield FunctionInfo.from_fn(_str_api_fn, description=config.description)
File without changes
@@ -0,0 +1,224 @@
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
+ import re
18
+ from collections.abc import AsyncGenerator
19
+
20
+ from pydantic import Field
21
+
22
+ from aiq.agent.base import AGENT_LOG_PREFIX
23
+ from aiq.builder.builder import Builder
24
+ from aiq.builder.framework_enum import LLMFrameworkEnum
25
+ from aiq.builder.function_info import FunctionInfo
26
+ from aiq.cli.register_workflow import register_function
27
+ from aiq.data_models.api_server import AIQChatRequest
28
+ from aiq.data_models.component_ref import FunctionRef
29
+ from aiq.data_models.component_ref import LLMRef
30
+ from aiq.data_models.function import FunctionBaseConfig
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ class ReasoningFunctionConfig(FunctionBaseConfig, name="reasoning_agent"):
36
+ """
37
+ Defines an AgentIQ function that performs reasoning on the input data.
38
+ Output is passed to the next function in the workflow.
39
+
40
+ Designed to be used with an InterceptingFunction.
41
+ """
42
+
43
+ llm_name: LLMRef = Field(description="The name of the LLM to use for reasoning.")
44
+ augmented_fn: FunctionRef = Field(description="The name of the function to reason on.")
45
+ verbose: bool = Field(default=False, description="Whether to log detailed information.")
46
+ reasoning_prompt_template: str = Field(
47
+ default=("You are an expert reasoning model task with creating a detailed execution plan"
48
+ " for a system that has the following description:\n\n"
49
+ "**Description:** \n{augmented_function_desc}\n\n"
50
+ "Given the following input and a list of available tools, please provide a detailed step-by-step plan"
51
+ " that an instruction following system can use to address the input. Ensure the plan includes:\n\n"
52
+ "1. Identifying the key components of the input.\n"
53
+ "2. Determining the most suitable tools for each task.\n"
54
+ "3. Outlining the sequence of actions to be taken.\n\n"
55
+ "**Input:** \n{input_text}\n\n"
56
+ "**Tools and description of the tool:** \n{tools}\n\n"
57
+ "An example plan could look like this:\n\n"
58
+ "1. Call tool A with input X\n"
59
+ "2. Call tool B with input Y\n"
60
+ "3. Interpret the output of tool A and B\n"
61
+ "4. Return the final result"
62
+ "\n\n **PLAN:**\n"),
63
+ description="The reasoning model prompt template.")
64
+
65
+ instruction_prompt_template: str = Field(
66
+ default=("Answer the following question based on message history: {input_text}"
67
+ "\n\nHere is a plan for execution that you could use to guide you if you wanted to:"
68
+ "\n\n{reasoning_output}"
69
+ "\n\nNOTE: Remember to follow your guidance on how to format output, etc."
70
+ "\n\n You must respond with the answer to the original question directly to the user."),
71
+ description="The instruction prompt template.")
72
+
73
+
74
+ @register_function(config_type=ReasoningFunctionConfig, framework_wrappers=[LLMFrameworkEnum.LANGCHAIN])
75
+ async def build_reasoning_function(config: ReasoningFunctionConfig, builder: Builder):
76
+ """
77
+ Build a ReasoningFunction from the provided config.
78
+
79
+ Args:
80
+ config (ReasoningFunctionConfig): The config for the ReasoningFunction.
81
+ builder (Builder): The Builder instance to use for building the function.
82
+
83
+ Returns:
84
+ ReasoningFunction: The built ReasoningFunction.
85
+ """
86
+ from langchain_core.language_models import BaseChatModel
87
+ from langchain_core.prompts import PromptTemplate
88
+
89
+ def remove_r1_think_tags(text: str):
90
+ pattern = r'(<think>)?.*?</think>\s*(.*)'
91
+
92
+ # Add re.DOTALL flag to make . match newlines
93
+ match = re.match(pattern, text, re.DOTALL)
94
+
95
+ if match:
96
+ return match.group(2)
97
+
98
+ return text
99
+
100
+ # Get the LLM to use for reasoning
101
+ llm: BaseChatModel = await builder.get_llm(config.llm_name, wrapper_type=LLMFrameworkEnum.LANGCHAIN)
102
+
103
+ # Get the augmented function's description
104
+ augmented_function = builder.get_function(config.augmented_fn)
105
+
106
+ # For now, we rely on runtime checking for type conversion
107
+
108
+ if augmented_function.description and augmented_function.description != "":
109
+ augmented_function_desc = augmented_function.description
110
+ else:
111
+ raise ValueError(f"Function {config.augmented_fn} does not have a description. Cannot augment "
112
+ f"function without a description.")
113
+
114
+ # Get the function dependencies of the augmented function
115
+ function_used_tools = builder.get_function_dependencies(config.augmented_fn).functions
116
+ tool_names_with_desc: list[tuple[str, str]] = []
117
+
118
+ for tool in function_used_tools:
119
+ tool_impl = builder.get_function(tool)
120
+ tool_names_with_desc.append((tool, tool_impl.description if hasattr(tool_impl, "description") else ""))
121
+
122
+ # Draft the reasoning prompt for the augmented function
123
+ template = PromptTemplate(template=config.reasoning_prompt_template,
124
+ input_variables=["augmented_function_desc", "input_text", "tools"],
125
+ validate_template=True)
126
+
127
+ downstream_template = PromptTemplate(template=config.instruction_prompt_template,
128
+ input_variables=["input_text", "reasoning_output"],
129
+ validate_template=True)
130
+
131
+ streaming_inner_fn = None
132
+ single_inner_fn = None
133
+
134
+ if augmented_function.has_streaming_output:
135
+
136
+ async def streaming_inner(
137
+ input_message: AIQChatRequest) -> AsyncGenerator[augmented_function.streaming_output_type]:
138
+ """
139
+ Perform reasoning on the input text.
140
+
141
+ Args:
142
+ input_message (AIQChatRequest): The input text to reason on.
143
+ """
144
+
145
+ input_text = "".join([str(message.model_dump()) + "\n" for message in input_message.messages])
146
+
147
+ prompt = await template.ainvoke(
148
+ input={
149
+ "augmented_function_desc": augmented_function_desc,
150
+ "input_text": input_text,
151
+ "tools": "\n".join([f"- {tool[0]}: {tool[1]}" for tool in tool_names_with_desc])
152
+ })
153
+
154
+ prompt = prompt.to_string()
155
+
156
+ # Get the reasoning output from the LLM
157
+ reasoning_output = ""
158
+
159
+ async for chunk in llm.astream(prompt):
160
+ reasoning_output += chunk.content
161
+
162
+ reasoning_output = remove_r1_think_tags(reasoning_output)
163
+
164
+ output = await downstream_template.ainvoke(input={
165
+ "input_text": input_text, "reasoning_output": reasoning_output
166
+ })
167
+
168
+ output = output.to_string()
169
+
170
+ if config.verbose:
171
+ logger.info("%s Reasoning plan and input to agent: \n\n%s", AGENT_LOG_PREFIX, output)
172
+
173
+ async for chunk in augmented_function.acall_stream(output):
174
+ yield chunk
175
+
176
+ streaming_inner_fn = streaming_inner
177
+
178
+ if augmented_function.has_single_output:
179
+
180
+ async def single_inner(input_message: AIQChatRequest) -> augmented_function.single_output_type:
181
+ """
182
+ Perform reasoning on the input text.
183
+
184
+ Args:
185
+ input_message (AIQChatRequest): The input text to reason on.
186
+ """
187
+
188
+ input_text = "".join([str(message.model_dump()) + "\n" for message in input_message.messages])
189
+
190
+ prompt = await template.ainvoke(
191
+ input={
192
+ "augmented_function_desc": augmented_function_desc,
193
+ "input_text": input_text,
194
+ "tools": "\n".join([f"- {tool[0]}: {tool[1]}" for tool in tool_names_with_desc])
195
+ })
196
+
197
+ prompt = prompt.to_string()
198
+
199
+ # Get the reasoning output from the LLM
200
+ reasoning_output = ""
201
+
202
+ async for chunk in llm.astream(prompt):
203
+ reasoning_output += chunk.content
204
+
205
+ reasoning_output = remove_r1_think_tags(reasoning_output)
206
+
207
+ output = await downstream_template.ainvoke(input={
208
+ "input_text": input_text, "reasoning_output": reasoning_output
209
+ })
210
+
211
+ output = output.to_string()
212
+
213
+ if config.verbose:
214
+ logger.info("%s Reasoning plan and input to agent: \n\n%s", AGENT_LOG_PREFIX, output)
215
+
216
+ return await augmented_function.acall_invoke(output)
217
+
218
+ single_inner_fn = single_inner
219
+
220
+ yield FunctionInfo.create(
221
+ single_fn=single_inner_fn,
222
+ stream_fn=streaming_inner_fn,
223
+ description=("Reasoning function that generates a detailed execution plan for a system based on the input."),
224
+ converters=augmented_function.converter_list)
aiq/agent/register.py ADDED
@@ -0,0 +1,23 @@
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
+ # pylint: disable=unused-import
17
+ # flake8: noqa
18
+
19
+ # Import any workflows which need to be automatically registered here
20
+ from .react_agent import register as react_agent
21
+ from .reasoning_agent import reasoning_agent
22
+ from .rewoo_agent import register as rewoo_agent
23
+ from .tool_calling_agent import register as tool_calling_agent
File without changes