data-designer 0.4.0rc2__tar.gz → 0.5.0rc1__tar.gz
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.
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/PKG-INFO +3 -3
- data_designer-0.5.0rc1/dev-tools/hatch_build.py +31 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/pyproject.toml +23 -14
- data_designer-0.5.0rc1/src/data_designer/cli/commands/list.py +255 -0
- data_designer-0.5.0rc1/src/data_designer/cli/commands/mcp.py +13 -0
- data_designer-0.5.0rc1/src/data_designer/cli/commands/tools.py +13 -0
- data_designer-0.5.0rc1/src/data_designer/cli/controllers/mcp_provider_controller.py +241 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/controllers/provider_controller.py +6 -2
- data_designer-0.5.0rc1/src/data_designer/cli/controllers/tool_controller.py +236 -0
- data_designer-0.5.0rc1/src/data_designer/cli/forms/mcp_provider_builder.py +219 -0
- data_designer-0.5.0rc1/src/data_designer/cli/forms/tool_builder.py +204 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/main.py +3 -1
- data_designer-0.5.0rc1/src/data_designer/cli/repositories/mcp_provider_repository.py +48 -0
- data_designer-0.5.0rc1/src/data_designer/cli/repositories/tool_repository.py +44 -0
- data_designer-0.5.0rc1/src/data_designer/cli/services/mcp_provider_service.py +86 -0
- data_designer-0.5.0rc1/src/data_designer/cli/services/tool_service.py +83 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/interface/data_designer.py +27 -0
- data_designer-0.5.0rc1/tests/cli/commands/test_list_command.py +158 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/conftest.py +91 -0
- data_designer-0.5.0rc1/tests/cli/controllers/test_mcp_provider_controller.py +368 -0
- data_designer-0.5.0rc1/tests/cli/controllers/test_tool_controller.py +413 -0
- data_designer-0.5.0rc1/tests/cli/forms/test_mcp_provider_builder.py +609 -0
- data_designer-0.5.0rc1/tests/cli/forms/test_tool_builder.py +628 -0
- data_designer-0.5.0rc1/tests/cli/repositories/test_mcp_provider_repository.py +39 -0
- data_designer-0.5.0rc1/tests/cli/repositories/test_tool_repository.py +37 -0
- data_designer-0.5.0rc1/tests/cli/services/test_mcp_provider_service.py +110 -0
- data_designer-0.5.0rc1/tests/cli/services/test_tool_service.py +92 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/conftest.py +4 -1
- data_designer-0.4.0rc2/src/data_designer/cli/commands/list.py +0 -118
- data_designer-0.4.0rc2/src/data_designer/interface/_version.py +0 -34
- data_designer-0.4.0rc2/tests/cli/commands/test_list_command.py +0 -82
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/.gitignore +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/README.md +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/README.md +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/__init__.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/commands/__init__.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/commands/download.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/commands/models.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/commands/providers.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/commands/reset.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/controllers/__init__.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/controllers/download_controller.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/controllers/model_controller.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/forms/__init__.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/forms/builder.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/forms/field.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/forms/form.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/forms/model_builder.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/forms/provider_builder.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/repositories/__init__.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/repositories/base.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/repositories/model_repository.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/repositories/persona_repository.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/repositories/provider_repository.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/services/__init__.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/services/download_service.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/services/model_service.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/services/provider_service.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/ui.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/cli/utils.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/interface/__init__.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/interface/errors.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/src/data_designer/interface/results.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/commands/test_download_command.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/commands/test_models_command.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/commands/test_providers_command.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/commands/test_reset_command.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/controllers/test_download_controller.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/controllers/test_model_controller.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/controllers/test_provider_controller.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/forms/test_field.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/forms/test_form.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/forms/test_model_builder.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/forms/test_provider_builder.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/repositories/test_model_repository.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/repositories/test_persona_repository.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/repositories/test_provider_repository.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/services/test_download_service.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/services/test_model_service.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/services/test_provider_service.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/cli/test_cli_utils.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/interface/test_data_designer.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/interface/test_results.py +0 -0
- {data_designer-0.4.0rc2 → data_designer-0.5.0rc1}/tests/test_import_perf.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: data-designer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0rc1
|
|
4
4
|
Summary: General framework for synthetic data generation
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -14,8 +14,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
14
14
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
15
|
Classifier: Topic :: Software Development
|
|
16
16
|
Requires-Python: >=3.10
|
|
17
|
-
Requires-Dist: data-designer-config
|
|
18
|
-
Requires-Dist: data-designer-engine
|
|
17
|
+
Requires-Dist: data-designer-config==0.5.0rc1
|
|
18
|
+
Requires-Dist: data-designer-engine==0.5.0rc1
|
|
19
19
|
Requires-Dist: prompt-toolkit<4,>=3.0.0
|
|
20
20
|
Requires-Dist: typer<1,>=0.12.0
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Custom hatch metadata hook to sync README from root.
|
|
5
|
+
|
|
6
|
+
This hook runs during metadata resolution (before build hooks) to ensure
|
|
7
|
+
the README.md from the repository root is copied before hatchling validates
|
|
8
|
+
that the readme file exists.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import shutil
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from hatchling.metadata.plugin.interface import MetadataHookInterface
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ReadmeSyncHook(MetadataHookInterface):
|
|
21
|
+
"""Metadata hook that copies README.md from repository root before building."""
|
|
22
|
+
|
|
23
|
+
PLUGIN_NAME = "readme-sync"
|
|
24
|
+
|
|
25
|
+
def update(self, metadata: dict[str, Any]) -> None:
|
|
26
|
+
"""Copy README.md from repository root to package directory."""
|
|
27
|
+
root_readme = Path(self.root) / ".." / ".." / "README.md"
|
|
28
|
+
package_readme = Path(self.root) / "README.md"
|
|
29
|
+
|
|
30
|
+
if root_readme.exists():
|
|
31
|
+
shutil.copy2(root_readme, package_readme)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "data-designer"
|
|
3
|
-
dynamic = ["version"]
|
|
3
|
+
dynamic = ["version", "dependencies"]
|
|
4
4
|
description = "General framework for synthetic data generation"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -19,27 +19,31 @@ classifiers = [
|
|
|
19
19
|
"Programming Language :: Python :: 3.13",
|
|
20
20
|
]
|
|
21
21
|
|
|
22
|
-
dependencies = [
|
|
23
|
-
"data-designer-config",
|
|
24
|
-
"data-designer-engine",
|
|
25
|
-
"prompt-toolkit>=3.0.0,<4",
|
|
26
|
-
"typer>=0.12.0,<1",
|
|
27
|
-
]
|
|
28
|
-
|
|
29
22
|
[project.scripts]
|
|
30
23
|
data-designer = "data_designer.cli:main"
|
|
31
24
|
|
|
32
25
|
[build-system]
|
|
33
|
-
requires = ["hatchling", "
|
|
26
|
+
requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"]
|
|
34
27
|
build-backend = "hatchling.build"
|
|
35
28
|
|
|
36
29
|
[tool.hatch.version]
|
|
37
|
-
source = "
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
source = "uv-dynamic-versioning"
|
|
31
|
+
|
|
32
|
+
[tool.uv-dynamic-versioning]
|
|
33
|
+
vcs = "git"
|
|
34
|
+
style = "pep440"
|
|
35
|
+
bump = true
|
|
40
36
|
|
|
41
|
-
[tool.hatch.
|
|
42
|
-
|
|
37
|
+
[tool.hatch.metadata.hooks.custom]
|
|
38
|
+
path = "dev-tools/hatch_build.py"
|
|
39
|
+
|
|
40
|
+
[tool.hatch.metadata.hooks.uv-dynamic-versioning]
|
|
41
|
+
dependencies = [
|
|
42
|
+
"data-designer-config=={{ version }}",
|
|
43
|
+
"data-designer-engine=={{ version }}",
|
|
44
|
+
"prompt-toolkit>=3.0.0,<4",
|
|
45
|
+
"typer>=0.12.0,<1",
|
|
46
|
+
]
|
|
43
47
|
|
|
44
48
|
[tool.hatch.build.targets.wheel]
|
|
45
49
|
packages = ["src/data_designer"]
|
|
@@ -47,6 +51,11 @@ packages = ["src/data_designer"]
|
|
|
47
51
|
[tool.ruff]
|
|
48
52
|
extend = "../../pyproject.toml"
|
|
49
53
|
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
testpaths = ["tests"]
|
|
56
|
+
asyncio_default_fixture_loop_scope = "session"
|
|
57
|
+
env = ["DISABLE_DATA_DESIGNER_PLUGINS=true"]
|
|
58
|
+
|
|
50
59
|
[tool.uv]
|
|
51
60
|
package = true
|
|
52
61
|
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from data_designer.cli.repositories.mcp_provider_repository import MCPProviderRepository
|
|
11
|
+
from data_designer.cli.repositories.model_repository import ModelRepository
|
|
12
|
+
from data_designer.cli.repositories.provider_repository import ProviderRepository
|
|
13
|
+
from data_designer.cli.repositories.tool_repository import ToolRepository
|
|
14
|
+
from data_designer.cli.ui import console, print_error, print_header, print_info, print_warning
|
|
15
|
+
from data_designer.config.mcp import LocalStdioMCPProvider, MCPProvider
|
|
16
|
+
from data_designer.config.utils.constants import DATA_DESIGNER_HOME, NordColor
|
|
17
|
+
|
|
18
|
+
# Pattern for valid environment variable names (uppercase letters, digits, underscores, not starting with digit)
|
|
19
|
+
_ENV_VAR_PATTERN = re.compile(r"^[A-Z_][A-Z0-9_]*$")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _is_env_var_name(value: str) -> bool:
|
|
23
|
+
"""Check if a string looks like an environment variable name.
|
|
24
|
+
|
|
25
|
+
Returns True only if the string matches the pattern for typical env var names:
|
|
26
|
+
- All uppercase letters, digits, and underscores only
|
|
27
|
+
- Does not start with a digit
|
|
28
|
+
- At least one character
|
|
29
|
+
|
|
30
|
+
This is intentionally conservative to avoid leaking secrets that happen
|
|
31
|
+
to be uppercase (e.g., base32-encoded API keys).
|
|
32
|
+
"""
|
|
33
|
+
return bool(_ENV_VAR_PATTERN.match(value))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _mask_api_key(api_key: str | None) -> str:
|
|
37
|
+
"""Mask an API key for display, preserving environment variable names.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
api_key: The API key value or None.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
A display string: "(not set)" if None, the original value if it looks
|
|
44
|
+
like an env var name, or a masked version showing only the last 4 chars.
|
|
45
|
+
"""
|
|
46
|
+
if not api_key:
|
|
47
|
+
return "(not set)"
|
|
48
|
+
# Only show unmasked if it looks like a valid environment variable name
|
|
49
|
+
if _is_env_var_name(api_key):
|
|
50
|
+
return api_key
|
|
51
|
+
return "***" + api_key[-4:] if len(api_key) > 4 else "***"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def list_command() -> None:
|
|
55
|
+
"""List current Data Designer configurations.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
None
|
|
59
|
+
"""
|
|
60
|
+
# Determine config directory
|
|
61
|
+
print_header("Data Designer Configurations")
|
|
62
|
+
print_info(f"Configuration directory: {DATA_DESIGNER_HOME}")
|
|
63
|
+
console.print()
|
|
64
|
+
|
|
65
|
+
# Display all configuration types
|
|
66
|
+
display_providers(ProviderRepository(DATA_DESIGNER_HOME))
|
|
67
|
+
display_models(ModelRepository(DATA_DESIGNER_HOME))
|
|
68
|
+
display_mcp_providers(MCPProviderRepository(DATA_DESIGNER_HOME))
|
|
69
|
+
display_tool_configs(ToolRepository(DATA_DESIGNER_HOME))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def display_providers(provider_repo: ProviderRepository) -> None:
|
|
73
|
+
"""Load and display model providers.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
provider_repo: Provider repository
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
None
|
|
80
|
+
"""
|
|
81
|
+
try:
|
|
82
|
+
provider_registry = provider_repo.load()
|
|
83
|
+
|
|
84
|
+
if not provider_registry:
|
|
85
|
+
print_warning("Providers have not been configured. Run 'data-designer config providers' to configure them.")
|
|
86
|
+
console.print()
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
# Display as table
|
|
90
|
+
table = Table(title="Model Providers", border_style=NordColor.NORD8.value)
|
|
91
|
+
table.add_column("Name", style=NordColor.NORD14.value, no_wrap=True)
|
|
92
|
+
table.add_column("Endpoint", style=NordColor.NORD4.value)
|
|
93
|
+
table.add_column("Type", style=NordColor.NORD9.value, no_wrap=True)
|
|
94
|
+
table.add_column("API Key", style=NordColor.NORD7.value)
|
|
95
|
+
table.add_column("Default", style=NordColor.NORD13.value, justify="center")
|
|
96
|
+
|
|
97
|
+
default_name = provider_registry.default or provider_registry.providers[0].name
|
|
98
|
+
|
|
99
|
+
for provider in provider_registry.providers:
|
|
100
|
+
is_default = "✓" if provider.name == default_name else ""
|
|
101
|
+
api_key_display = _mask_api_key(provider.api_key)
|
|
102
|
+
|
|
103
|
+
table.add_row(
|
|
104
|
+
provider.name,
|
|
105
|
+
provider.endpoint,
|
|
106
|
+
provider.provider_type,
|
|
107
|
+
api_key_display,
|
|
108
|
+
is_default,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
console.print(table)
|
|
112
|
+
console.print()
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print_error(f"Error loading provider configuration: {e}")
|
|
115
|
+
console.print()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def display_models(model_repo: ModelRepository) -> None:
|
|
119
|
+
"""Load and display model configurations.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
model_repo: Model repository
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
None
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
registry = model_repo.load()
|
|
129
|
+
|
|
130
|
+
if not registry:
|
|
131
|
+
print_warning("Models have not been configured. Run 'data-designer config models' to configure them.")
|
|
132
|
+
console.print()
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
# Display as table
|
|
136
|
+
table = Table(title="Model Configurations", border_style=NordColor.NORD8.value)
|
|
137
|
+
table.add_column("Alias", style=NordColor.NORD14.value, no_wrap=True)
|
|
138
|
+
table.add_column("Model", style=NordColor.NORD4.value)
|
|
139
|
+
table.add_column("Provider", style=NordColor.NORD9.value, no_wrap=True)
|
|
140
|
+
table.add_column("Inference Parameters", style=NordColor.NORD15.value)
|
|
141
|
+
|
|
142
|
+
for mc in registry.model_configs:
|
|
143
|
+
params_display = mc.inference_parameters.format_for_display()
|
|
144
|
+
|
|
145
|
+
table.add_row(
|
|
146
|
+
mc.alias,
|
|
147
|
+
mc.model,
|
|
148
|
+
mc.provider or "(default)",
|
|
149
|
+
params_display,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
console.print(table)
|
|
153
|
+
console.print()
|
|
154
|
+
except Exception as e:
|
|
155
|
+
print_error(f"Error loading model configuration: {e}")
|
|
156
|
+
console.print()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def display_mcp_providers(mcp_provider_repo: MCPProviderRepository) -> None:
|
|
160
|
+
"""Load and display MCP provider configurations.
|
|
161
|
+
|
|
162
|
+
Handles both MCPProvider (remote SSE) and LocalStdioMCPProvider (subprocess).
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
mcp_provider_repo: MCP provider repository
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
None
|
|
169
|
+
"""
|
|
170
|
+
try:
|
|
171
|
+
registry = mcp_provider_repo.load()
|
|
172
|
+
|
|
173
|
+
if not registry:
|
|
174
|
+
print_warning("MCP providers have not been configured. Run 'data-designer config mcp' to configure them.")
|
|
175
|
+
console.print()
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
# Display as table
|
|
179
|
+
table = Table(title="MCP Providers", border_style=NordColor.NORD8.value)
|
|
180
|
+
table.add_column("Name", style=NordColor.NORD14.value, no_wrap=True)
|
|
181
|
+
table.add_column("Endpoint / Command", style=NordColor.NORD4.value)
|
|
182
|
+
table.add_column("Type", style=NordColor.NORD9.value, no_wrap=True)
|
|
183
|
+
table.add_column("API Key / Env", style=NordColor.NORD7.value)
|
|
184
|
+
|
|
185
|
+
for provider in registry.providers:
|
|
186
|
+
if isinstance(provider, MCPProvider):
|
|
187
|
+
endpoint_display = provider.endpoint
|
|
188
|
+
api_key_display = _mask_api_key(provider.api_key)
|
|
189
|
+
elif isinstance(provider, LocalStdioMCPProvider):
|
|
190
|
+
# Display command + args for stdio provider
|
|
191
|
+
args_str = " ".join(provider.args) if provider.args else ""
|
|
192
|
+
endpoint_display = f"{provider.command} {args_str}".strip()
|
|
193
|
+
# Show env vars count for stdio provider
|
|
194
|
+
api_key_display = f"{len(provider.env)} env vars" if provider.env else "(none)"
|
|
195
|
+
else:
|
|
196
|
+
endpoint_display = "(unknown)"
|
|
197
|
+
api_key_display = "(unknown)"
|
|
198
|
+
|
|
199
|
+
table.add_row(
|
|
200
|
+
provider.name,
|
|
201
|
+
endpoint_display,
|
|
202
|
+
provider.provider_type,
|
|
203
|
+
api_key_display,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
console.print(table)
|
|
207
|
+
console.print()
|
|
208
|
+
except Exception as e:
|
|
209
|
+
print_error(f"Error loading MCP provider configuration: {e}")
|
|
210
|
+
console.print()
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def display_tool_configs(tool_repo: ToolRepository) -> None:
|
|
214
|
+
"""Load and display tool configurations.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
tool_repo: Tool repository
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
None
|
|
221
|
+
"""
|
|
222
|
+
try:
|
|
223
|
+
registry = tool_repo.load()
|
|
224
|
+
|
|
225
|
+
if not registry:
|
|
226
|
+
print_warning("Tool configs have not been configured. Run 'data-designer config tools' to configure them.")
|
|
227
|
+
console.print()
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
# Display as table
|
|
231
|
+
table = Table(title="Tool Configurations", border_style=NordColor.NORD8.value)
|
|
232
|
+
table.add_column("Alias", style=NordColor.NORD14.value, no_wrap=True)
|
|
233
|
+
table.add_column("Providers", style=NordColor.NORD4.value)
|
|
234
|
+
table.add_column("Allowed Tools", style=NordColor.NORD9.value)
|
|
235
|
+
table.add_column("Max Turns", style=NordColor.NORD7.value, justify="center")
|
|
236
|
+
table.add_column("Timeout", style=NordColor.NORD15.value, justify="center")
|
|
237
|
+
|
|
238
|
+
for tc in registry.tool_configs:
|
|
239
|
+
providers_display = ", ".join(tc.providers)
|
|
240
|
+
allow_tools_display = ", ".join(tc.allow_tools) if tc.allow_tools else "(all)"
|
|
241
|
+
timeout_display = f"{tc.timeout_sec}s" if tc.timeout_sec else "(none)"
|
|
242
|
+
|
|
243
|
+
table.add_row(
|
|
244
|
+
tc.tool_alias,
|
|
245
|
+
providers_display,
|
|
246
|
+
allow_tools_display,
|
|
247
|
+
str(tc.max_tool_call_turns),
|
|
248
|
+
timeout_display,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
console.print(table)
|
|
252
|
+
console.print()
|
|
253
|
+
except Exception as e:
|
|
254
|
+
print_error(f"Error loading tool configuration: {e}")
|
|
255
|
+
console.print()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from data_designer.cli.controllers.mcp_provider_controller import MCPProviderController
|
|
7
|
+
from data_designer.config.utils.constants import DATA_DESIGNER_HOME
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def mcp_command() -> None:
|
|
11
|
+
"""Configure MCP providers interactively."""
|
|
12
|
+
controller = MCPProviderController(DATA_DESIGNER_HOME)
|
|
13
|
+
controller.run()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from data_designer.cli.controllers.tool_controller import ToolController
|
|
7
|
+
from data_designer.config.utils.constants import DATA_DESIGNER_HOME
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def tools_command() -> None:
|
|
11
|
+
"""Configure tool configs interactively."""
|
|
12
|
+
controller = ToolController(DATA_DESIGNER_HOME)
|
|
13
|
+
controller.run()
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import copy
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
# Pattern for valid environment variable names (uppercase letters, digits, underscores, not starting with digit)
|
|
11
|
+
_ENV_VAR_PATTERN = re.compile(r"^[A-Z_][A-Z0-9_]*$")
|
|
12
|
+
|
|
13
|
+
from data_designer.cli.forms.mcp_provider_builder import MCPProviderFormBuilder, MCPProviderT
|
|
14
|
+
from data_designer.cli.repositories.mcp_provider_repository import MCPProviderRepository
|
|
15
|
+
from data_designer.cli.services.mcp_provider_service import MCPProviderService
|
|
16
|
+
from data_designer.cli.ui import (
|
|
17
|
+
confirm_action,
|
|
18
|
+
console,
|
|
19
|
+
display_config_preview,
|
|
20
|
+
print_error,
|
|
21
|
+
print_header,
|
|
22
|
+
print_info,
|
|
23
|
+
print_success,
|
|
24
|
+
select_with_arrows,
|
|
25
|
+
)
|
|
26
|
+
from data_designer.config.mcp import LocalStdioMCPProvider, MCPProvider
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MCPProviderController:
|
|
30
|
+
"""Controller for MCP provider configuration workflows."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, config_dir: Path):
|
|
33
|
+
self.config_dir = config_dir
|
|
34
|
+
self.repository = MCPProviderRepository(config_dir)
|
|
35
|
+
self.service = MCPProviderService(self.repository)
|
|
36
|
+
|
|
37
|
+
def run(self) -> None:
|
|
38
|
+
"""Main entry point for MCP provider configuration."""
|
|
39
|
+
print_header("Configure MCP Providers")
|
|
40
|
+
print_info(f"Configuration directory: {self.config_dir}")
|
|
41
|
+
console.print()
|
|
42
|
+
|
|
43
|
+
# Check for existing configuration
|
|
44
|
+
providers = self.service.list_all()
|
|
45
|
+
|
|
46
|
+
if providers:
|
|
47
|
+
self._show_existing_config()
|
|
48
|
+
mode = self._select_mode()
|
|
49
|
+
else:
|
|
50
|
+
print_info("No MCP providers configured yet")
|
|
51
|
+
console.print()
|
|
52
|
+
mode = "add"
|
|
53
|
+
|
|
54
|
+
if mode is None:
|
|
55
|
+
print_info("No changes made")
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
# Execute selected mode
|
|
59
|
+
mode_handlers = {
|
|
60
|
+
"add": self._handle_add,
|
|
61
|
+
"update": self._handle_update,
|
|
62
|
+
"delete": self._handle_delete,
|
|
63
|
+
"delete_all": self._handle_delete_all,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
handler = mode_handlers.get(mode)
|
|
67
|
+
if handler:
|
|
68
|
+
handler()
|
|
69
|
+
|
|
70
|
+
def _show_existing_config(self) -> None:
|
|
71
|
+
"""Display current configuration."""
|
|
72
|
+
registry = self.repository.load()
|
|
73
|
+
if not registry:
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
print_info(f"Found {len(registry.providers)} configured MCP provider(s)")
|
|
77
|
+
console.print()
|
|
78
|
+
|
|
79
|
+
# Display configuration (with masked API keys)
|
|
80
|
+
config_dict = registry.model_dump(mode="json", exclude_none=True)
|
|
81
|
+
masked_config = self._mask_api_keys(config_dict)
|
|
82
|
+
display_config_preview(masked_config, "Current Configuration")
|
|
83
|
+
console.print()
|
|
84
|
+
|
|
85
|
+
def _mask_api_keys(self, config: dict) -> dict:
|
|
86
|
+
"""Mask API keys in configuration for display."""
|
|
87
|
+
masked = copy.deepcopy(config)
|
|
88
|
+
|
|
89
|
+
if "providers" in masked:
|
|
90
|
+
for provider in masked["providers"]:
|
|
91
|
+
if "api_key" in provider and provider["api_key"]:
|
|
92
|
+
api_key = provider["api_key"]
|
|
93
|
+
# Only show unmasked if it looks like a valid environment variable name
|
|
94
|
+
if not _ENV_VAR_PATTERN.match(api_key):
|
|
95
|
+
provider["api_key"] = "***" + api_key[-4:] if len(api_key) > 4 else "***"
|
|
96
|
+
|
|
97
|
+
return masked
|
|
98
|
+
|
|
99
|
+
def _select_mode(self) -> str | None:
|
|
100
|
+
"""Prompt user to select operation mode."""
|
|
101
|
+
options = {
|
|
102
|
+
"add": "Add a new MCP provider",
|
|
103
|
+
"update": "Update an existing MCP provider",
|
|
104
|
+
"delete": "Delete an MCP provider",
|
|
105
|
+
"delete_all": "Delete all MCP providers",
|
|
106
|
+
"exit": "Exit without changes",
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
result = select_with_arrows(
|
|
110
|
+
options,
|
|
111
|
+
"What would you like to do?",
|
|
112
|
+
default_key="add",
|
|
113
|
+
allow_back=False,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return None if result == "exit" or result is None else result
|
|
117
|
+
|
|
118
|
+
def _handle_add(self) -> None:
|
|
119
|
+
"""Handle adding new MCP providers."""
|
|
120
|
+
existing_names = {p.name for p in self.service.list_all()}
|
|
121
|
+
|
|
122
|
+
while True:
|
|
123
|
+
# Create builder with current existing names
|
|
124
|
+
builder = MCPProviderFormBuilder(existing_names)
|
|
125
|
+
provider = builder.run()
|
|
126
|
+
|
|
127
|
+
if provider is None:
|
|
128
|
+
break
|
|
129
|
+
|
|
130
|
+
# Attempt to add
|
|
131
|
+
try:
|
|
132
|
+
self.service.add(provider)
|
|
133
|
+
print_success(f"MCP provider '{provider.name}' added successfully")
|
|
134
|
+
existing_names.add(provider.name)
|
|
135
|
+
except ValueError as e:
|
|
136
|
+
print_error(f"Failed to add MCP provider: {e}")
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
# Ask if they want to add more
|
|
140
|
+
if not self._confirm_add_another():
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
def _handle_update(self) -> None:
|
|
144
|
+
"""Handle updating an existing MCP provider."""
|
|
145
|
+
providers = self.service.list_all()
|
|
146
|
+
if not providers:
|
|
147
|
+
print_error("No MCP providers to update")
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
# Select provider to update
|
|
151
|
+
selected_name = self._select_provider(providers, "Select MCP provider to update")
|
|
152
|
+
if selected_name is None:
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
provider = self.service.get_by_name(selected_name)
|
|
156
|
+
if not provider:
|
|
157
|
+
print_error(f"MCP provider '{selected_name}' not found")
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
# Run builder with existing data
|
|
161
|
+
existing_names = {p.name for p in providers if p.name != selected_name}
|
|
162
|
+
builder = MCPProviderFormBuilder(existing_names)
|
|
163
|
+
initial_data = provider.model_dump(mode="json", exclude_none=True)
|
|
164
|
+
updated_provider = builder.run(initial_data)
|
|
165
|
+
|
|
166
|
+
if updated_provider:
|
|
167
|
+
try:
|
|
168
|
+
self.service.update(selected_name, updated_provider)
|
|
169
|
+
print_success(f"MCP provider '{updated_provider.name}' updated successfully")
|
|
170
|
+
except ValueError as e:
|
|
171
|
+
print_error(f"Failed to update MCP provider: {e}")
|
|
172
|
+
|
|
173
|
+
def _handle_delete(self) -> None:
|
|
174
|
+
"""Handle deleting an MCP provider."""
|
|
175
|
+
providers = self.service.list_all()
|
|
176
|
+
if not providers:
|
|
177
|
+
print_error("No MCP providers to delete")
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
# Select provider to delete
|
|
181
|
+
selected_name = self._select_provider(providers, "Select MCP provider to delete")
|
|
182
|
+
if selected_name is None:
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
# Confirm deletion
|
|
186
|
+
console.print()
|
|
187
|
+
if confirm_action(f"Delete MCP provider '{selected_name}'?", default=False):
|
|
188
|
+
try:
|
|
189
|
+
self.service.delete(selected_name)
|
|
190
|
+
print_success(f"MCP provider '{selected_name}' deleted successfully")
|
|
191
|
+
except ValueError as e:
|
|
192
|
+
print_error(f"Failed to delete MCP provider: {e}")
|
|
193
|
+
|
|
194
|
+
def _handle_delete_all(self) -> None:
|
|
195
|
+
"""Handle deleting all MCP providers."""
|
|
196
|
+
providers = self.service.list_all()
|
|
197
|
+
if not providers:
|
|
198
|
+
print_error("No MCP providers to delete")
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
# List providers to be deleted
|
|
202
|
+
console.print()
|
|
203
|
+
provider_count = len(providers)
|
|
204
|
+
provider_names = ", ".join([f"'{p.name}'" for p in providers])
|
|
205
|
+
|
|
206
|
+
if confirm_action(
|
|
207
|
+
f"Delete ALL ({provider_count}) MCP provider(s): {provider_names}?\n This action cannot be undone.",
|
|
208
|
+
default=False,
|
|
209
|
+
):
|
|
210
|
+
try:
|
|
211
|
+
self.repository.delete()
|
|
212
|
+
print_success(f"All ({provider_count}) MCP provider(s) deleted successfully")
|
|
213
|
+
except Exception as e:
|
|
214
|
+
print_error(f"Failed to delete all MCP providers: {e}")
|
|
215
|
+
|
|
216
|
+
def _select_provider(self, providers: list[MCPProviderT], prompt: str, default: str | None = None) -> str | None:
|
|
217
|
+
"""Helper to select an MCP provider from list."""
|
|
218
|
+
options = {}
|
|
219
|
+
for p in providers:
|
|
220
|
+
if isinstance(p, MCPProvider):
|
|
221
|
+
options[p.name] = f"{p.name} (SSE: {p.endpoint})"
|
|
222
|
+
elif isinstance(p, LocalStdioMCPProvider):
|
|
223
|
+
options[p.name] = f"{p.name} (stdio: {p.command})"
|
|
224
|
+
else:
|
|
225
|
+
options[p.name] = p.name
|
|
226
|
+
return select_with_arrows(
|
|
227
|
+
options,
|
|
228
|
+
prompt,
|
|
229
|
+
default_key=default or providers[0].name,
|
|
230
|
+
allow_back=False,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
def _confirm_add_another(self) -> bool:
|
|
234
|
+
"""Ask if user wants to add another MCP provider."""
|
|
235
|
+
result = select_with_arrows(
|
|
236
|
+
{"yes": "Add another MCP provider", "no": "Finish"},
|
|
237
|
+
"Add another MCP provider?",
|
|
238
|
+
default_key="no",
|
|
239
|
+
allow_back=False,
|
|
240
|
+
)
|
|
241
|
+
return result == "yes"
|
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
import copy
|
|
7
|
+
import re
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from typing import TYPE_CHECKING
|
|
9
10
|
|
|
10
11
|
from data_designer.cli.forms.provider_builder import ProviderFormBuilder
|
|
12
|
+
|
|
13
|
+
# Pattern for valid environment variable names (uppercase letters, digits, underscores, not starting with digit)
|
|
14
|
+
_ENV_VAR_PATTERN = re.compile(r"^[A-Z_][A-Z0-9_]*$")
|
|
11
15
|
from data_designer.cli.repositories.model_repository import ModelRepository
|
|
12
16
|
from data_designer.cli.repositories.provider_repository import ProviderRepository
|
|
13
17
|
from data_designer.cli.services.model_service import ModelService
|
|
@@ -95,8 +99,8 @@ class ProviderController:
|
|
|
95
99
|
for provider in masked["providers"]:
|
|
96
100
|
if "api_key" in provider and provider["api_key"]:
|
|
97
101
|
api_key = provider["api_key"]
|
|
98
|
-
#
|
|
99
|
-
if not
|
|
102
|
+
# Only show unmasked if it looks like a valid environment variable name
|
|
103
|
+
if not _ENV_VAR_PATTERN.match(api_key):
|
|
100
104
|
provider["api_key"] = "***" + api_key[-4:] if len(api_key) > 4 else "***"
|
|
101
105
|
|
|
102
106
|
return masked
|