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,215 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import json
17
+ import logging
18
+ import os
19
+ import shutil
20
+ from pathlib import Path
21
+
22
+ from aiq.data_models.swe_bench_model import SWEBenchInput
23
+ from aiq.data_models.swe_bench_model import SWEBenchOutput
24
+ from aiq.eval.evaluator.evaluator_model import EvalInput
25
+ from aiq.eval.evaluator.evaluator_model import EvalOutput
26
+
27
+ try:
28
+ import swebench.harness.run_evaluation as swebench_eval
29
+ from swebench.harness.constants import MAP_REPO_VERSION_TO_SPECS
30
+ except ImportError as exc:
31
+ raise ImportError("Please install swebench to use this evaluator") from exc
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ class SweBenchEvaluator:
37
+
38
+ def __init__(self, run_id: str, max_workers: int, output_dir: Path):
39
+
40
+ self.run_id = run_id
41
+ self.max_workers = max_workers
42
+ self.output_dir = output_dir
43
+
44
+ # metadata
45
+ self._unsupported_repos = []
46
+ self._swe_bench_inputs = []
47
+ self._swe_bench_outputs = []
48
+ self._model_name_or_path = "no_llm"
49
+
50
+ def get_model_name_from_output(self, workflow_output: list[dict]) -> str | None:
51
+ """Fetch the `model_name_or_path` from the first entry in the list."""
52
+ return workflow_output[0].get("model_name_or_path") if workflow_output else None
53
+
54
+ @staticmethod
55
+ def empty_report_dir(report_dir: Path):
56
+ """Remove the current contents of the report directory."""
57
+ os.makedirs(report_dir, exist_ok=True)
58
+
59
+ # Iterate through all files in the directory and remove them
60
+ for item in report_dir.iterdir():
61
+ if item.is_file(): # Remove files only
62
+ item.unlink()
63
+ elif item.is_dir(): # Remove subdirectories and their contents
64
+ shutil.rmtree(item)
65
+
66
+ @staticmethod
67
+ def move_report_and_logs(swe_bench_report_file: str, logs_dir: str, report_dir: Path):
68
+ """ Temorary function to move the report and logs to the output directory"""
69
+ try:
70
+ shutil.move(swe_bench_report_file, report_dir)
71
+ except Exception as e:
72
+ logger.exception("Error moving report file: %s", e, exc_info=True)
73
+
74
+ try:
75
+ dest_logs_dir = os.path.join(report_dir, 'logs')
76
+ shutil.move(logs_dir, dest_logs_dir)
77
+ except Exception as e:
78
+ logger.exception("Error moving logs directory: %s", e, exc_info=True)
79
+
80
+ def is_repo_supported(self, repo: str, version: str) -> bool:
81
+ """Check if the repo is supported by swebench"""
82
+
83
+ try:
84
+ _ = MAP_REPO_VERSION_TO_SPECS[repo][str(version)]
85
+ except KeyError:
86
+ self._unsupported_repos.append({repo, version})
87
+ return False
88
+ return True
89
+
90
+ def process_eval_input(self, eval_input: EvalInput) -> tuple[Path, Path]:
91
+ """Converts EvalInput into lists of SWEBenchInput and SWEBenchOutput models and applies filtering."""
92
+ # Convert input_obj and output_obj JSON strings to SWEBenchInput and SWEBenchOutput models
93
+ swebench_inputs = []
94
+ swebench_outputs = []
95
+
96
+ for item in eval_input.eval_input_items:
97
+ try:
98
+ swebench_input = SWEBenchInput.model_validate_json(item.input_obj) # Convert input JSON to model
99
+ swebench_input.version = str(swebench_input.version) # Convert version to string
100
+ swebench_inputs.append(swebench_input)
101
+
102
+ if item.output_obj: # Convert output JSON to model if available
103
+ swebench_output = SWEBenchOutput.model_validate_json(item.output_obj)
104
+ swebench_outputs.append(swebench_output)
105
+ # this is bit of a hack to match the swe_bench harness
106
+ self._model_name_or_path = swebench_output.model_name_or_path
107
+
108
+ except Exception as e:
109
+ logger.exception("Failed to parse EvalInputItem %s: %s", item.id, e, exc_info=True)
110
+
111
+ # Filter out repos/version not supported by SWEBench
112
+ supported_inputs = [
113
+ swebench for swebench in swebench_inputs if self.is_repo_supported(swebench.repo, swebench.version)
114
+ ]
115
+
116
+ if not supported_inputs:
117
+ logger.error("No supported instances; nothing to evaluate")
118
+ return None, None
119
+
120
+ if len(supported_inputs) < len(swebench_inputs):
121
+ logger.warning("The following repos are not supported by SWEBench and were skipped:\n %s",
122
+ {s.repo
123
+ for s in swebench_inputs if s not in supported_inputs})
124
+
125
+ # Write SWEBenchInput to file
126
+ workflow_input_file = self.output_dir / "aiq_workflow_input.json"
127
+ workflow_input_file.parent.mkdir(parents=True, exist_ok=True)
128
+ Path(workflow_input_file).write_text(json.dumps([swebench.model_dump() for swebench in supported_inputs],
129
+ indent=2),
130
+ encoding="utf-8")
131
+ logger.info("Workflow input written to %s", workflow_input_file)
132
+
133
+ # Filter SWEBenchOutput to include only instance_ids present in SWEBenchInput
134
+ valid_instance_ids = {swebench.instance_id for swebench in supported_inputs}
135
+ filtered_outputs = [output for output in swebench_outputs if output.instance_id in valid_instance_ids]
136
+
137
+ if not filtered_outputs:
138
+ logger.error("No supported outputs; nothing to evaluate")
139
+ return None, None
140
+
141
+ # Write SWEBenchOutput to file
142
+ workflow_output_file = self.output_dir / "aiq_workflow_output.json"
143
+ Path(workflow_output_file).write_text(json.dumps([output.model_dump() for output in filtered_outputs],
144
+ indent=2),
145
+ encoding="utf-8")
146
+ logger.info("Workflow output written to %s", workflow_output_file)
147
+
148
+ self._swe_bench_inputs = supported_inputs
149
+ self._swe_bench_outputs = filtered_outputs
150
+ return workflow_input_file, workflow_output_file
151
+
152
+ def build_eval_output(self):
153
+ """Builds the EvalOutput object from the SWEBenchOutput models and the average score."""
154
+ # WIP: Build a score based on eval run logs
155
+ for swebench_output in self._swe_bench_outputs:
156
+ yield {"id": swebench_output.instance_id, "score": "-", "reasoning": "-"}
157
+
158
+ @staticmethod
159
+ def compute_score(success_cnt: int, total_cnt: int) -> float:
160
+ if total_cnt == 0:
161
+ return 0.0
162
+ score = success_cnt / total_cnt
163
+ return min(max(score, 0.0), 1.0)
164
+
165
+ async def evaluate(self, eval_input: EvalInput) -> EvalOutput:
166
+ '''Run the swebench evaluation and store the report in the output directory'''
167
+
168
+ # Process the EvalInput
169
+ workflow_input_file, workflow_output_file = self.process_eval_input(eval_input)
170
+ if not workflow_input_file or not workflow_output_file:
171
+ # nothing to evaluate
172
+ return EvalOutput(average_score=0.0, eval_output_items=[])
173
+
174
+ report_dir = self.output_dir / "swe_bench_reports"
175
+ self.empty_report_dir(report_dir)
176
+
177
+ logger.info("Starting swe_bench run %s", self.run_id)
178
+ swebench_eval.main(dataset_name=str(workflow_input_file),
179
+ split="dev",
180
+ instance_ids=[],
181
+ predictions_path=str(workflow_output_file),
182
+ max_workers=self.max_workers,
183
+ force_rebuild=False,
184
+ cache_level="env",
185
+ clean=False,
186
+ open_file_limit=4096,
187
+ run_id=self.run_id,
188
+ timeout=1800,
189
+ namespace=None,
190
+ rewrite_reports=False,
191
+ modal=False,
192
+ instance_image_tag='latest',
193
+ report_dir=str(report_dir))
194
+ logger.info("Completed swe_bench run %s", self.run_id)
195
+
196
+ swe_bench_report_file = f"{self._model_name_or_path}.{self.run_id}.json"
197
+
198
+ # There is a bug in swebench because of which report_dir is being ignored. Copy the report to the output dir
199
+ self.move_report_and_logs(swe_bench_report_file=swe_bench_report_file, logs_dir="logs", report_dir=report_dir)
200
+ logger.info("SWE_bench report and logs written to %s directory", report_dir)
201
+
202
+ # read the swe_bench report file
203
+ report_file = report_dir / swe_bench_report_file
204
+ # if report file is not present, return empty EvalOutput
205
+ avg_score = 0.0
206
+ if report_file.exists():
207
+ with open(report_file, "r", encoding="utf-8") as f:
208
+ report = json.load(f)
209
+ resolved_instances = report.get("resolved_instances", 0)
210
+ total_instances = report.get("total_instances", 0)
211
+ avg_score = self.compute_score(resolved_instances, total_instances)
212
+
213
+ # Build the EvalOutput from self._swe_bench_outputs and avg_score
214
+ eval_output_items = list(self.build_eval_output())
215
+ return EvalOutput(average_score=avg_score, eval_output_items=eval_output_items)
@@ -0,0 +1,36 @@
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
+ from pydantic import Field
17
+
18
+ from aiq.builder.builder import EvalBuilder
19
+ from aiq.builder.evaluator import EvaluatorInfo
20
+ from aiq.cli.register_workflow import register_evaluator
21
+ from aiq.data_models.evaluator import EvaluatorBaseConfig
22
+
23
+
24
+ class SweBenchEvaluatorConfig(EvaluatorBaseConfig, name="swe_bench"):
25
+ """Code patch evaluation for SWE Bench problems."""
26
+
27
+ run_id: str = Field(description="swe-bench test harness run identifier.")
28
+
29
+
30
+ @register_evaluator(config_type=SweBenchEvaluatorConfig)
31
+ async def register_swe_bench_evaluator(config: SweBenchEvaluatorConfig, builder: EvalBuilder):
32
+
33
+ from .evaluate import SweBenchEvaluator
34
+ _evaluator = SweBenchEvaluator(config.run_id, builder.get_max_concurrency(), builder.get_output_dir())
35
+
36
+ yield EvaluatorInfo(config=config, evaluate_fn=_evaluator.evaluate, description="SWE Bench Evaluator")
File without changes
@@ -0,0 +1,118 @@
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
+ import asyncio
17
+ import logging
18
+
19
+ from langchain.evaluation import TrajectoryEvalChain
20
+ from langchain_core.language_models import BaseChatModel
21
+ from langchain_core.tools import BaseTool
22
+ from tqdm import tqdm
23
+
24
+ from aiq.eval.evaluator.evaluator_model import EvalInput
25
+ from aiq.eval.evaluator.evaluator_model import EvalInputItem
26
+ from aiq.eval.evaluator.evaluator_model import EvalOutput
27
+ from aiq.eval.evaluator.evaluator_model import EvalOutputItem
28
+ from aiq.eval.utils.tqdm_position_registry import TqdmPositionRegistry
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class TrajectoryEvaluator:
34
+
35
+ def __init__(
36
+ self,
37
+ llm: BaseChatModel,
38
+ tools: list[BaseTool] | None = None,
39
+ max_concurrency: int = 8,
40
+ ):
41
+
42
+ self.llm = llm
43
+ self.tools = tools
44
+ self.max_concurrency = max_concurrency
45
+ self.semaphore = asyncio.Semaphore(self.max_concurrency)
46
+ # Initialize trajectory evaluation chain
47
+ self.traj_eval_chain = TrajectoryEvalChain.from_llm(llm=self.llm,
48
+ tools=self.tools,
49
+ return_reasoning=True,
50
+ requires_reference=True)
51
+ logger.debug("Trajectory evaluation chain initialized.")
52
+
53
+ async def evaluate(self, eval_input: EvalInput) -> EvalOutput:
54
+ """
55
+ Evaluates the agent trajectories using trajectory evaluation chain.
56
+ """
57
+
58
+ num_records = len(eval_input.eval_input_items)
59
+ logger.info("Running trajectory evaluation with %d records", num_records)
60
+ from aiq.data_models.intermediate_step import IntermediateStepType
61
+ from aiq.eval.intermediate_step_adapter import IntermediateStepAdapter
62
+
63
+ intermediate_step_adapter = IntermediateStepAdapter()
64
+ event_filter = [IntermediateStepType.LLM_END, IntermediateStepType.TOOL_END]
65
+
66
+ async def process_item(item: EvalInputItem) -> tuple[float, dict]:
67
+ """
68
+ Evaluate a single EvalInputItem asynchronously and return a tuple of-
69
+ 1. score
70
+ 2. reasoning for the score
71
+ """
72
+ question = item.input_obj
73
+ generated_answer = item.output_obj
74
+ agent_trajectory = intermediate_step_adapter.get_agent_actions(item.trajectory, event_filter)
75
+ try:
76
+ eval_result = await self.traj_eval_chain.aevaluate_agent_trajectory(
77
+ input=question,
78
+ agent_trajectory=agent_trajectory,
79
+ prediction=generated_answer,
80
+ )
81
+ except Exception as e:
82
+ logger.exception("Error evaluating trajectory for question: %s, Error: %s", question, e, exc_info=True)
83
+ return 0.0, f"Error evaluating trajectory: {e}"
84
+
85
+ reasoning = {
86
+ "reasoning": eval_result["reasoning"],
87
+ "trajectory": [(action.model_dump(), output) for (action, output) in agent_trajectory]
88
+ }
89
+ return eval_result["score"], reasoning
90
+
91
+ async def wrapped_process(item: EvalInputItem) -> tuple[float, dict]:
92
+ async with self.semaphore:
93
+ result = await process_item(item)
94
+ pbar.update(1)
95
+ return result
96
+
97
+ # Execute all evaluations asynchronously
98
+ try:
99
+ tqdm_position = TqdmPositionRegistry.claim()
100
+ pbar = tqdm(total=len(eval_input.eval_input_items), desc="Evaluating Trajectory", position=tqdm_position)
101
+ results = await asyncio.gather(*[wrapped_process(item) for item in eval_input.eval_input_items])
102
+ finally:
103
+ pbar.close()
104
+ TqdmPositionRegistry.release(tqdm_position)
105
+
106
+ # Extract scores and reasonings
107
+ sample_scores, sample_reasonings = zip(*results) if results else ([], [])
108
+
109
+ # Compute average score
110
+ avg_score = round(sum(sample_scores) / len(sample_scores), 2) if sample_scores else 0.0
111
+
112
+ # Construct EvalOutputItems
113
+ eval_output_items = [
114
+ EvalOutputItem(id=item.id, score=score, reasoning=reasoning)
115
+ for item, score, reasoning in zip(eval_input.eval_input_items, sample_scores, sample_reasonings)
116
+ ]
117
+
118
+ return EvalOutput(average_score=avg_score, eval_output_items=eval_output_items)
@@ -0,0 +1,40 @@
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
+ from pydantic import Field
17
+
18
+ from aiq.builder.builder import EvalBuilder
19
+ from aiq.builder.evaluator import EvaluatorInfo
20
+ from aiq.cli.register_workflow import register_evaluator
21
+ from aiq.data_models.evaluator import EvaluatorBaseConfig
22
+
23
+
24
+ class TrajectoryEvaluatorConfig(EvaluatorBaseConfig, name="trajectory"):
25
+ """Agent Trajectory Evaluation."""
26
+
27
+ llm_name: str = Field(description="LLM as a judge.")
28
+
29
+
30
+ @register_evaluator(config_type=TrajectoryEvaluatorConfig)
31
+ async def register_trajectory_evaluator(config: TrajectoryEvaluatorConfig, builder: EvalBuilder):
32
+ from aiq.builder.framework_enum import LLMFrameworkEnum
33
+
34
+ from .evaluate import TrajectoryEvaluator
35
+ llm = await builder.get_llm(config.llm_name, wrapper_type=LLMFrameworkEnum.LANGCHAIN)
36
+ tools = builder.get_all_tools(wrapper_type=LLMFrameworkEnum.LANGCHAIN)
37
+
38
+ _evaluator = TrajectoryEvaluator(llm, tools, builder.get_max_concurrency())
39
+
40
+ yield EvaluatorInfo(config=config, evaluate_fn=_evaluator.evaluate, description="Trajectory Evaluator")
File without changes
@@ -0,0 +1,131 @@
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 subprocess
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ import aioboto3
24
+ from botocore.exceptions import NoCredentialsError
25
+ from tqdm import tqdm
26
+
27
+ from aiq.data_models.evaluate import EvalOutputConfig
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ class OutputUploader:
33
+ """
34
+ Run custom scripts and upload evaluation outputs using the configured s3
35
+ credentials.
36
+ """
37
+
38
+ def __init__(self, output_config: EvalOutputConfig, job_id: str | None = None):
39
+ self.output_config = output_config
40
+ self._s3_client = None
41
+ self.job_id = job_id
42
+
43
+ @property
44
+ def s3_config(self):
45
+ return self.output_config.s3
46
+
47
+ async def _upload_file(self, s3_client, bucket, s3_key, local_path, pbar):
48
+ try:
49
+ await s3_client.upload_file(str(local_path), bucket, s3_key)
50
+ logger.info("Uploaded %s to s3://%s/%s", local_path, bucket, s3_key)
51
+ pbar.update(1)
52
+ except Exception as e:
53
+ logger.error("Failed to upload %s to s3://%s/%s: %s", local_path, bucket, s3_key, e)
54
+ raise
55
+
56
+ async def upload_directory(self):
57
+ """
58
+ Upload the contents of the local output directory to the remote S3 bucket in parallel.
59
+ """
60
+ if not self.output_config.s3:
61
+ logger.info("No S3 config provided; skipping upload.")
62
+ return
63
+
64
+ local_dir = self.output_config.dir
65
+ bucket = self.s3_config.bucket
66
+ remote_prefix = self.output_config.remote_dir or ""
67
+ if self.job_id:
68
+ remote_prefix = str(Path(remote_prefix) / f"jobs/{self.job_id}")
69
+
70
+ file_entries = []
71
+ for root, _, files in os.walk(local_dir):
72
+ for file in files:
73
+ local_path = Path(root) / file
74
+ relative_path = local_path.relative_to(local_dir)
75
+ s3_path = Path(remote_prefix) / relative_path
76
+ s3_key = str(s3_path).replace("\\", "/") # Normalize for S3
77
+ file_entries.append((local_path, s3_key))
78
+
79
+ session = aioboto3.Session()
80
+ try:
81
+ async with session.client(
82
+ "s3",
83
+ endpoint_url=self.s3_config.endpoint_url,
84
+ aws_access_key_id=self.s3_config.access_key,
85
+ aws_secret_access_key=self.s3_config.secret_key,
86
+ ) as s3_client:
87
+ with tqdm(total=len(file_entries), desc="Uploading files to S3") as pbar:
88
+ upload_tasks = [
89
+ self._upload_file(s3_client, bucket, s3_key, local_path, pbar)
90
+ for local_path, s3_key in file_entries
91
+ ]
92
+ await asyncio.gather(*upload_tasks)
93
+
94
+ except NoCredentialsError as e:
95
+ logger.error("AWS credentials not available: %s", e)
96
+ raise
97
+ except Exception as e:
98
+ logger.error("Failed to upload files to S3: %s", e)
99
+ raise
100
+
101
+ def run_custom_scripts(self):
102
+ """
103
+ Run custom Python scripts defined in the EvalOutputConfig.
104
+ Each script is run with its kwargs passed as command-line arguments.
105
+ The output directory is passed as the first argument.
106
+ """
107
+ for _, script_config in self.output_config.custom_scripts.items():
108
+ script_path = script_config.script
109
+ if not script_path.exists():
110
+ logger.error("Custom script %s does not exist.", script_path)
111
+ continue
112
+
113
+ # use python interpreter
114
+ args = [sys.executable, str(script_path)]
115
+ # add output directory as first keyword argument
116
+ args.append("--output_dir")
117
+ args.append(str(self.output_config.dir))
118
+ if script_config.kwargs:
119
+ for key, value in script_config.kwargs.items():
120
+ args.append(f"--{key}")
121
+ args.append(str(value))
122
+
123
+ display_args = " ".join(f'"{arg}"' if " " in arg else arg for arg in args[1:])
124
+
125
+ try:
126
+ logger.info("Running custom script: %s %s", script_path, display_args)
127
+ subprocess.run(args, check=True, text=True)
128
+ logger.info("Custom script %s completed successfully.", script_path)
129
+ except subprocess.CalledProcessError as e:
130
+ logger.error("Custom script %s failed with return code %s", script_path, e.returncode)
131
+ raise
@@ -0,0 +1,40 @@
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
+
17
+ class TqdmPositionRegistry:
18
+ """
19
+ A simple registry for tqdm positions.
20
+ """
21
+ _positions = set()
22
+ _max_positions = 100
23
+
24
+ @classmethod
25
+ def claim(cls) -> int:
26
+ """
27
+ Claim a tqdm position in the range of 0-99.
28
+ """
29
+ for i in range(cls._max_positions):
30
+ if i not in cls._positions:
31
+ cls._positions.add(i)
32
+ return i
33
+ raise RuntimeError("No available tqdm positions.")
34
+
35
+ @classmethod
36
+ def release(cls, pos: int):
37
+ """
38
+ Release a tqdm position.
39
+ """
40
+ cls._positions.discard(pos)
@@ -0,0 +1,14 @@
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.
@@ -0,0 +1,14 @@
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.
@@ -0,0 +1,32 @@
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
+ from pathlib import Path
17
+
18
+ from pydantic import Field
19
+
20
+ from aiq.data_models.front_end import FrontEndBaseConfig
21
+
22
+
23
+ class ConsoleFrontEndConfig(FrontEndBaseConfig, name="console"):
24
+ """
25
+ A front end that allows an AgentIQ workflow to be run from the console.
26
+ """
27
+
28
+ input_query: list[str] | None = Field(default=None,
29
+ alias="input",
30
+ description="A single input to submit the the workflow.")
31
+ input_file: Path | None = Field(default=None,
32
+ description="Path to a json file of inputs to submit to the workflow.")