aiqtoolkit 1.1.0a20250515__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.0a20250515.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 -135
  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.0a20250515.dist-info/METADATA +0 -331
  315. aiqtoolkit-1.1.0a20250515.dist-info/RECORD +0 -316
  316. aiqtoolkit-1.1.0a20250515.dist-info/entry_points.txt +0 -17
  317. aiqtoolkit-1.1.0a20250515.dist-info/licenses/LICENSE-3rd-party.txt +0 -3686
  318. aiqtoolkit-1.1.0a20250515.dist-info/licenses/LICENSE.md +0 -201
  319. aiqtoolkit-1.1.0a20250515.dist-info/top_level.txt +0 -1
@@ -1,607 +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 asyncio
17
- import logging
18
- import os
19
- import typing
20
- from abc import ABC
21
- from abc import abstractmethod
22
- from contextlib import asynccontextmanager
23
- from functools import partial
24
- from pathlib import Path
25
-
26
- from fastapi import BackgroundTasks
27
- from fastapi import Body
28
- from fastapi import FastAPI
29
- from fastapi import Request
30
- from fastapi import Response
31
- from fastapi.exceptions import HTTPException
32
- from fastapi.middleware.cors import CORSMiddleware
33
- from fastapi.responses import StreamingResponse
34
- from pydantic import BaseModel
35
-
36
- from aiq.builder.workflow_builder import WorkflowBuilder
37
- from aiq.data_models.api_server import AIQChatRequest
38
- from aiq.data_models.api_server import AIQChatResponse
39
- from aiq.data_models.api_server import AIQChatResponseChunk
40
- from aiq.data_models.api_server import AIQResponseIntermediateStep
41
- from aiq.data_models.config import AIQConfig
42
- from aiq.eval.config import EvaluationRunOutput
43
- from aiq.eval.evaluate import EvaluationRun
44
- from aiq.eval.evaluate import EvaluationRunConfig
45
- from aiq.front_ends.fastapi.fastapi_front_end_config import AIQEvaluateRequest
46
- from aiq.front_ends.fastapi.fastapi_front_end_config import AIQEvaluateResponse
47
- from aiq.front_ends.fastapi.fastapi_front_end_config import AIQEvaluateStatusResponse
48
- from aiq.front_ends.fastapi.fastapi_front_end_config import FastApiFrontEndConfig
49
- from aiq.front_ends.fastapi.job_store import JobInfo
50
- from aiq.front_ends.fastapi.job_store import JobStore
51
- from aiq.front_ends.fastapi.response_helpers import generate_single_response
52
- from aiq.front_ends.fastapi.response_helpers import generate_streaming_response_as_str
53
- from aiq.front_ends.fastapi.response_helpers import generate_streaming_response_full_as_str
54
- from aiq.front_ends.fastapi.step_adaptor import StepAdaptor
55
- from aiq.front_ends.fastapi.websocket import AIQWebSocket
56
- from aiq.runtime.session import AIQSessionManager
57
-
58
- logger = logging.getLogger(__name__)
59
-
60
-
61
- class FastApiFrontEndPluginWorkerBase(ABC):
62
-
63
- def __init__(self, config: AIQConfig):
64
- self._config = config
65
-
66
- assert isinstance(config.general.front_end,
67
- FastApiFrontEndConfig), ("Front end config is not FastApiFrontEndConfig")
68
-
69
- self._front_end_config = config.general.front_end
70
-
71
- @property
72
- def config(self) -> AIQConfig:
73
- return self._config
74
-
75
- @property
76
- def front_end_config(self) -> FastApiFrontEndConfig:
77
-
78
- return self._front_end_config
79
-
80
- def build_app(self) -> FastAPI:
81
-
82
- # Create the FastAPI app and configure it
83
- @asynccontextmanager
84
- async def lifespan(starting_app: FastAPI):
85
-
86
- logger.debug("Starting AIQ Toolkit server from process %s", os.getpid())
87
-
88
- async with WorkflowBuilder.from_config(self.config) as builder:
89
-
90
- await self.configure(starting_app, builder)
91
-
92
- yield
93
-
94
- # If a cleanup task is running, cancel it
95
- cleanup_task = getattr(starting_app.state, "cleanup_task", None)
96
- if cleanup_task:
97
- logger.info("Cancelling cleanup task")
98
- cleanup_task.cancel()
99
-
100
- logger.debug("Closing AIQ Toolkit server from process %s", os.getpid())
101
-
102
- aiq_app = FastAPI(lifespan=lifespan)
103
-
104
- self.set_cors_config(aiq_app)
105
-
106
- return aiq_app
107
-
108
- def set_cors_config(self, aiq_app: FastAPI) -> None:
109
- """
110
- Set the cross origin resource sharing configuration.
111
- """
112
- cors_kwargs = {}
113
-
114
- if self.front_end_config.cors.allow_origins is not None:
115
- cors_kwargs["allow_origins"] = self.front_end_config.cors.allow_origins
116
-
117
- if self.front_end_config.cors.allow_origin_regex is not None:
118
- cors_kwargs["allow_origin_regex"] = self.front_end_config.cors.allow_origin_regex
119
-
120
- if self.front_end_config.cors.allow_methods is not None:
121
- cors_kwargs["allow_methods"] = self.front_end_config.cors.allow_methods
122
-
123
- if self.front_end_config.cors.allow_headers is not None:
124
- cors_kwargs["allow_headers"] = self.front_end_config.cors.allow_headers
125
-
126
- if self.front_end_config.cors.allow_credentials is not None:
127
- cors_kwargs["allow_credentials"] = self.front_end_config.cors.allow_credentials
128
-
129
- if self.front_end_config.cors.expose_headers is not None:
130
- cors_kwargs["expose_headers"] = self.front_end_config.cors.expose_headers
131
-
132
- if self.front_end_config.cors.max_age is not None:
133
- cors_kwargs["max_age"] = self.front_end_config.cors.max_age
134
-
135
- aiq_app.add_middleware(
136
- CORSMiddleware,
137
- **cors_kwargs,
138
- )
139
-
140
- @abstractmethod
141
- async def configure(self, app: FastAPI, builder: WorkflowBuilder):
142
- pass
143
-
144
- @abstractmethod
145
- def get_step_adaptor(self) -> StepAdaptor:
146
- pass
147
-
148
-
149
- class RouteInfo(BaseModel):
150
-
151
- function_name: str | None
152
-
153
-
154
- class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
155
-
156
- def get_step_adaptor(self) -> StepAdaptor:
157
-
158
- return StepAdaptor(self.front_end_config.step_adaptor)
159
-
160
- async def configure(self, app: FastAPI, builder: WorkflowBuilder):
161
-
162
- # Do things like setting the base URL and global configuration options
163
- app.root_path = self.front_end_config.root_path
164
-
165
- await self.add_routes(app, builder)
166
-
167
- async def add_routes(self, app: FastAPI, builder: WorkflowBuilder):
168
-
169
- await self.add_default_route(app, AIQSessionManager(builder.build()))
170
- await self.add_evaluate_route(app, AIQSessionManager(builder.build()))
171
-
172
- for ep in self.front_end_config.endpoints:
173
-
174
- entry_workflow = builder.build(entry_function=ep.function_name)
175
-
176
- await self.add_route(app, endpoint=ep, session_manager=AIQSessionManager(entry_workflow))
177
-
178
- async def add_default_route(self, app: FastAPI, session_manager: AIQSessionManager):
179
-
180
- await self.add_route(app, self.front_end_config.workflow, session_manager)
181
-
182
- async def add_evaluate_route(self, app: FastAPI, session_manager: AIQSessionManager):
183
- """Add the evaluate endpoint to the FastAPI app."""
184
-
185
- response_500 = {
186
- "description": "Internal Server Error",
187
- "content": {
188
- "application/json": {
189
- "example": {
190
- "detail": "Internal server error occurred"
191
- }
192
- }
193
- },
194
- }
195
-
196
- # Create job store for tracking evaluation jobs
197
- job_store = JobStore()
198
- # Don't run multiple evaluations at the same time
199
- evaluation_lock = asyncio.Lock()
200
-
201
- async def periodic_cleanup(job_store: JobStore):
202
- while True:
203
- try:
204
- job_store.cleanup_expired_jobs()
205
- logger.debug("Expired jobs cleaned up")
206
- except Exception as e:
207
- logger.error("Error during job cleanup: %s", str(e))
208
- await asyncio.sleep(300) # every 5 minutes
209
-
210
- def create_cleanup_task():
211
- # Schedule periodic cleanup of expired jobs on first job creation
212
- if not hasattr(app.state, "cleanup_task"):
213
- logger.info("Starting periodic cleanup task")
214
- app.state.cleanup_task = asyncio.create_task(periodic_cleanup(job_store))
215
-
216
- async def run_evaluation(job_id: str, config_file: str, reps: int, session_manager: AIQSessionManager):
217
- """Background task to run the evaluation."""
218
- async with evaluation_lock:
219
- try:
220
- # Create EvaluationRunConfig using the CLI defaults
221
- eval_config = EvaluationRunConfig(config_file=Path(config_file), dataset=None, reps=reps)
222
-
223
- # Create a new EvaluationRun with the evaluation-specific config
224
- job_store.update_status(job_id, "running")
225
- eval_runner = EvaluationRun(eval_config)
226
- output: EvaluationRunOutput = await eval_runner.run_and_evaluate(session_manager=session_manager,
227
- job_id=job_id)
228
- if output.workflow_interrupted:
229
- job_store.update_status(job_id, "interrupted")
230
- else:
231
- parent_dir = os.path.dirname(
232
- output.workflow_output_file) if output.workflow_output_file else None
233
-
234
- job_store.update_status(job_id, "success", output_path=str(parent_dir))
235
- except Exception as e:
236
- logger.error("Error in evaluation job %s: %s", job_id, str(e))
237
- job_store.update_status(job_id, "failure", error=str(e))
238
-
239
- async def start_evaluation(request: AIQEvaluateRequest,
240
- background_tasks: BackgroundTasks,
241
- http_request: Request):
242
- """Handle evaluation requests."""
243
-
244
- async with session_manager.session(request=http_request):
245
-
246
- # if job_id is present and already exists return the job info
247
- if request.job_id:
248
- job = job_store.get_job(request.job_id)
249
- if job:
250
- return AIQEvaluateResponse(job_id=job.job_id, status=job.status)
251
-
252
- job_id = job_store.create_job(request.config_file, request.job_id, request.expiry_seconds)
253
- create_cleanup_task()
254
- background_tasks.add_task(run_evaluation, job_id, request.config_file, request.reps, session_manager)
255
-
256
- return AIQEvaluateResponse(job_id=job_id, status="submitted")
257
-
258
- def translate_job_to_response(job: JobInfo) -> AIQEvaluateStatusResponse:
259
- """Translate a JobInfo object to an AIQEvaluateStatusResponse."""
260
- return AIQEvaluateStatusResponse(job_id=job.job_id,
261
- status=job.status,
262
- config_file=str(job.config_file),
263
- error=job.error,
264
- output_path=str(job.output_path),
265
- created_at=job.created_at,
266
- updated_at=job.updated_at,
267
- expires_at=job_store.get_expires_at(job))
268
-
269
- async def get_job_status(job_id: str, http_request: Request) -> AIQEvaluateStatusResponse:
270
- """Get the status of an evaluation job."""
271
- logger.info("Getting status for job %s", job_id)
272
-
273
- async with session_manager.session(request=http_request):
274
-
275
- job = job_store.get_job(job_id)
276
- if not job:
277
- logger.warning("Job %s not found", job_id)
278
- raise HTTPException(status_code=404, detail=f"Job {job_id} not found")
279
- logger.info(f"Found job {job_id} with status {job.status}")
280
- return translate_job_to_response(job)
281
-
282
- async def get_last_job_status(http_request: Request) -> AIQEvaluateStatusResponse:
283
- """Get the status of the last created evaluation job."""
284
- logger.info("Getting last job status")
285
-
286
- async with session_manager.session(request=http_request):
287
-
288
- job = job_store.get_last_job()
289
- if not job:
290
- logger.warning("No jobs found when requesting last job status")
291
- raise HTTPException(status_code=404, detail="No jobs found")
292
- logger.info("Found last job %s with status %s", job.job_id, job.status)
293
- return translate_job_to_response(job)
294
-
295
- async def get_jobs(http_request: Request, status: str | None = None) -> list[AIQEvaluateStatusResponse]:
296
- """Get all jobs, optionally filtered by status."""
297
-
298
- async with session_manager.session(request=http_request):
299
-
300
- if status is None:
301
- logger.info("Getting all jobs")
302
- jobs = job_store.get_all_jobs()
303
- else:
304
- logger.info("Getting jobs with status %s", status)
305
- jobs = job_store.get_jobs_by_status(status)
306
- logger.info("Found %d jobs", len(jobs))
307
- return [translate_job_to_response(job) for job in jobs]
308
-
309
- if self.front_end_config.evaluate.path:
310
- # Add last job endpoint first (most specific)
311
- app.add_api_route(
312
- path=f"{self.front_end_config.evaluate.path}/job/last",
313
- endpoint=get_last_job_status,
314
- methods=["GET"],
315
- response_model=AIQEvaluateStatusResponse,
316
- description="Get the status of the last created evaluation job",
317
- responses={
318
- 404: {
319
- "description": "No jobs found"
320
- }, 500: response_500
321
- },
322
- )
323
-
324
- # Add specific job endpoint (least specific)
325
- app.add_api_route(
326
- path=f"{self.front_end_config.evaluate.path}/job/{{job_id}}",
327
- endpoint=get_job_status,
328
- methods=["GET"],
329
- response_model=AIQEvaluateStatusResponse,
330
- description="Get the status of an evaluation job",
331
- responses={
332
- 404: {
333
- "description": "Job not found"
334
- }, 500: response_500
335
- },
336
- )
337
-
338
- # Add jobs endpoint with optional status query parameter
339
- app.add_api_route(
340
- path=f"{self.front_end_config.evaluate.path}/jobs",
341
- endpoint=get_jobs,
342
- methods=["GET"],
343
- response_model=list[AIQEvaluateStatusResponse],
344
- description="Get all jobs, optionally filtered by status",
345
- responses={500: response_500},
346
- )
347
-
348
- # Add HTTP endpoint for evaluation
349
- app.add_api_route(
350
- path=self.front_end_config.evaluate.path,
351
- endpoint=start_evaluation,
352
- methods=[self.front_end_config.evaluate.method],
353
- response_model=AIQEvaluateResponse,
354
- description=self.front_end_config.evaluate.description,
355
- responses={500: response_500},
356
- )
357
-
358
- async def add_route(self,
359
- app: FastAPI,
360
- endpoint: FastApiFrontEndConfig.EndpointBase,
361
- session_manager: AIQSessionManager):
362
-
363
- workflow = session_manager.workflow
364
-
365
- if (endpoint.websocket_path):
366
- app.add_websocket_route(endpoint.websocket_path,
367
- partial(AIQWebSocket, session_manager, self.get_step_adaptor()))
368
-
369
- GenerateBodyType = workflow.input_schema # pylint: disable=invalid-name
370
- GenerateStreamResponseType = workflow.streaming_output_schema # pylint: disable=invalid-name
371
- GenerateSingleResponseType = workflow.single_output_schema # pylint: disable=invalid-name
372
-
373
- # Ensure that the input is in the body. POD types are treated as query parameters
374
- if (not issubclass(GenerateBodyType, BaseModel)):
375
- GenerateBodyType = typing.Annotated[GenerateBodyType, Body()]
376
-
377
- response_500 = {
378
- "description": "Internal Server Error",
379
- "content": {
380
- "application/json": {
381
- "example": {
382
- "detail": "Internal server error occurred"
383
- }
384
- }
385
- },
386
- }
387
-
388
- def get_single_endpoint(result_type: type | None):
389
-
390
- async def get_single(response: Response, request: Request):
391
-
392
- response.headers["Content-Type"] = "application/json"
393
-
394
- async with session_manager.session(request=request):
395
-
396
- return await generate_single_response(None, session_manager, result_type=result_type)
397
-
398
- return get_single
399
-
400
- def get_streaming_endpoint(streaming: bool, result_type: type | None, output_type: type | None):
401
-
402
- async def get_stream(request: Request):
403
-
404
- async with session_manager.session(request=request):
405
-
406
- return StreamingResponse(headers={"Content-Type": "text/event-stream; charset=utf-8"},
407
- content=generate_streaming_response_as_str(
408
- None,
409
- session_manager=session_manager,
410
- streaming=streaming,
411
- step_adaptor=self.get_step_adaptor(),
412
- result_type=result_type,
413
- output_type=output_type))
414
-
415
- return get_stream
416
-
417
- def get_streaming_raw_endpoint(streaming: bool, result_type: type | None, output_type: type | None):
418
-
419
- async def get_stream(filter_steps: str | None = None):
420
-
421
- return StreamingResponse(headers={"Content-Type": "text/event-stream; charset=utf-8"},
422
- content=generate_streaming_response_full_as_str(
423
- None,
424
- session_manager=session_manager,
425
- streaming=streaming,
426
- result_type=result_type,
427
- output_type=output_type,
428
- filter_steps=filter_steps))
429
-
430
- return get_stream
431
-
432
- def post_single_endpoint(request_type: type, result_type: type | None):
433
-
434
- async def post_single(response: Response, request: Request, payload: request_type):
435
-
436
- response.headers["Content-Type"] = "application/json"
437
-
438
- async with session_manager.session(request=request):
439
-
440
- return await generate_single_response(payload, session_manager, result_type=result_type)
441
-
442
- return post_single
443
-
444
- def post_streaming_endpoint(request_type: type,
445
- streaming: bool,
446
- result_type: type | None,
447
- output_type: type | None):
448
-
449
- async def post_stream(request: Request, payload: request_type):
450
-
451
- async with session_manager.session(request=request):
452
-
453
- return StreamingResponse(headers={"Content-Type": "text/event-stream; charset=utf-8"},
454
- content=generate_streaming_response_as_str(
455
- payload,
456
- session_manager=session_manager,
457
- streaming=streaming,
458
- step_adaptor=self.get_step_adaptor(),
459
- result_type=result_type,
460
- output_type=output_type))
461
-
462
- return post_stream
463
-
464
- def post_streaming_raw_endpoint(request_type: type,
465
- streaming: bool,
466
- result_type: type | None,
467
- output_type: type | None):
468
- """
469
- Stream raw intermediate steps without any step adaptor translations.
470
- """
471
-
472
- async def post_stream(payload: request_type, filter_steps: str | None = None):
473
-
474
- return StreamingResponse(headers={"Content-Type": "text/event-stream; charset=utf-8"},
475
- content=generate_streaming_response_full_as_str(
476
- payload,
477
- session_manager=session_manager,
478
- streaming=streaming,
479
- result_type=result_type,
480
- output_type=output_type,
481
- filter_steps=filter_steps))
482
-
483
- return post_stream
484
-
485
- if (endpoint.path):
486
- if (endpoint.method == "GET"):
487
-
488
- app.add_api_route(
489
- path=endpoint.path,
490
- endpoint=get_single_endpoint(result_type=GenerateSingleResponseType),
491
- methods=[endpoint.method],
492
- response_model=GenerateSingleResponseType,
493
- description=endpoint.description,
494
- responses={500: response_500},
495
- )
496
-
497
- app.add_api_route(
498
- path=f"{endpoint.path}/stream",
499
- endpoint=get_streaming_endpoint(streaming=True,
500
- result_type=GenerateStreamResponseType,
501
- output_type=GenerateStreamResponseType),
502
- methods=[endpoint.method],
503
- response_model=GenerateStreamResponseType,
504
- description=endpoint.description,
505
- responses={500: response_500},
506
- )
507
-
508
- app.add_api_route(
509
- path=f"{endpoint.path}/full",
510
- endpoint=get_streaming_raw_endpoint(streaming=True,
511
- result_type=GenerateStreamResponseType,
512
- output_type=GenerateStreamResponseType),
513
- methods=[endpoint.method],
514
- description="Stream raw intermediate steps without any step adaptor translations.\n"
515
- "Use filter_steps query parameter to filter steps by type (comma-separated list) or\
516
- set to 'none' to suppress all intermediate steps.",
517
- )
518
-
519
- elif (endpoint.method == "POST"):
520
-
521
- app.add_api_route(
522
- path=endpoint.path,
523
- endpoint=post_single_endpoint(request_type=GenerateBodyType,
524
- result_type=GenerateSingleResponseType),
525
- methods=[endpoint.method],
526
- response_model=GenerateSingleResponseType,
527
- description=endpoint.description,
528
- responses={500: response_500},
529
- )
530
-
531
- app.add_api_route(
532
- path=f"{endpoint.path}/stream",
533
- endpoint=post_streaming_endpoint(request_type=GenerateBodyType,
534
- streaming=True,
535
- result_type=GenerateStreamResponseType,
536
- output_type=GenerateStreamResponseType),
537
- methods=[endpoint.method],
538
- response_model=GenerateStreamResponseType,
539
- description=endpoint.description,
540
- responses={500: response_500},
541
- )
542
-
543
- app.add_api_route(
544
- path=f"{endpoint.path}/full",
545
- endpoint=post_streaming_raw_endpoint(request_type=GenerateBodyType,
546
- streaming=True,
547
- result_type=GenerateStreamResponseType,
548
- output_type=GenerateStreamResponseType),
549
- methods=[endpoint.method],
550
- response_model=GenerateStreamResponseType,
551
- description="Stream raw intermediate steps without any step adaptor translations.\n"
552
- "Use filter_steps query parameter to filter steps by type (comma-separated list) or \
553
- set to 'none' to suppress all intermediate steps.",
554
- responses={500: response_500},
555
- )
556
-
557
- else:
558
- raise ValueError(f"Unsupported method {endpoint.method}")
559
-
560
- if (endpoint.openai_api_path):
561
- if (endpoint.method == "GET"):
562
-
563
- app.add_api_route(
564
- path=endpoint.openai_api_path,
565
- endpoint=get_single_endpoint(result_type=AIQChatResponse),
566
- methods=[endpoint.method],
567
- response_model=AIQChatResponse,
568
- description=endpoint.description,
569
- responses={500: response_500},
570
- )
571
-
572
- app.add_api_route(
573
- path=f"{endpoint.openai_api_path}/stream",
574
- endpoint=get_streaming_endpoint(streaming=True,
575
- result_type=AIQChatResponseChunk,
576
- output_type=AIQChatResponseChunk),
577
- methods=[endpoint.method],
578
- response_model=AIQChatResponseChunk,
579
- description=endpoint.description,
580
- responses={500: response_500},
581
- )
582
-
583
- elif (endpoint.method == "POST"):
584
-
585
- app.add_api_route(
586
- path=endpoint.openai_api_path,
587
- endpoint=post_single_endpoint(request_type=AIQChatRequest, result_type=AIQChatResponse),
588
- methods=[endpoint.method],
589
- response_model=AIQChatResponse,
590
- description=endpoint.description,
591
- responses={500: response_500},
592
- )
593
-
594
- app.add_api_route(
595
- path=f"{endpoint.openai_api_path}/stream",
596
- endpoint=post_streaming_endpoint(request_type=AIQChatRequest,
597
- streaming=True,
598
- result_type=AIQChatResponseChunk,
599
- output_type=AIQChatResponseChunk),
600
- methods=[endpoint.method],
601
- response_model=AIQChatResponseChunk | AIQResponseIntermediateStep,
602
- description=endpoint.description,
603
- responses={500: response_500},
604
- )
605
-
606
- else:
607
- raise ValueError(f"Unsupported method {endpoint.method}")
@@ -1,80 +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 asyncio
17
- import logging
18
-
19
- from aiq.builder.context import AIQContext
20
- from aiq.data_models.api_server import AIQResponseIntermediateStep
21
- from aiq.data_models.intermediate_step import IntermediateStep
22
-
23
- logger = logging.getLogger(__name__)
24
-
25
-
26
- async def pull_intermediate(_q, adapter):
27
- """
28
- Subscribes to the runner's event stream (which is now a simplified Observable)
29
- using direct callbacks. Processes each event with the adapter and enqueues
30
- results to `_q`.
31
- """
32
- intermediate_done = asyncio.Event()
33
- context = AIQContext.get()
34
- loop = asyncio.get_running_loop()
35
-
36
- async def set_intermediate_done():
37
- intermediate_done.set()
38
-
39
- def on_next_cb(item: IntermediateStep):
40
- """
41
- Synchronously called whenever the runner publishes an event.
42
- We process it, then place it into the async queue (via a small async task).
43
- If adapter is None, convert the raw IntermediateStep into the complete
44
- AIQResponseIntermediateStep and place it into the queue.
45
- """
46
- if adapter is None:
47
- adapted = AIQResponseIntermediateStep(id=item.UUID,
48
- type=item.event_type,
49
- name=item.name or "",
50
- parent_id=item.parent_id,
51
- payload=item.payload.model_dump_json())
52
- else:
53
- adapted = adapter.process(item)
54
-
55
- if adapted is not None:
56
- loop.create_task(_q.put(adapted))
57
-
58
- def on_error_cb(exc: Exception):
59
- """
60
- Called if the runner signals an error. We log it and unblock our wait.
61
- """
62
- logger.error("Hit on_error: %s", exc)
63
-
64
- loop.create_task(set_intermediate_done())
65
-
66
- def on_complete_cb():
67
- """
68
- Called once the runner signals no more items. We unblock our wait.
69
- """
70
- logger.debug("Completed reading intermediate steps")
71
-
72
- loop.create_task(set_intermediate_done())
73
-
74
- # Subscribe to the runner's "reactive_event_stream" (now a simple Observable)
75
- _ = context.intermediate_step_manager.subscribe(on_next=on_next_cb,
76
- on_error=on_error_cb,
77
- on_complete=on_complete_cb)
78
-
79
- # Wait until on_complete or on_error sets intermediate_done
80
- return intermediate_done