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,24 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-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
+ from enum import Enum
17
+
18
+
19
+ class LLMFrameworkEnum(str, Enum):
20
+ LANGCHAIN = "langchain"
21
+ LLAMA_INDEX = "llama_index"
22
+ CREWAI = "crewai"
23
+ SEMANTIC_KERNEL = "semantic_kernel"
24
+ AGNO = "agno"
@@ -0,0 +1,73 @@
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 typing
17
+ from abc import ABC
18
+ from abc import abstractmethod
19
+
20
+ from aiq.data_models.front_end import FrontEndConfigT
21
+
22
+ if (typing.TYPE_CHECKING):
23
+ from aiq.data_models.config import AIQConfig
24
+
25
+
26
+ class FrontEndBase(typing.Generic[FrontEndConfigT], ABC):
27
+
28
+ def __init__(self, full_config: "AIQConfig"):
29
+ """
30
+ Initializes the FrontEndBase object with the specified AgentIQ configuration.
31
+
32
+ Parameters
33
+ ----------
34
+ full_config : AIQConfig
35
+ The configuration object to use for the front end.
36
+ """
37
+
38
+ super().__init__()
39
+
40
+ self._full_config: "AIQConfig" = full_config
41
+ self._front_end_config: FrontEndConfigT = typing.cast(FrontEndConfigT, full_config.general.front_end)
42
+
43
+ @property
44
+ def front_end_config(self) -> FrontEndConfigT:
45
+ """
46
+ Returns the front end configuration object extracted from the AgentIQ configuration.
47
+
48
+ Returns
49
+ -------
50
+ FrontEndConfigT
51
+ The front end configuration object.
52
+ """
53
+ return self._front_end_config
54
+
55
+ @property
56
+ def full_config(self) -> "AIQConfig":
57
+ """
58
+ Returns the full AgentIQ configuration object.
59
+
60
+ Returns
61
+ -------
62
+ AIQConfig
63
+ The full AgentIQ configuration object.
64
+ """
65
+
66
+ return self._full_config
67
+
68
+ @abstractmethod
69
+ async def run(self):
70
+ """
71
+ Runs the specified configuration file, launching the workflow until the front end is complete.
72
+ """
73
+ pass
@@ -0,0 +1,297 @@
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 typing
18
+ from abc import ABC
19
+ from abc import abstractmethod
20
+ from collections.abc import AsyncGenerator
21
+ from collections.abc import Awaitable
22
+ from collections.abc import Callable
23
+
24
+ from pydantic import BaseModel
25
+
26
+ from aiq.builder.context import AIQContext
27
+ from aiq.builder.function_base import FunctionBase
28
+ from aiq.builder.function_base import InputT
29
+ from aiq.builder.function_base import SingleOutputT
30
+ from aiq.builder.function_base import StreamingOutputT
31
+ from aiq.builder.function_info import FunctionInfo
32
+ from aiq.data_models.function import FunctionBaseConfig
33
+
34
+ _InvokeFnT = Callable[[InputT], Awaitable[SingleOutputT]]
35
+ _StreamFnT = Callable[[InputT], AsyncGenerator[StreamingOutputT]]
36
+
37
+ _T = typing.TypeVar("_T")
38
+
39
+ logger = logging.getLogger(__name__)
40
+
41
+
42
+ class Function(FunctionBase[InputT, StreamingOutputT, SingleOutputT], ABC):
43
+
44
+ def __init__(self,
45
+ *,
46
+ config: FunctionBaseConfig,
47
+ description: str | None,
48
+ input_schema: type[BaseModel] | None = None,
49
+ streaming_output_schema: type[BaseModel] | type[None] | None = None,
50
+ single_output_schema: type[BaseModel] | type[None] | None = None,
51
+ converters: list[Callable[[typing.Any], typing.Any]] | None = None):
52
+
53
+ super().__init__(input_schema=input_schema,
54
+ streaming_output_schema=streaming_output_schema,
55
+ single_output_schema=single_output_schema,
56
+ converters=converters)
57
+
58
+ self.config = config
59
+ self.description = description
60
+ self._context = AIQContext.get()
61
+
62
+ def convert(self, value: typing.Any, to_type: type[_T]) -> _T:
63
+ """
64
+ Converts the given value to the specified type using the function's converter.
65
+
66
+ Parameters
67
+ ----------
68
+ value : typing.Any
69
+ The value to convert.
70
+ to_type : type
71
+ The type to convert the value to.
72
+
73
+ Returns
74
+ -------
75
+ _T
76
+ The converted value.
77
+ """
78
+
79
+ return self._converter.convert(value, to_type=to_type)
80
+
81
+ @abstractmethod
82
+ async def _ainvoke(self, value: InputT) -> SingleOutputT:
83
+ pass
84
+
85
+ @typing.overload
86
+ async def ainvoke(self, value: InputT | typing.Any) -> SingleOutputT:
87
+ ...
88
+
89
+ @typing.overload
90
+ async def ainvoke(self, value: InputT | typing.Any, to_type: type[_T]) -> _T:
91
+ ...
92
+
93
+ @typing.final
94
+ async def ainvoke(self, value: InputT | typing.Any, to_type: type | None = None):
95
+ """
96
+ Runs the function with the given input and returns a single output from the function. This is the
97
+ main entry point for running a function.
98
+
99
+ Parameters
100
+ ----------
101
+ value : InputT | typing.Any
102
+ The input to the function.
103
+ to_type : type | None, optional
104
+ The type to convert the output to using the function's converter. When not specified, the
105
+ output will match `single_output_type`.
106
+
107
+ Returns
108
+ -------
109
+ typing.Any
110
+ The output of the function optionally converted to the specified type.
111
+ """
112
+
113
+ with self._context.push_active_function(self.config.type,
114
+ input_data=value) as manager: # Set the current invocation context
115
+ try:
116
+ converted_input: InputT = self._convert_input(value) # type: ignore
117
+
118
+ result = await self._ainvoke(converted_input)
119
+
120
+ if to_type is not None and not isinstance(result, to_type):
121
+ result = self._converter.convert(result, to_type=to_type)
122
+
123
+ manager.set_output(result)
124
+
125
+ return result
126
+ except Exception as e:
127
+ logger.error("Error with ainvoke in function with input: %s.", value, exc_info=True)
128
+ raise e
129
+
130
+ @typing.final
131
+ async def acall_invoke(self, *args, **kwargs):
132
+ """
133
+ A wrapper around `ainvoke` that allows for calling the function with arbitrary arguments and keyword arguments.
134
+ This is useful in scenarios where the function might be called by an LLM or other system which gives varying
135
+ inputs to the function. The function will attempt to convert the args and kwargs to the input schema of the
136
+ function.
137
+
138
+ Returns
139
+ -------
140
+ SingleOutputT
141
+ The output of the function.
142
+ """
143
+
144
+ if (len(args) == 1 and not kwargs):
145
+ # If only one argument is passed, assume it is the input just like ainvoke
146
+ return await self.ainvoke(value=args[0])
147
+
148
+ if (not args and kwargs):
149
+ # If only kwargs are passed, assume we are calling a function with named arguments in a dict
150
+ # This will rely on the processing in ainvoke to convert from dict to the correct input type
151
+ return await self.ainvoke(value=kwargs)
152
+
153
+ # Possibly have both args and kwargs, final attempt is to use the input schema object constructor.
154
+ try:
155
+ input_obj = self.input_schema(*args, **kwargs)
156
+
157
+ return await self.ainvoke(value=input_obj)
158
+ except Exception as e:
159
+ logger.error(
160
+ "Error in acall_invoke() converting input to function schema. Both args and kwargs were "
161
+ "supplied which could not be converted to the input schema. args: %s\nkwargs: %s\nschema: %s",
162
+ args,
163
+ kwargs,
164
+ self.input_schema)
165
+ raise e
166
+
167
+ @abstractmethod
168
+ async def _astream(self, value: InputT) -> AsyncGenerator[StreamingOutputT]:
169
+ yield # type: ignore
170
+
171
+ @typing.overload
172
+ async def astream(self, value: InputT | typing.Any) -> AsyncGenerator[SingleOutputT]:
173
+ ...
174
+
175
+ @typing.overload
176
+ async def astream(self, value: InputT | typing.Any, to_type: type[_T]) -> AsyncGenerator[_T]:
177
+ ...
178
+
179
+ @typing.final
180
+ async def astream(self, value: InputT | typing.Any, to_type: type | None = None):
181
+ """
182
+ Runs the function with the given input and returns a stream of outputs from the function. This is the main entry
183
+ point for running a function with streaming output.
184
+
185
+ Parameters
186
+ ----------
187
+ value : InputT | typing.Any
188
+ The input to the function.
189
+ to_type : type | None, optional
190
+ The type to convert the output to using the function's converter. When not specified, the
191
+ output will match `streaming_output_type`.
192
+
193
+ Yields
194
+ ------
195
+ typing.Any
196
+ The output of the function optionally converted to the specified type.
197
+ """
198
+
199
+ with self._context.push_active_function(self.config.type, input_data=value):
200
+ try:
201
+ converted_input: InputT = self._convert_input(value) # type: ignore
202
+
203
+ async for data in self._astream(converted_input):
204
+
205
+ if to_type is not None and not isinstance(data, to_type):
206
+ yield self._converter.convert(data, to_type=to_type)
207
+ else:
208
+ yield data
209
+ except Exception as e:
210
+ logger.error("Error with astream in function with input: %s.", value, exc_info=True)
211
+ raise e
212
+
213
+ @typing.final
214
+ async def acall_stream(self, *args, **kwargs):
215
+ """
216
+ A wrapper around `astream` that allows for calling the function with arbitrary arguments and keyword arguments.
217
+ This is useful in scenarios where the function might be called by an LLM or other system which gives varying
218
+ inputs to the function. The function will attempt to convert the args and kwargs to the input schema of the
219
+ function.
220
+
221
+ Yields
222
+ ------
223
+ StreamingOutputT
224
+ The output of the function.
225
+ """
226
+
227
+ if (len(args) == 1 and not kwargs):
228
+ # If only one argument is passed, assume it is the input just like ainvoke
229
+ async for x in self.astream(value=args[0]):
230
+ yield x
231
+
232
+ elif (not args and kwargs):
233
+ # If only kwargs are passed, assume we are calling a function with named arguments in a dict
234
+ # This will rely on the processing in ainvoke to convert from dict to the correct input type
235
+ async for x in self.astream(value=kwargs):
236
+ yield x
237
+
238
+ # Possibly have both args and kwargs, final attempt is to use the input schema object constructor.
239
+ else:
240
+ try:
241
+ input_obj = self.input_schema(*args, **kwargs)
242
+
243
+ async for x in self.astream(value=input_obj):
244
+ yield x
245
+ except Exception as e:
246
+ logger.error(
247
+ "Error in acall_stream() converting input to function schema. Both args and kwargs were "
248
+ "supplied which could not be converted to the input schema. args: %s\nkwargs: %s\nschema: %s",
249
+ args,
250
+ kwargs,
251
+ self.input_schema)
252
+ raise e
253
+
254
+
255
+ class LambdaFunction(Function[InputT, StreamingOutputT, SingleOutputT]):
256
+
257
+ def __init__(self, *, config: FunctionBaseConfig, info: FunctionInfo):
258
+
259
+ super().__init__(config=config,
260
+ description=info.description,
261
+ input_schema=info.input_schema,
262
+ streaming_output_schema=info.stream_output_schema,
263
+ single_output_schema=info.single_output_schema,
264
+ converters=info.converters)
265
+
266
+ self._info = info
267
+
268
+ self._ainvoke_fn: _InvokeFnT = info.single_fn
269
+ self._astream_fn: _StreamFnT = info.stream_fn
270
+
271
+ @property
272
+ def has_streaming_output(self) -> bool:
273
+ return self._astream_fn is not None
274
+
275
+ @property
276
+ def has_single_output(self) -> bool:
277
+ return self._ainvoke_fn is not None
278
+
279
+ async def _ainvoke(self, value: InputT) -> SingleOutputT:
280
+ return await self._ainvoke_fn(value)
281
+
282
+ async def _astream(self, value: InputT) -> AsyncGenerator[StreamingOutputT]:
283
+ async for x in self._astream_fn(value):
284
+ yield x
285
+
286
+ @staticmethod
287
+ def from_info(*, config: FunctionBaseConfig,
288
+ info: FunctionInfo) -> 'LambdaFunction[InputT, StreamingOutputT, SingleOutputT]':
289
+
290
+ input_type: type = info.input_type
291
+ streaming_output_type = info.stream_output_type
292
+ single_output_type = info.single_output_type
293
+
294
+ class FunctionImpl(LambdaFunction[input_type, streaming_output_type, single_output_type]):
295
+ pass
296
+
297
+ return FunctionImpl(config=config, info=info)