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
aiq/cli/__init__.py ADDED
@@ -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.
File without changes
@@ -0,0 +1,233 @@
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 logging
17
+ import tempfile
18
+ from copy import deepcopy
19
+ from pathlib import Path
20
+ from typing import Any
21
+
22
+ import click
23
+ import yaml
24
+
25
+ from aiq.utils.data_models.schema_validator import validate_yaml
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class _Placeholder:
31
+ """Placeholder class to represent a value that is not set yet."""
32
+ pass
33
+
34
+
35
+ class LayeredConfig:
36
+
37
+ def __init__(self, base_config: dict[str, Any]):
38
+ if not isinstance(base_config, dict):
39
+ raise ValueError("Base config must be a dictionary")
40
+ self.base_config = deepcopy(base_config)
41
+ self.overrides: dict[str, Any] = {}
42
+ self._effective_config: dict[str, Any] | None = None
43
+
44
+ def validate_path(self, path: str) -> None:
45
+ """Validate if a path exists in base config"""
46
+ parts = path.split('.')
47
+ current = self.base_config
48
+
49
+ for i, part in enumerate(parts):
50
+ if not isinstance(current, dict):
51
+ current_path = '.'.join(parts[:i])
52
+ raise click.BadParameter(f"Cannot navigate through non-dictionary value at '{current_path}'")
53
+ if part not in current:
54
+ if i == len(parts) - 1:
55
+ current[part] = _Placeholder()
56
+ else:
57
+ current[part] = {}
58
+
59
+ current = current[part]
60
+
61
+ def set_override(self, path: str, value: str) -> None:
62
+ """Set an override value with type conversion based on original config value.
63
+
64
+ Args:
65
+ path: Configuration path in dot notation (e.g., "llms.nim_llm.temperature")
66
+ value: String value from CLI to override with
67
+
68
+ Raises:
69
+ click.BadParameter: If path doesn't exist or type conversion fails
70
+ Exception: For other unexpected errors
71
+ """
72
+ try:
73
+ # Validate path exists in config
74
+ self.validate_path(path)
75
+
76
+ # Get original value to determine type
77
+ original_value = self.get_value(path)
78
+
79
+ # Convert string value to appropriate type
80
+ try:
81
+ if isinstance(original_value, bool):
82
+ lower_value = value.lower().strip()
83
+ if lower_value not in ['true', 'false']:
84
+ raise ValueError(f"Boolean value must be 'true' or 'false', got '{value}'")
85
+ value = lower_value == 'true'
86
+ elif isinstance(original_value, (int, float)):
87
+ value = type(original_value)(value)
88
+ elif isinstance(original_value, list):
89
+ value = [v.strip() for v in value.split(',')]
90
+ elif isinstance(original_value, Path):
91
+ value = Path(value)
92
+ except (ValueError, TypeError) as e:
93
+ raise click.BadParameter(f"Type mismatch for '{path}': expected {type(original_value).__name__}, "
94
+ f"got '{value}' ({type(value).__name__}). Error: {str(e)}")
95
+
96
+ # Store converted value
97
+ self.overrides[path] = value
98
+ self._effective_config = None
99
+
100
+ log_msg = f"Successfully set override for {path} with value: {value}"
101
+ if not isinstance(original_value, _Placeholder):
102
+ log_msg += f" with type {type(value)})"
103
+
104
+ logger.info(log_msg)
105
+
106
+ except Exception as e:
107
+ logger.error("Failed to set override for %s: %s", path, str(e))
108
+ raise
109
+
110
+ def get_value(self, path: str) -> Any:
111
+ """Get value with better error messages"""
112
+ try:
113
+ if path in self.overrides:
114
+ return self.overrides[path]
115
+
116
+ parts = path.split('.')
117
+ current = self.base_config
118
+
119
+ for i, part in enumerate(parts):
120
+ if not isinstance(current, dict):
121
+ current_path = '.'.join(parts[:i])
122
+ raise click.BadParameter(f"Cannot access '{path}': '{current_path}' is not a dictionary")
123
+ if part not in current:
124
+ raise click.BadParameter(f"Path '{path}' not found: '{part}' does not exist")
125
+ current = current[part]
126
+
127
+ return current
128
+
129
+ except Exception as e:
130
+ logger.error("Error accessing path %s: %s", path, e)
131
+ raise
132
+
133
+ def _update_config_value(self, config: dict[str, Any], path: str, value: Any) -> None:
134
+ """Update a single value in the config dictionary at the specified path.
135
+
136
+ Args:
137
+ config: The configuration dictionary to update
138
+ path: String representing the path to the value using dot notation (e.g. "llms.nim_llm.temperature")
139
+ value: The new value to set at the specified path
140
+
141
+ Example:
142
+ If config is {"llms": {"nim_llm": {"temperature": 0.5}}}
143
+ and path is "llms.nim_llm.temperature" with value 0.7,
144
+ this will update config to {"llms": {"nim_llm": {"temperature": 0.7}}}
145
+ """
146
+ parts = path.split('.')
147
+ current = config
148
+ # Navigate through nested dictionaries until reaching the parent of target
149
+ for part in parts[:-1]:
150
+ current = current[part]
151
+ # Update the value at the target location
152
+ current[parts[-1]] = value
153
+
154
+ def get_effective_config(self) -> dict[str, Any]:
155
+ """Get the configuration with all overrides applied.
156
+
157
+ Creates a new configuration dictionary by applying all stored overrides
158
+ to a deep copy of the base configuration. Caches the result to avoid
159
+ recomputing unless overrides change.
160
+
161
+ Returns:
162
+ Dict containing the full configuration with all overrides applied
163
+
164
+ Note:
165
+ The configuration is cached in self._effective_config and only
166
+ recomputed when new overrides are added via set_override()
167
+ """
168
+ # Return cached config if available
169
+ if self._effective_config is not None:
170
+ return self._effective_config
171
+
172
+ # Create deep copy to avoid modifying base config
173
+ config = deepcopy(self.base_config)
174
+
175
+ # Apply each override to the config copy
176
+ for path, value in self.overrides.items():
177
+ self._update_config_value(config, path, value)
178
+
179
+ # Return the result
180
+ self._effective_config = config
181
+ return config
182
+
183
+
184
+ def load_and_override_config(config_file: Path, overrides: tuple[tuple[str, str], ...]) -> dict[str, Any]:
185
+ """Load config file and apply any overrides"""
186
+ # First validate the original config file
187
+ validate_yaml(None, None, config_file)
188
+
189
+ # Load the base config
190
+ with open(config_file, 'r', encoding='utf-8') as f:
191
+ base_config = yaml.safe_load(f)
192
+
193
+ # Create layered config
194
+ config = LayeredConfig(base_config)
195
+
196
+ # Apply overrides if any
197
+ if overrides:
198
+ for param_path, value in overrides:
199
+ config.set_override(param_path, value)
200
+
201
+ effective_config = config.get_effective_config()
202
+
203
+ # Second validation is necessary to ensure overrides haven't created an invalid config
204
+ # For example, overrides might break required relationships between fields
205
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.yml', delete=False) as tmp:
206
+ yaml.safe_dump(effective_config, tmp)
207
+ tmp_path = Path(tmp.name)
208
+
209
+ try:
210
+ # Validate using the temporary file
211
+ validate_yaml(None, None, tmp_path)
212
+ # If validation succeeds, print the config
213
+ logger.info(
214
+ "\n\nConfiguration after overrides:\n\n%s",
215
+ yaml.dump(effective_config, default_flow_style=False),
216
+ )
217
+ except Exception as e:
218
+ logger.exception("Modified configuration failed validation: %s", e, exc_info=True)
219
+ raise click.BadParameter(f"Modified configuration failed validation: {str(e)}")
220
+ finally:
221
+ # Clean up the temporary file
222
+ tmp_path.unlink()
223
+
224
+ return config.get_effective_config()
225
+
226
+
227
+ def add_override_option(command):
228
+ """Decorator to add override option to a command"""
229
+ return click.option(
230
+ '--override',
231
+ type=(str, str),
232
+ multiple=True,
233
+ help="Override config values using dot notation (e.g., --override llms.nim_llm.temperature 0.7)")(command)
@@ -0,0 +1,37 @@
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 pathlib import Path
17
+
18
+ import click
19
+ import yaml
20
+
21
+ from aiq.data_models.config import AIQConfig
22
+
23
+
24
+ def validate_config(config_file: Path) -> AIQConfig:
25
+ """Validate configuration file and return parsed config"""
26
+ try:
27
+ from aiq.runtime.loader import load_config
28
+
29
+ # Load using the AgentIQ loader functions. This performs validation
30
+ config = load_config(config_file)
31
+
32
+ return config
33
+
34
+ except yaml.YAMLError as e:
35
+ raise click.ClickException(f"Invalid YAML format: {str(e)}")
36
+ except Exception as e:
37
+ raise click.ClickException(f"Validation error: {str(e)}")
File without changes
File without changes
File without changes
@@ -0,0 +1,28 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+
18
+ import click
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ @click.group(name=__name__, invoke_without_command=True, help="Utility to add an AgentIQ remote registry channel.")
24
+ @click.argument("channel_type", type=str)
25
+ def add(channel_type: str) -> None:
26
+ from aiq.utils.settings.global_settings import add_channel_interative
27
+
28
+ add_channel_interative(channel_type=channel_type)
@@ -0,0 +1,34 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+
18
+ import click
19
+
20
+ from aiq.cli.commands.configure.channel.add import add
21
+ from aiq.cli.commands.configure.channel.remove import remove
22
+ from aiq.cli.commands.configure.channel.update import update
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ @click.group(name=__name__, invoke_without_command=False, help="Utility to configure AgentIQ remote registry channels.")
28
+ def channel(**kwargs):
29
+ pass
30
+
31
+
32
+ channel.add_command(add, "add")
33
+ channel.add_command(remove, "remove")
34
+ channel.add_command(update, "update")
@@ -0,0 +1,30 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+
18
+ import click
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ @click.group(name=__name__,
24
+ invoke_without_command=True,
25
+ help="Utility to remove a configured AgentIQ remote registry channel.")
26
+ @click.argument("channel", type=str)
27
+ def remove(channel: str):
28
+ from aiq.utils.settings.global_settings import remove_channel_interactive
29
+
30
+ remove_channel_interactive(channel_name=channel)
@@ -0,0 +1,30 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+
18
+ import click
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ @click.group(name="update",
24
+ invoke_without_command=True,
25
+ help="Utility to update an AgentIQ remote registry channel's settings.")
26
+ @click.argument("channel", type=str)
27
+ def update(channel):
28
+ from aiq.utils.settings.global_settings import update_channel_interactive
29
+
30
+ update_channel_interactive(channel_name=channel)
@@ -0,0 +1,33 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+
18
+ import click
19
+
20
+ from aiq.cli.commands.configure.channel.channel import channel
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ @click.group(name=__name__, invoke_without_command=False, help="Configure AgentIQ developer preferences.")
26
+ def configure_command(**kwargs):
27
+ """
28
+ Publish AgentIQ artifacts with the specified configuration
29
+ """
30
+ pass
31
+
32
+
33
+ configure_command.add_command(channel, name="channel")
@@ -0,0 +1,139 @@
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
+ from pathlib import Path
19
+
20
+ import click
21
+
22
+ from aiq.eval.evaluate import EvaluationRun
23
+ from aiq.eval.evaluate import EvaluationRunConfig
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ @click.group(name=__name__, invoke_without_command=True, help="Evaluate a workflow with the specified dataset.")
29
+ @click.option(
30
+ "--config_file",
31
+ type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path),
32
+ required=True,
33
+ help="A JSON/YAML file that sets the parameters for the workflow and evaluation.",
34
+ )
35
+ @click.option(
36
+ "--dataset",
37
+ type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path),
38
+ required=False,
39
+ help="A json file with questions and ground truth answers. This will override the dataset path in the config file.",
40
+ )
41
+ @click.option(
42
+ "--result_json_path",
43
+ type=str,
44
+ default="$",
45
+ help=("A JSON path to extract the result from the workflow. Use this when the workflow returns "
46
+ "multiple objects or a dictionary. For example, '$.output' will extract the 'output' field "
47
+ "from the result."),
48
+ )
49
+ @click.option(
50
+ "--skip_workflow",
51
+ is_flag=True,
52
+ default=False,
53
+ help="Skip the workflow execution and use the provided dataset for evaluation. "
54
+ "In this case the dataset should have the 'generated_' columns.",
55
+ )
56
+ @click.option(
57
+ "--skip_completed_entries",
58
+ is_flag=True,
59
+ default=False,
60
+ help="Skip the dataset entries that have a generated answer.",
61
+ )
62
+ @click.option(
63
+ "--endpoint",
64
+ type=str,
65
+ default=None,
66
+ help="Use endpoint for running the workflow. Example: http://localhost:8000/generate",
67
+ )
68
+ @click.option(
69
+ "--endpoint_timeout",
70
+ type=int,
71
+ default=300,
72
+ help="HTTP response timeout in seconds. Only relevant if endpoint is specified.",
73
+ )
74
+ @click.option(
75
+ "--reps",
76
+ type=int,
77
+ default=1,
78
+ help="Number of repetitions for the evaluation.",
79
+ )
80
+ @click.option(
81
+ "--override",
82
+ type=(str, str),
83
+ multiple=True,
84
+ help="Override config values using dot notation (e.g., --override llms.nim_llm.temperature 0.7)",
85
+ )
86
+ @click.pass_context
87
+ def eval_command(ctx, **kwargs) -> None:
88
+ """ Evaluate datasets with the specified mechanism"""
89
+ pass
90
+
91
+
92
+ async def run_and_evaluate(config: EvaluationRunConfig):
93
+ # Run evaluation
94
+ eval_runner = EvaluationRun(config=config)
95
+ await eval_runner.run_and_evaluate()
96
+
97
+
98
+ @eval_command.result_callback(replace=True)
99
+ def process_aiq_eval(
100
+ processors, # pylint: disable=unused-argument
101
+ *,
102
+ config_file: Path,
103
+ dataset: Path,
104
+ result_json_path: str,
105
+ skip_workflow: bool,
106
+ skip_completed_entries: bool,
107
+ endpoint: str,
108
+ endpoint_timeout: int,
109
+ reps: int,
110
+ override: tuple[tuple[str, str], ...],
111
+ ):
112
+ """
113
+ Process the eval command and execute the evaluation. Here the config_file, if provided, is checked for its existence
114
+ on disk.
115
+ """
116
+ # Cannot skip_workflow if endpoint is specified
117
+ if skip_workflow and endpoint:
118
+ raise click.UsageError("The options '--skip_workflow' and '--endpoint' are mutually exclusive. "
119
+ "Please use only one of them.")
120
+
121
+ # You cannot run multiple repetitions if you are skipping the workflow or skipping completed entries
122
+ if reps > 1 and (skip_workflow or skip_completed_entries):
123
+ raise click.UsageError("The options '--reps' and '--skip_workflow' or '--skip_completed_entries' are mutually "
124
+ "exclusive. You cannot run multiple repetitions if you are skipping the workflow or "
125
+ "have a partially completed dataset.")
126
+
127
+ # Create the configuration object
128
+ config = EvaluationRunConfig(
129
+ config_file=config_file,
130
+ dataset=str(dataset) if dataset else None,
131
+ result_json_path=result_json_path,
132
+ skip_workflow=skip_workflow,
133
+ skip_completed_entries=skip_completed_entries,
134
+ endpoint=endpoint,
135
+ endpoint_timeout=endpoint_timeout,
136
+ reps=reps,
137
+ override=override,
138
+ )
139
+ asyncio.run(run_and_evaluate(config))
@@ -0,0 +1,14 @@
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.
@@ -0,0 +1,37 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+
18
+ import click
19
+
20
+ from aiq.cli.commands.info.list_channels import list_channels
21
+ from aiq.cli.commands.info.list_components import list_components
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ @click.group(name=__name__,
27
+ invoke_without_command=False,
28
+ help="Provide information about the local AgentIQ environment.")
29
+ def info_command(**kwargs):
30
+ """
31
+ Provide information about the local AgentIQ environment.
32
+ """
33
+ pass
34
+
35
+
36
+ info_command.add_command(list_components, name="components")
37
+ info_command.add_command(list_channels, "channels")
@@ -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
+ import logging
17
+
18
+ import click
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ @click.group(name=__name__, invoke_without_command=True, help="List the configured remote registry channels.")
24
+ @click.option("-t", "--type", "channel_type", type=str, required=False, help=("Filter the results by channel type."))
25
+ def list_channels(channel_type: str):
26
+ from aiq.settings.global_settings import GlobalSettings
27
+
28
+ settings = GlobalSettings().get()
29
+ try:
30
+ settings.print_channel_settings(channel_type=channel_type)
31
+ except Exception as e:
32
+ logger.exception("Error listing channels: %s", e, exc_info=True)