nvidia-nat 1.3.0.dev2__py3-none-any.whl → 1.3.0rc1__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.
- aiq/__init__.py +2 -2
- nat/agent/base.py +24 -15
- nat/agent/dual_node.py +9 -4
- nat/agent/prompt_optimizer/prompt.py +68 -0
- nat/agent/prompt_optimizer/register.py +149 -0
- nat/agent/react_agent/agent.py +79 -47
- nat/agent/react_agent/register.py +41 -21
- nat/agent/reasoning_agent/reasoning_agent.py +11 -9
- nat/agent/register.py +1 -1
- nat/agent/rewoo_agent/agent.py +326 -148
- nat/agent/rewoo_agent/prompt.py +19 -22
- nat/agent/rewoo_agent/register.py +46 -26
- nat/agent/tool_calling_agent/agent.py +84 -28
- nat/agent/tool_calling_agent/register.py +51 -28
- nat/authentication/api_key/api_key_auth_provider.py +2 -2
- nat/authentication/credential_validator/bearer_token_validator.py +557 -0
- nat/authentication/http_basic_auth/http_basic_auth_provider.py +1 -1
- nat/authentication/interfaces.py +5 -2
- nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +40 -20
- nat/authentication/oauth2/oauth2_resource_server_config.py +124 -0
- nat/authentication/register.py +0 -1
- nat/builder/builder.py +56 -24
- nat/builder/component_utils.py +9 -5
- nat/builder/context.py +46 -11
- nat/builder/eval_builder.py +16 -11
- nat/builder/framework_enum.py +1 -0
- nat/builder/front_end.py +1 -1
- nat/builder/function.py +378 -8
- nat/builder/function_base.py +3 -3
- nat/builder/function_info.py +6 -8
- nat/builder/user_interaction_manager.py +2 -2
- nat/builder/workflow.py +13 -1
- nat/builder/workflow_builder.py +281 -76
- nat/cli/cli_utils/config_override.py +2 -2
- nat/cli/commands/evaluate.py +1 -1
- nat/cli/commands/info/info.py +16 -6
- nat/cli/commands/info/list_channels.py +1 -1
- nat/cli/commands/info/list_components.py +7 -8
- nat/cli/commands/mcp/__init__.py +14 -0
- nat/cli/commands/mcp/mcp.py +986 -0
- nat/cli/commands/object_store/__init__.py +14 -0
- nat/cli/commands/object_store/object_store.py +227 -0
- nat/cli/commands/optimize.py +90 -0
- nat/cli/commands/registry/publish.py +2 -2
- nat/cli/commands/registry/pull.py +2 -2
- nat/cli/commands/registry/remove.py +2 -2
- nat/cli/commands/registry/search.py +15 -17
- nat/cli/commands/start.py +16 -5
- nat/cli/commands/uninstall.py +1 -1
- nat/cli/commands/workflow/templates/config.yml.j2 +0 -1
- nat/cli/commands/workflow/templates/pyproject.toml.j2 +4 -1
- nat/cli/commands/workflow/templates/register.py.j2 +0 -1
- nat/cli/commands/workflow/workflow_commands.py +9 -13
- nat/cli/entrypoint.py +8 -10
- nat/cli/register_workflow.py +38 -4
- nat/cli/type_registry.py +75 -6
- nat/control_flow/__init__.py +0 -0
- nat/control_flow/register.py +20 -0
- nat/control_flow/router_agent/__init__.py +0 -0
- nat/control_flow/router_agent/agent.py +329 -0
- nat/control_flow/router_agent/prompt.py +48 -0
- nat/control_flow/router_agent/register.py +91 -0
- nat/control_flow/sequential_executor.py +166 -0
- nat/data_models/agent.py +34 -0
- nat/data_models/api_server.py +10 -10
- nat/data_models/authentication.py +23 -9
- nat/data_models/common.py +1 -1
- nat/data_models/component.py +2 -0
- nat/data_models/component_ref.py +11 -0
- nat/data_models/config.py +41 -17
- nat/data_models/dataset_handler.py +1 -1
- nat/data_models/discovery_metadata.py +4 -4
- nat/data_models/evaluate.py +4 -1
- nat/data_models/function.py +34 -0
- nat/data_models/function_dependencies.py +14 -6
- nat/data_models/gated_field_mixin.py +242 -0
- nat/data_models/intermediate_step.py +3 -3
- nat/data_models/optimizable.py +119 -0
- nat/data_models/optimizer.py +149 -0
- nat/data_models/swe_bench_model.py +1 -1
- nat/data_models/temperature_mixin.py +44 -0
- nat/data_models/thinking_mixin.py +86 -0
- nat/data_models/top_p_mixin.py +44 -0
- nat/embedder/nim_embedder.py +1 -1
- nat/embedder/openai_embedder.py +1 -1
- nat/embedder/register.py +0 -1
- nat/eval/config.py +3 -1
- nat/eval/dataset_handler/dataset_handler.py +71 -7
- nat/eval/evaluate.py +86 -31
- nat/eval/evaluator/base_evaluator.py +1 -1
- nat/eval/evaluator/evaluator_model.py +13 -0
- nat/eval/intermediate_step_adapter.py +1 -1
- nat/eval/rag_evaluator/evaluate.py +2 -2
- nat/eval/rag_evaluator/register.py +3 -3
- nat/eval/register.py +4 -1
- nat/eval/remote_workflow.py +3 -3
- nat/eval/runtime_evaluator/__init__.py +14 -0
- nat/eval/runtime_evaluator/evaluate.py +123 -0
- nat/eval/runtime_evaluator/register.py +100 -0
- nat/eval/swe_bench_evaluator/evaluate.py +6 -6
- nat/eval/trajectory_evaluator/evaluate.py +1 -1
- nat/eval/trajectory_evaluator/register.py +1 -1
- nat/eval/tunable_rag_evaluator/evaluate.py +4 -7
- nat/eval/utils/eval_trace_ctx.py +89 -0
- nat/eval/utils/weave_eval.py +18 -9
- nat/experimental/decorators/experimental_warning_decorator.py +27 -7
- nat/experimental/test_time_compute/functions/plan_select_execute_function.py +7 -3
- nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +3 -3
- nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +1 -1
- nat/experimental/test_time_compute/models/strategy_base.py +5 -4
- nat/experimental/test_time_compute/register.py +0 -1
- nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +1 -3
- nat/front_ends/console/authentication_flow_handler.py +82 -30
- nat/front_ends/console/console_front_end_plugin.py +8 -5
- nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +52 -17
- nat/front_ends/fastapi/dask_client_mixin.py +65 -0
- nat/front_ends/fastapi/fastapi_front_end_config.py +36 -5
- nat/front_ends/fastapi/fastapi_front_end_controller.py +4 -4
- nat/front_ends/fastapi/fastapi_front_end_plugin.py +135 -4
- nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +481 -281
- nat/front_ends/fastapi/job_store.py +518 -99
- nat/front_ends/fastapi/main.py +11 -19
- nat/front_ends/fastapi/message_handler.py +13 -14
- nat/front_ends/fastapi/message_validator.py +17 -19
- nat/front_ends/fastapi/response_helpers.py +4 -4
- nat/front_ends/fastapi/step_adaptor.py +2 -2
- nat/front_ends/fastapi/utils.py +57 -0
- nat/front_ends/mcp/introspection_token_verifier.py +73 -0
- nat/front_ends/mcp/mcp_front_end_config.py +10 -1
- nat/front_ends/mcp/mcp_front_end_plugin.py +45 -13
- nat/front_ends/mcp/mcp_front_end_plugin_worker.py +116 -8
- nat/front_ends/mcp/tool_converter.py +44 -14
- nat/front_ends/register.py +0 -1
- nat/front_ends/simple_base/simple_front_end_plugin_base.py +3 -1
- nat/llm/aws_bedrock_llm.py +24 -12
- nat/llm/azure_openai_llm.py +13 -6
- nat/llm/litellm_llm.py +69 -0
- nat/llm/nim_llm.py +20 -8
- nat/llm/openai_llm.py +14 -6
- nat/llm/register.py +4 -1
- nat/llm/utils/env_config_value.py +2 -3
- nat/llm/utils/thinking.py +215 -0
- nat/meta/pypi.md +9 -9
- nat/object_store/register.py +0 -1
- nat/observability/exporter/base_exporter.py +3 -3
- nat/observability/exporter/file_exporter.py +1 -1
- nat/observability/exporter/processing_exporter.py +309 -81
- nat/observability/exporter/span_exporter.py +1 -1
- nat/observability/exporter_manager.py +7 -7
- nat/observability/mixin/file_mixin.py +7 -7
- nat/observability/mixin/redaction_config_mixin.py +42 -0
- nat/observability/mixin/tagging_config_mixin.py +62 -0
- nat/observability/mixin/type_introspection_mixin.py +420 -107
- nat/observability/processor/batching_processor.py +5 -7
- nat/observability/processor/falsy_batch_filter_processor.py +55 -0
- nat/observability/processor/processor.py +3 -0
- nat/observability/processor/processor_factory.py +70 -0
- nat/observability/processor/redaction/__init__.py +24 -0
- nat/observability/processor/redaction/contextual_redaction_processor.py +125 -0
- nat/observability/processor/redaction/contextual_span_redaction_processor.py +66 -0
- nat/observability/processor/redaction/redaction_processor.py +177 -0
- nat/observability/processor/redaction/span_header_redaction_processor.py +92 -0
- nat/observability/processor/span_tagging_processor.py +68 -0
- nat/observability/register.py +6 -4
- nat/profiler/calc/calc_runner.py +3 -4
- nat/profiler/callbacks/agno_callback_handler.py +1 -1
- nat/profiler/callbacks/langchain_callback_handler.py +6 -6
- nat/profiler/callbacks/llama_index_callback_handler.py +3 -3
- nat/profiler/callbacks/semantic_kernel_callback_handler.py +3 -3
- nat/profiler/data_frame_row.py +1 -1
- nat/profiler/decorators/framework_wrapper.py +62 -13
- nat/profiler/decorators/function_tracking.py +160 -3
- nat/profiler/forecasting/models/forecasting_base_model.py +3 -1
- nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +1 -1
- nat/profiler/inference_optimization/data_models.py +3 -3
- nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +7 -8
- nat/profiler/inference_optimization/token_uniqueness.py +1 -1
- nat/profiler/parameter_optimization/__init__.py +0 -0
- nat/profiler/parameter_optimization/optimizable_utils.py +93 -0
- nat/profiler/parameter_optimization/optimizer_runtime.py +67 -0
- nat/profiler/parameter_optimization/parameter_optimizer.py +153 -0
- nat/profiler/parameter_optimization/parameter_selection.py +107 -0
- nat/profiler/parameter_optimization/pareto_visualizer.py +380 -0
- nat/profiler/parameter_optimization/prompt_optimizer.py +384 -0
- nat/profiler/parameter_optimization/update_helpers.py +66 -0
- nat/profiler/profile_runner.py +14 -9
- nat/profiler/utils.py +4 -2
- nat/registry_handlers/local/local_handler.py +2 -2
- nat/registry_handlers/package_utils.py +1 -2
- nat/registry_handlers/pypi/pypi_handler.py +23 -26
- nat/registry_handlers/register.py +3 -4
- nat/registry_handlers/rest/rest_handler.py +12 -13
- nat/retriever/milvus/retriever.py +2 -2
- nat/retriever/nemo_retriever/retriever.py +1 -1
- nat/retriever/register.py +0 -1
- nat/runtime/loader.py +2 -2
- nat/runtime/runner.py +3 -2
- nat/runtime/session.py +43 -8
- nat/settings/global_settings.py +16 -5
- nat/tool/chat_completion.py +5 -2
- nat/tool/code_execution/local_sandbox/local_sandbox_server.py +3 -3
- nat/tool/datetime_tools.py +49 -9
- nat/tool/document_search.py +2 -2
- nat/tool/github_tools.py +450 -0
- nat/tool/nvidia_rag.py +1 -1
- nat/tool/register.py +2 -9
- nat/tool/retriever.py +3 -2
- nat/utils/callable_utils.py +70 -0
- nat/utils/data_models/schema_validator.py +3 -3
- nat/utils/exception_handlers/automatic_retries.py +104 -51
- nat/utils/exception_handlers/schemas.py +1 -1
- nat/utils/io/yaml_tools.py +2 -2
- nat/utils/log_levels.py +25 -0
- nat/utils/reactive/base/observable_base.py +2 -2
- nat/utils/reactive/base/observer_base.py +1 -1
- nat/utils/reactive/observable.py +2 -2
- nat/utils/reactive/observer.py +4 -4
- nat/utils/reactive/subscription.py +1 -1
- nat/utils/settings/global_settings.py +6 -8
- nat/utils/type_converter.py +4 -3
- nat/utils/type_utils.py +9 -5
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/METADATA +42 -16
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/RECORD +230 -189
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/entry_points.txt +1 -0
- nat/cli/commands/info/list_mcp.py +0 -304
- nat/tool/github_tools/create_github_commit.py +0 -133
- nat/tool/github_tools/create_github_issue.py +0 -87
- nat/tool/github_tools/create_github_pr.py +0 -106
- nat/tool/github_tools/get_github_file.py +0 -106
- nat/tool/github_tools/get_github_issue.py +0 -166
- nat/tool/github_tools/get_github_pr.py +0 -256
- nat/tool/github_tools/update_github_issue.py +0 -100
- nat/tool/mcp/exceptions.py +0 -142
- nat/tool/mcp/mcp_client.py +0 -255
- nat/tool/mcp/mcp_tool.py +0 -96
- nat/utils/exception_handlers/mcp.py +0 -211
- /nat/{tool/github_tools → agent/prompt_optimizer}/__init__.py +0 -0
- /nat/{tool/mcp → authentication/credential_validator}/__init__.py +0 -0
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/WHEEL +0 -0
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/licenses/LICENSE.md +0 -0
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -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,227 @@
|
|
|
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 importlib
|
|
18
|
+
import logging
|
|
19
|
+
import mimetypes
|
|
20
|
+
import time
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
import click
|
|
24
|
+
|
|
25
|
+
from nat.builder.workflow_builder import WorkflowBuilder
|
|
26
|
+
from nat.data_models.object_store import ObjectStoreBaseConfig
|
|
27
|
+
from nat.object_store.interfaces import ObjectStore
|
|
28
|
+
from nat.object_store.models import ObjectStoreItem
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
STORE_CONFIGS = {
|
|
33
|
+
"s3": {
|
|
34
|
+
"module": "nat.plugins.s3.object_store", "config_class": "S3ObjectStoreClientConfig"
|
|
35
|
+
},
|
|
36
|
+
"mysql": {
|
|
37
|
+
"module": "nat.plugins.mysql.object_store", "config_class": "MySQLObjectStoreClientConfig"
|
|
38
|
+
},
|
|
39
|
+
"redis": {
|
|
40
|
+
"module": "nat.plugins.redis.object_store", "config_class": "RedisObjectStoreClientConfig"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_object_store_config(**kwargs) -> ObjectStoreBaseConfig:
|
|
46
|
+
"""Process common object store arguments and return the config class"""
|
|
47
|
+
store_type = kwargs.pop("store_type")
|
|
48
|
+
config = STORE_CONFIGS[store_type]
|
|
49
|
+
module = importlib.import_module(config["module"])
|
|
50
|
+
config_class = getattr(module, config["config_class"])
|
|
51
|
+
return config_class(**kwargs)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def upload_file(object_store: ObjectStore, file_path: Path, key: str):
|
|
55
|
+
"""
|
|
56
|
+
Upload a single file to object store.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
object_store: The object store instance to use.
|
|
60
|
+
file_path: The path to the file to upload.
|
|
61
|
+
key: The key to upload the file to.
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
data = await asyncio.to_thread(file_path.read_bytes)
|
|
65
|
+
|
|
66
|
+
item = ObjectStoreItem(data=data,
|
|
67
|
+
content_type=mimetypes.guess_type(str(file_path))[0],
|
|
68
|
+
metadata={
|
|
69
|
+
"original_filename": file_path.name,
|
|
70
|
+
"file_size": str(len(data)),
|
|
71
|
+
"file_extension": file_path.suffix,
|
|
72
|
+
"upload_timestamp": str(int(time.time()))
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
# Upload using upsert to allow overwriting
|
|
76
|
+
await object_store.upsert_object(key, item)
|
|
77
|
+
click.echo(f"✅ Uploaded: {file_path.name} -> {key}")
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
raise RuntimeError(f"Failed to upload {file_path.name}:\n{e}") from e
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def object_store_command_decorator(async_func):
|
|
84
|
+
"""
|
|
85
|
+
Decorator that handles the common object store command pattern.
|
|
86
|
+
|
|
87
|
+
The decorated function should take (store: ObjectStore, kwargs) as parameters
|
|
88
|
+
and return an exit code (0 for success).
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
@click.pass_context
|
|
92
|
+
def wrapper(ctx: click.Context, **kwargs):
|
|
93
|
+
config = ctx.obj["store_config"]
|
|
94
|
+
|
|
95
|
+
async def work():
|
|
96
|
+
async with WorkflowBuilder() as builder:
|
|
97
|
+
await builder.add_object_store(name="store", config=config)
|
|
98
|
+
store = await builder.get_object_store_client("store")
|
|
99
|
+
return await async_func(store, **kwargs)
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
exit_code = asyncio.run(work())
|
|
103
|
+
except Exception as e:
|
|
104
|
+
raise click.ClickException(f"Command failed: {e}") from e
|
|
105
|
+
if exit_code != 0:
|
|
106
|
+
raise click.ClickException(f"Command failed with exit code {exit_code}")
|
|
107
|
+
return exit_code
|
|
108
|
+
|
|
109
|
+
return wrapper
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@click.command(name="upload", help="Upload a directory to an object store.")
|
|
113
|
+
@click.argument("local_dir",
|
|
114
|
+
type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path),
|
|
115
|
+
required=True)
|
|
116
|
+
@click.help_option("--help", "-h")
|
|
117
|
+
@object_store_command_decorator
|
|
118
|
+
async def upload_command(store: ObjectStore, local_dir: Path, **_kwargs):
|
|
119
|
+
"""
|
|
120
|
+
Upload a directory to an object store.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
local_dir: The local directory to upload.
|
|
124
|
+
store: The object store to use.
|
|
125
|
+
_kwargs: Additional keyword arguments.
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
click.echo(f"📁 Processing directory: {local_dir}")
|
|
129
|
+
file_count = 0
|
|
130
|
+
|
|
131
|
+
# Process each file recursively
|
|
132
|
+
for file_path in local_dir.rglob("*"):
|
|
133
|
+
if file_path.is_file():
|
|
134
|
+
key = file_path.relative_to(local_dir).as_posix()
|
|
135
|
+
await upload_file(store, file_path, key)
|
|
136
|
+
file_count += 1
|
|
137
|
+
|
|
138
|
+
click.echo(f"✅ Directory uploaded successfully! {file_count} files uploaded.")
|
|
139
|
+
return 0
|
|
140
|
+
|
|
141
|
+
except Exception as e:
|
|
142
|
+
raise click.ClickException(f"❌ Failed to upload directory {local_dir}:\n {e}") from e
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@click.command(name="delete", help="Delete files from an object store.")
|
|
146
|
+
@click.argument("keys", type=str, required=True, nargs=-1)
|
|
147
|
+
@click.help_option("--help", "-h")
|
|
148
|
+
@object_store_command_decorator
|
|
149
|
+
async def delete_command(store: ObjectStore, keys: list[str], **_kwargs):
|
|
150
|
+
"""
|
|
151
|
+
Delete files from an object store.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
store: The object store to use.
|
|
155
|
+
keys: The keys to delete.
|
|
156
|
+
_kwargs: Additional keyword arguments.
|
|
157
|
+
"""
|
|
158
|
+
deleted_count = 0
|
|
159
|
+
failed_count = 0
|
|
160
|
+
for key in keys:
|
|
161
|
+
try:
|
|
162
|
+
await store.delete_object(key)
|
|
163
|
+
click.echo(f"✅ Deleted: {key}")
|
|
164
|
+
deleted_count += 1
|
|
165
|
+
except Exception as e:
|
|
166
|
+
click.echo(f"❌ Failed to delete {key}: {e}")
|
|
167
|
+
failed_count += 1
|
|
168
|
+
|
|
169
|
+
click.echo(f"✅ Deletion completed! {deleted_count} keys deleted. {failed_count} keys failed to delete.")
|
|
170
|
+
return 0 if failed_count == 0 else 1
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@click.group(name="object-store", invoke_without_command=False, help="Manage object store operations.")
|
|
174
|
+
def object_store_command(**_kwargs):
|
|
175
|
+
"""Manage object store operations including uploading files and directories."""
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def register_object_store_commands():
|
|
180
|
+
|
|
181
|
+
@click.group(name="s3", invoke_without_command=False, help="S3 object store operations.")
|
|
182
|
+
@click.argument("bucket_name", type=str, required=True)
|
|
183
|
+
@click.option("--endpoint-url", type=str, help="S3 endpoint URL")
|
|
184
|
+
@click.option("--access-key", type=str, help="S3 access key")
|
|
185
|
+
@click.option("--secret-key", type=str, help="S3 secret key")
|
|
186
|
+
@click.option("--region", type=str, help="S3 region")
|
|
187
|
+
@click.pass_context
|
|
188
|
+
def s3(ctx: click.Context, **kwargs):
|
|
189
|
+
ctx.ensure_object(dict)
|
|
190
|
+
ctx.obj["store_config"] = get_object_store_config(store_type="s3", **kwargs)
|
|
191
|
+
|
|
192
|
+
@click.group(name="mysql", invoke_without_command=False, help="MySQL object store operations.")
|
|
193
|
+
@click.argument("bucket_name", type=str, required=True)
|
|
194
|
+
@click.option("--host", type=str, help="MySQL host")
|
|
195
|
+
@click.option("--port", type=int, help="MySQL port")
|
|
196
|
+
@click.option("--db", type=str, help="MySQL database name")
|
|
197
|
+
@click.option("--username", type=str, help="MySQL username")
|
|
198
|
+
@click.option("--password", type=str, help="MySQL password")
|
|
199
|
+
@click.pass_context
|
|
200
|
+
def mysql(ctx: click.Context, **kwargs):
|
|
201
|
+
ctx.ensure_object(dict)
|
|
202
|
+
ctx.obj["store_config"] = get_object_store_config(store_type="mysql", **kwargs)
|
|
203
|
+
|
|
204
|
+
@click.group(name="redis", invoke_without_command=False, help="Redis object store operations.")
|
|
205
|
+
@click.argument("bucket_name", type=str, required=True)
|
|
206
|
+
@click.option("--host", type=str, help="Redis host")
|
|
207
|
+
@click.option("--port", type=int, help="Redis port")
|
|
208
|
+
@click.option("--db", type=int, help="Redis db")
|
|
209
|
+
@click.pass_context
|
|
210
|
+
def redis(ctx: click.Context, **kwargs):
|
|
211
|
+
ctx.ensure_object(dict)
|
|
212
|
+
ctx.obj["store_config"] = get_object_store_config(store_type="redis", **kwargs)
|
|
213
|
+
|
|
214
|
+
commands = {"s3": s3, "mysql": mysql, "redis": redis}
|
|
215
|
+
|
|
216
|
+
for store_type, config in STORE_CONFIGS.items():
|
|
217
|
+
try:
|
|
218
|
+
importlib.import_module(config["module"])
|
|
219
|
+
command = commands[store_type]
|
|
220
|
+
object_store_command.add_command(command, name=store_type)
|
|
221
|
+
command.add_command(upload_command, name="upload")
|
|
222
|
+
command.add_command(delete_command, name="delete")
|
|
223
|
+
except ImportError:
|
|
224
|
+
pass
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
register_object_store_commands()
|
|
@@ -0,0 +1,90 @@
|
|
|
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 nat.data_models.optimizer import OptimizerRunConfig
|
|
23
|
+
from nat.profiler.parameter_optimization.optimizer_runtime import optimize_config
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@click.group(name=__name__, invoke_without_command=True, help="Optimize 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
|
+
"--endpoint",
|
|
51
|
+
type=str,
|
|
52
|
+
default=None,
|
|
53
|
+
help="Use endpoint for running the workflow. Example: http://localhost:8000/generate",
|
|
54
|
+
)
|
|
55
|
+
@click.option(
|
|
56
|
+
"--endpoint_timeout",
|
|
57
|
+
type=int,
|
|
58
|
+
default=300,
|
|
59
|
+
help="HTTP response timeout in seconds. Only relevant if endpoint is specified.",
|
|
60
|
+
)
|
|
61
|
+
@click.pass_context
|
|
62
|
+
def optimizer_command(ctx, **kwargs) -> None:
|
|
63
|
+
""" Optimize workflow with the specified dataset"""
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async def run_optimizer(config: OptimizerRunConfig):
|
|
68
|
+
await optimize_config(config)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@optimizer_command.result_callback(replace=True)
|
|
72
|
+
def run_optimizer_callback(
|
|
73
|
+
processors, # pylint: disable=unused-argument
|
|
74
|
+
*,
|
|
75
|
+
config_file: Path,
|
|
76
|
+
dataset: Path,
|
|
77
|
+
result_json_path: str,
|
|
78
|
+
endpoint: str,
|
|
79
|
+
endpoint_timeout: int,
|
|
80
|
+
):
|
|
81
|
+
"""Run the optimizer with the provided config file and dataset."""
|
|
82
|
+
config = OptimizerRunConfig(
|
|
83
|
+
config_file=config_file,
|
|
84
|
+
dataset=dataset,
|
|
85
|
+
result_json_path=result_json_path,
|
|
86
|
+
endpoint=endpoint,
|
|
87
|
+
endpoint_timeout=endpoint_timeout,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
asyncio.run(run_optimizer(config))
|
|
@@ -40,7 +40,7 @@ async def publish_artifact(registry_handler_config: RegistryHandlerBaseConfig, p
|
|
|
40
40
|
try:
|
|
41
41
|
artifact = build_artifact(package_root=package_root)
|
|
42
42
|
except Exception as e:
|
|
43
|
-
logger.exception("Error building artifact: %s", e
|
|
43
|
+
logger.exception("Error building artifact: %s", e)
|
|
44
44
|
return
|
|
45
45
|
await stack.enter_async_context(registry_handler.publish(artifact=artifact))
|
|
46
46
|
|
|
@@ -82,7 +82,7 @@ def publish(channel: str, config_file: str, package_root: str) -> None:
|
|
|
82
82
|
logger.error("Publish channel '%s' has not been configured.", channel)
|
|
83
83
|
return
|
|
84
84
|
except Exception as e:
|
|
85
|
-
logger.exception("Error loading user settings: %s", e
|
|
85
|
+
logger.exception("Error loading user settings: %s", e)
|
|
86
86
|
return
|
|
87
87
|
|
|
88
88
|
asyncio.run(publish_artifact(registry_handler_config=publish_channel_config, package_root=package_root))
|
|
@@ -66,7 +66,7 @@ async def pull_artifact(registry_handler_config: RegistryHandlerBaseConfig, pack
|
|
|
66
66
|
validated_packages = PullRequestPackages(packages=package_list)
|
|
67
67
|
|
|
68
68
|
except Exception as e:
|
|
69
|
-
logger.exception("Error processing package names: %s", e
|
|
69
|
+
logger.exception("Error processing package names: %s", e)
|
|
70
70
|
return
|
|
71
71
|
|
|
72
72
|
await stack.enter_async_context(registry_handler.pull(packages=validated_packages))
|
|
@@ -112,7 +112,7 @@ def pull(channel: str, config_file: str, packages: str) -> None:
|
|
|
112
112
|
logger.error("Pull channel '%s' has not been configured.", channel)
|
|
113
113
|
return
|
|
114
114
|
except Exception as e:
|
|
115
|
-
logger.exception("Error loading user settings: %s", e
|
|
115
|
+
logger.exception("Error loading user settings: %s", e)
|
|
116
116
|
return
|
|
117
117
|
|
|
118
118
|
asyncio.run(pull_artifact(pull_channel_config, packages))
|
|
@@ -41,7 +41,7 @@ async def remove_artifact(registry_handler_config: RegistryHandlerBaseConfig, pa
|
|
|
41
41
|
try:
|
|
42
42
|
package_name_list = PackageNameVersionList(**{"packages": packages})
|
|
43
43
|
except Exception as e:
|
|
44
|
-
logger.exception("Invalid package format: '%s'", e
|
|
44
|
+
logger.exception("Invalid package format: '%s'", e)
|
|
45
45
|
|
|
46
46
|
await stack.enter_async_context(registry_handler.remove(packages=package_name_list))
|
|
47
47
|
|
|
@@ -102,7 +102,7 @@ def remove(channel: str, config_file: str, packages: str) -> None:
|
|
|
102
102
|
logger.error("Remove channel '%s' has not been configured.", channel)
|
|
103
103
|
return
|
|
104
104
|
except Exception as e:
|
|
105
|
-
logger.exception("Error loading user settings: %s", e
|
|
105
|
+
logger.exception("Error loading user settings: %s", e)
|
|
106
106
|
return
|
|
107
107
|
|
|
108
108
|
asyncio.run(remove_artifact(registry_handler_config=remove_channel_config, packages=packages_versions))
|
|
@@ -29,14 +29,13 @@ from nat.utils.data_models.schema_validator import validate_yaml
|
|
|
29
29
|
logger = logging.getLogger(__name__)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
async def search_artifacts(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
n_results: int = 10) -> None:
|
|
32
|
+
async def search_artifacts(registry_handler_config: RegistryHandlerBaseConfig,
|
|
33
|
+
query: str,
|
|
34
|
+
search_fields: list[SearchFields],
|
|
35
|
+
visualize: bool,
|
|
36
|
+
component_types: list[ComponentEnum],
|
|
37
|
+
save_path: str | None = None,
|
|
38
|
+
n_results: int = 10) -> None:
|
|
40
39
|
|
|
41
40
|
from nat.cli.type_registry import GlobalTypeRegistry
|
|
42
41
|
from nat.registry_handlers.schemas.search import SearchQuery
|
|
@@ -116,14 +115,13 @@ async def search_artifacts( # pylint: disable=R0917
|
|
|
116
115
|
required=False,
|
|
117
116
|
help=("The component types to include in search."),
|
|
118
117
|
)
|
|
119
|
-
def search(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
output_path: str) -> None:
|
|
118
|
+
def search(config_file: str,
|
|
119
|
+
channel: str,
|
|
120
|
+
fields: list[str],
|
|
121
|
+
query: str,
|
|
122
|
+
component_types: list[ComponentEnum],
|
|
123
|
+
n_results: int,
|
|
124
|
+
output_path: str) -> None:
|
|
127
125
|
"""
|
|
128
126
|
Search for NAT artifacts with the specified configuration.
|
|
129
127
|
"""
|
|
@@ -142,7 +140,7 @@ def search( # pylint: disable=R0917
|
|
|
142
140
|
logger.error("Search channel '%s' has not been configured.", channel)
|
|
143
141
|
return
|
|
144
142
|
except Exception as e:
|
|
145
|
-
logger.exception("Error loading user settings: %s", e
|
|
143
|
+
logger.exception("Error loading user settings: %s", e)
|
|
146
144
|
return
|
|
147
145
|
|
|
148
146
|
asyncio.run(
|
nat/cli/commands/start.py
CHANGED
|
@@ -35,7 +35,6 @@ logger = logging.getLogger(__name__)
|
|
|
35
35
|
|
|
36
36
|
class StartCommandGroup(click.Group):
|
|
37
37
|
|
|
38
|
-
# pylint: disable=too-many-positional-arguments
|
|
39
38
|
def __init__(
|
|
40
39
|
self,
|
|
41
40
|
name: str | None = None,
|
|
@@ -103,12 +102,24 @@ class StartCommandGroup(click.Group):
|
|
|
103
102
|
raise ValueError(f"Invalid field '{name}'.Unions are only supported for optional parameters.")
|
|
104
103
|
|
|
105
104
|
# Handle the types
|
|
106
|
-
|
|
105
|
+
# Literal[...] -> map to click.Choice([...])
|
|
106
|
+
if (decomposed_type.origin is typing.Literal):
|
|
107
|
+
# typing.get_args returns the literal values; ensure they are strings for Click
|
|
108
|
+
literal_values = [str(v) for v in decomposed_type.args]
|
|
109
|
+
param_type = click.Choice(literal_values)
|
|
110
|
+
|
|
111
|
+
elif (issubclass(decomposed_type.root, Path)):
|
|
107
112
|
param_type = click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path)
|
|
108
113
|
|
|
109
|
-
elif (issubclass(decomposed_type.root,
|
|
114
|
+
elif (issubclass(decomposed_type.root, list | tuple | set)):
|
|
110
115
|
if (len(decomposed_type.args) == 1):
|
|
111
|
-
|
|
116
|
+
inner = DecomposedType(decomposed_type.args[0])
|
|
117
|
+
# Support containers of Literal values -> multiple Choice
|
|
118
|
+
if (inner.origin is typing.Literal):
|
|
119
|
+
literal_values = [str(v) for v in inner.args]
|
|
120
|
+
param_type = click.Choice(literal_values)
|
|
121
|
+
else:
|
|
122
|
+
param_type = inner.root
|
|
112
123
|
else:
|
|
113
124
|
param_type = None
|
|
114
125
|
|
|
@@ -225,7 +236,7 @@ class StartCommandGroup(click.Group):
|
|
|
225
236
|
return asyncio.run(run_plugin())
|
|
226
237
|
|
|
227
238
|
except Exception as e:
|
|
228
|
-
logger.error("Failed to initialize workflow"
|
|
239
|
+
logger.error("Failed to initialize workflow")
|
|
229
240
|
raise click.ClickException(str(e)) from e
|
|
230
241
|
|
|
231
242
|
def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None:
|
nat/cli/commands/uninstall.py
CHANGED
|
@@ -44,7 +44,7 @@ async def uninstall_packages(packages: list[dict[str, str]]) -> None:
|
|
|
44
44
|
try:
|
|
45
45
|
package_name_list = PackageNameVersionList(**{"packages": packages})
|
|
46
46
|
except Exception as e:
|
|
47
|
-
logger.exception("Error validating package format: %s", e
|
|
47
|
+
logger.exception("Error validating package format: %s", e)
|
|
48
48
|
return
|
|
49
49
|
|
|
50
50
|
async with AsyncExitStack() as stack:
|
|
@@ -3,6 +3,9 @@ build-backend = "setuptools.build_meta"
|
|
|
3
3
|
{% if editable %}requires = ["setuptools >= 64", "setuptools-scm>=8"]
|
|
4
4
|
|
|
5
5
|
[tool.setuptools_scm]
|
|
6
|
+
# NAT uses the --first-parent flag to avoid tags from previous releases which have been merged into the develop branch
|
|
7
|
+
# from causing an unexpected version change. This can be safely removed if developing outside of the NAT repository.
|
|
8
|
+
git_describe_command = "git describe --long --first-parent"
|
|
6
9
|
root = "{{ rel_path_to_repo_root}}"{% else %}requires = ["setuptools >= 64"]{% endif %}
|
|
7
10
|
|
|
8
11
|
[project]
|
|
@@ -11,7 +14,7 @@ name = "{{ package_name }}"
|
|
|
11
14
|
dependencies = [
|
|
12
15
|
"{{ nat_dependency }}",
|
|
13
16
|
]
|
|
14
|
-
requires-python = ">=3.11,<3.
|
|
17
|
+
requires-python = ">=3.11,<3.14"
|
|
15
18
|
description = "Custom NeMo Agent Toolkit Workflow"
|
|
16
19
|
classifiers = ["Programming Language :: Python"]
|
|
17
20
|
|
|
@@ -37,7 +37,7 @@ def _get_nat_dependency(versioned: bool = True) -> str:
|
|
|
37
37
|
Returns:
|
|
38
38
|
str: The dependency string to use in pyproject.toml
|
|
39
39
|
"""
|
|
40
|
-
# Assume the default dependency is
|
|
40
|
+
# Assume the default dependency is LangChain/LangGraph
|
|
41
41
|
dependency = "nvidia-nat[langchain]"
|
|
42
42
|
|
|
43
43
|
if not versioned:
|
|
@@ -97,7 +97,7 @@ def find_package_root(package_name: str) -> Path | None:
|
|
|
97
97
|
try:
|
|
98
98
|
info = json.loads(direct_url)
|
|
99
99
|
except json.JSONDecodeError:
|
|
100
|
-
logger.
|
|
100
|
+
logger.exception("Malformed direct_url.json for package: %s", package_name)
|
|
101
101
|
return None
|
|
102
102
|
|
|
103
103
|
if not info.get("dir_info", {}).get("editable"):
|
|
@@ -161,7 +161,6 @@ def get_workflow_path_from_name(workflow_name: str):
|
|
|
161
161
|
default="NAT function template. Please update the description.",
|
|
162
162
|
help="""A description of the component being created. Will be used to populate the docstring and will describe the
|
|
163
163
|
component when inspecting installed components using 'nat info component'""")
|
|
164
|
-
# pylint: disable=missing-param-doc
|
|
165
164
|
def create_command(workflow_name: str, install: bool, workflow_dir: str, description: str):
|
|
166
165
|
"""
|
|
167
166
|
Create a new NAT workflow using templates.
|
|
@@ -172,6 +171,9 @@ def create_command(workflow_name: str, install: bool, workflow_dir: str, descrip
|
|
|
172
171
|
workflow_dir (str): The directory to create the workflow package.
|
|
173
172
|
description (str): Description to pre-popluate the workflow docstring.
|
|
174
173
|
"""
|
|
174
|
+
# Fail fast with Click's standard exit code (2) for bad params.
|
|
175
|
+
if not workflow_name or not workflow_name.strip():
|
|
176
|
+
raise click.BadParameter("Workflow name cannot be empty.") # noqa: TRY003
|
|
175
177
|
try:
|
|
176
178
|
# Get the repository root
|
|
177
179
|
try:
|
|
@@ -218,15 +220,13 @@ def create_command(workflow_name: str, install: bool, workflow_dir: str, descrip
|
|
|
218
220
|
else:
|
|
219
221
|
install_cmd = ['pip', 'install', '-e', str(new_workflow_dir)]
|
|
220
222
|
|
|
221
|
-
config_source = configs_dir / 'config.yml'
|
|
222
|
-
|
|
223
223
|
# List of templates and their destinations
|
|
224
224
|
files_to_render = {
|
|
225
225
|
'pyproject.toml.j2': new_workflow_dir / 'pyproject.toml',
|
|
226
226
|
'register.py.j2': base_dir / 'register.py',
|
|
227
227
|
'workflow.py.j2': base_dir / f'{workflow_name}_function.py',
|
|
228
228
|
'__init__.py.j2': base_dir / '__init__.py',
|
|
229
|
-
'config.yml.j2':
|
|
229
|
+
'config.yml.j2': configs_dir / 'config.yml',
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
# Render templates
|
|
@@ -247,10 +247,6 @@ def create_command(workflow_name: str, install: bool, workflow_dir: str, descrip
|
|
|
247
247
|
with open(output_path, 'w', encoding="utf-8") as f:
|
|
248
248
|
f.write(content)
|
|
249
249
|
|
|
250
|
-
# Create symlink for config.yml
|
|
251
|
-
config_link = new_workflow_dir / 'configs' / 'config.yml'
|
|
252
|
-
os.symlink(config_source, config_link)
|
|
253
|
-
|
|
254
250
|
# Create symlinks for config and data directories
|
|
255
251
|
config_dir_source = configs_dir
|
|
256
252
|
config_dir_link = new_workflow_dir / 'configs'
|
|
@@ -272,7 +268,7 @@ def create_command(workflow_name: str, install: bool, workflow_dir: str, descrip
|
|
|
272
268
|
|
|
273
269
|
click.echo(f"Workflow '{workflow_name}' created successfully in '{new_workflow_dir}'.")
|
|
274
270
|
except Exception as e:
|
|
275
|
-
logger.exception("An error occurred while creating the workflow: %s", e
|
|
271
|
+
logger.exception("An error occurred while creating the workflow: %s", e)
|
|
276
272
|
click.echo(f"An error occurred while creating the workflow: {e}")
|
|
277
273
|
|
|
278
274
|
|
|
@@ -308,7 +304,7 @@ def reinstall_command(workflow_name):
|
|
|
308
304
|
|
|
309
305
|
click.echo(f"Workflow '{workflow_name}' reinstalled successfully.")
|
|
310
306
|
except Exception as e:
|
|
311
|
-
logger.exception("An error occurred while reinstalling the workflow: %s", e
|
|
307
|
+
logger.exception("An error occurred while reinstalling the workflow: %s", e)
|
|
312
308
|
click.echo(f"An error occurred while reinstalling the workflow: {e}")
|
|
313
309
|
|
|
314
310
|
|
|
@@ -355,7 +351,7 @@ def delete_command(workflow_name: str):
|
|
|
355
351
|
|
|
356
352
|
click.echo(f"Workflow '{workflow_name}' deleted successfully.")
|
|
357
353
|
except Exception as e:
|
|
358
|
-
logger.exception("An error occurred while deleting the workflow: %s", e
|
|
354
|
+
logger.exception("An error occurred while deleting the workflow: %s", e)
|
|
359
355
|
click.echo(f"An error occurred while deleting the workflow: {e}")
|
|
360
356
|
|
|
361
357
|
|
nat/cli/entrypoint.py
CHANGED
|
@@ -30,9 +30,14 @@ import time
|
|
|
30
30
|
import click
|
|
31
31
|
import nest_asyncio
|
|
32
32
|
|
|
33
|
+
from nat.utils.log_levels import LOG_LEVELS
|
|
34
|
+
|
|
33
35
|
from .commands.configure.configure import configure_command
|
|
34
36
|
from .commands.evaluate import eval_command
|
|
35
37
|
from .commands.info.info import info_command
|
|
38
|
+
from .commands.mcp.mcp import mcp_command
|
|
39
|
+
from .commands.object_store.object_store import object_store_command
|
|
40
|
+
from .commands.optimize import optimizer_command
|
|
36
41
|
from .commands.registry.registry import registry_command
|
|
37
42
|
from .commands.sizing.sizing import sizing
|
|
38
43
|
from .commands.start import start_command
|
|
@@ -43,15 +48,6 @@ from .commands.workflow.workflow import workflow_command
|
|
|
43
48
|
# Apply at the beginning of the file to avoid issues with asyncio
|
|
44
49
|
nest_asyncio.apply()
|
|
45
50
|
|
|
46
|
-
# Define log level choices
|
|
47
|
-
LOG_LEVELS = {
|
|
48
|
-
'DEBUG': logging.DEBUG,
|
|
49
|
-
'INFO': logging.INFO,
|
|
50
|
-
'WARNING': logging.WARNING,
|
|
51
|
-
'ERROR': logging.ERROR,
|
|
52
|
-
'CRITICAL': logging.CRITICAL
|
|
53
|
-
}
|
|
54
|
-
|
|
55
51
|
|
|
56
52
|
def setup_logging(log_level: str):
|
|
57
53
|
"""Configure logging with the specified level"""
|
|
@@ -107,11 +103,13 @@ cli.add_command(uninstall_command, name="uninstall")
|
|
|
107
103
|
cli.add_command(validate_command, name="validate")
|
|
108
104
|
cli.add_command(workflow_command, name="workflow")
|
|
109
105
|
cli.add_command(sizing, name="sizing")
|
|
106
|
+
cli.add_command(optimizer_command, name="optimize")
|
|
107
|
+
cli.add_command(object_store_command, name="object-store")
|
|
108
|
+
cli.add_command(mcp_command, name="mcp")
|
|
110
109
|
|
|
111
110
|
# Aliases
|
|
112
111
|
cli.add_command(start_command.get_command(None, "console"), name="run") # type: ignore
|
|
113
112
|
cli.add_command(start_command.get_command(None, "fastapi"), name="serve") # type: ignore
|
|
114
|
-
cli.add_command(start_command.get_command(None, "mcp"), name="mcp")
|
|
115
113
|
|
|
116
114
|
|
|
117
115
|
@cli.result_callback()
|