Skip to content

Commit 3cea3bb

Browse files
committed
Merge branch 'johnny/vibe-cli' into nm/support-cli-model-configs
2 parents bddeb4f + 6bc4c17 commit 3cea3bb

File tree

12 files changed

+2363
-209
lines changed

12 files changed

+2363
-209
lines changed

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ dependencies = [
2929
"pygments>=2.19.2",
3030
"pyyaml>=6.0.1",
3131
"python-json-logger==2.0.7",
32+
"prompt-toolkit>=3.0.0",
3233
"requests<3,>=2.32.2",
3334
"rich>=13.7.1",
35+
"typer>=0.12.0",
3436
"anyascii>=0.3.3,<1.0",
3537
"boto3==1.35.74",
3638
"datasets>=4.0.0",
@@ -52,6 +54,9 @@ dependencies = [
5254
"ruff==0.12.3",
5355
]
5456

57+
[project.scripts]
58+
data-designer = "data_designer.cli:main"
59+
5560
[dependency-groups]
5661
dev = [
5762
"jsonpath-ng==1.5.3",

src/data_designer/cli/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from data_designer.cli.main import app, main
5+
6+
__all__ = ["app", "main"]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
import json
5+
from pathlib import Path
6+
7+
from rich.table import Table
8+
import typer
9+
10+
from data_designer.cli.ui import console, print_error, print_header, print_info, print_warning
11+
from data_designer.cli.utils import get_default_config_dir, get_model_config_path, get_model_provider_path
12+
from data_designer.config.errors import InvalidConfigError, InvalidFileFormatError, InvalidFilePathError
13+
from data_designer.config.models import ModelConfig
14+
from data_designer.config.utils.constants import NordColor
15+
from data_designer.engine.model_provider import ModelProviderRegistry
16+
17+
18+
def list_command(
19+
config_dir: str | None = typer.Option(None, "--config-dir", help="Custom configuration directory"),
20+
output_json: bool = typer.Option(False, "--json", help="Output as JSON"),
21+
) -> None:
22+
"""List current Data Designer configurations."""
23+
# Determine config directory
24+
if config_dir:
25+
config_path = Path(config_dir).expanduser().resolve()
26+
else:
27+
config_path = get_default_config_dir()
28+
29+
if not output_json:
30+
print_header("Data Designer Configuration")
31+
print_info(f"Configuration directory: {config_path}")
32+
console.print()
33+
34+
# Load and display providers
35+
provider_path = get_model_provider_path(config_path)
36+
providers_data = load_providers(provider_path, output_json)
37+
38+
# Load and display models
39+
model_path = get_model_config_path(config_path)
40+
models_data = load_models(model_path, output_json)
41+
42+
# Output as JSON if requested
43+
if output_json:
44+
output = {
45+
"config_directory": str(config_path),
46+
"providers": providers_data,
47+
"models": models_data,
48+
}
49+
console.print_json(json.dumps(output, indent=2))
50+
else:
51+
# Display summary
52+
console.print()
53+
if providers_data or models_data:
54+
print_info("Configuration loaded successfully")
55+
else:
56+
print_warning("No configuration files found. Run 'data-designer config' to see available commands.")
57+
58+
59+
def load_providers(provider_path: Path, as_json: bool) -> dict | None:
60+
"""Load and display model providers.
61+
62+
Args:
63+
provider_path: Path to provider configuration file
64+
as_json: If True, return data for JSON output instead of displaying
65+
66+
Returns:
67+
Provider data if as_json=True, None otherwise
68+
"""
69+
try:
70+
from data_designer.cli.utils import load_config_file
71+
72+
config = load_config_file(provider_path)
73+
74+
# Validate with Pydantic
75+
registry = ModelProviderRegistry.model_validate(config)
76+
77+
if as_json:
78+
return {
79+
"file": str(provider_path),
80+
"providers": [
81+
{
82+
"name": p.name,
83+
"endpoint": p.endpoint,
84+
"provider_type": p.provider_type,
85+
"api_key": p.api_key,
86+
}
87+
for p in registry.providers
88+
],
89+
"default": registry.default or registry.providers[0].name,
90+
"valid": True,
91+
}
92+
93+
# Display as table
94+
table = Table(title="Model Providers", border_style=NordColor.NORD8.value)
95+
table.add_column("Name", style=NordColor.NORD14.value, no_wrap=True)
96+
table.add_column("Endpoint", style=NordColor.NORD4.value)
97+
table.add_column("Type", style=NordColor.NORD9.value, no_wrap=True)
98+
table.add_column("API Key", style=NordColor.NORD7.value)
99+
table.add_column("Default", style=NordColor.NORD13.value, justify="center")
100+
101+
default_name = registry.default or registry.providers[0].name
102+
103+
for provider in registry.providers:
104+
is_default = "✓" if provider.name == default_name else ""
105+
api_key_display = provider.api_key or "(not set)"
106+
107+
# Mask actual API keys (keep env var names visible)
108+
if provider.api_key and not provider.api_key.isupper():
109+
api_key_display = "***" + provider.api_key[-4:] if len(provider.api_key) > 4 else "***"
110+
111+
table.add_row(
112+
provider.name,
113+
provider.endpoint,
114+
provider.provider_type,
115+
api_key_display,
116+
is_default,
117+
)
118+
119+
console.print(table)
120+
console.print()
121+
return None
122+
123+
except InvalidFilePathError:
124+
if not as_json:
125+
print_warning(f"Provider configuration not found: {provider_path}")
126+
print_info("Run 'data-designer config providers' to create it")
127+
console.print()
128+
return {"file": str(provider_path), "valid": False, "error": "File not found"} if as_json else None
129+
130+
except (InvalidFileFormatError, InvalidConfigError) as e:
131+
if not as_json:
132+
print_error(f"Invalid provider configuration: {e}")
133+
console.print()
134+
return {"file": str(provider_path), "valid": False, "error": str(e)} if as_json else None
135+
136+
except Exception as e:
137+
if not as_json:
138+
print_error(f"Error loading provider configuration: {e}")
139+
console.print()
140+
return {"file": str(provider_path), "valid": False, "error": str(e)} if as_json else None
141+
142+
143+
def load_models(model_path: Path, as_json: bool) -> dict | None:
144+
"""Load and display model configurations.
145+
146+
Args:
147+
model_path: Path to model configuration file
148+
as_json: If True, return data for JSON output instead of displaying
149+
150+
Returns:
151+
Model data if as_json=True, None otherwise
152+
"""
153+
try:
154+
from data_designer.cli.utils import load_config_file
155+
156+
config = load_config_file(model_path)
157+
158+
# Validate model configs
159+
if "model_configs" not in config:
160+
raise InvalidConfigError("Missing 'model_configs' key in configuration")
161+
162+
model_configs = [ModelConfig.model_validate(mc) for mc in config["model_configs"]]
163+
164+
if as_json:
165+
return {
166+
"file": str(model_path),
167+
"models": [
168+
{
169+
"alias": mc.alias,
170+
"model": mc.model,
171+
"provider": mc.provider,
172+
"inference_parameters": {
173+
"temperature": mc.inference_parameters.temperature,
174+
"top_p": mc.inference_parameters.top_p,
175+
"max_tokens": mc.inference_parameters.max_tokens,
176+
"max_parallel_requests": mc.inference_parameters.max_parallel_requests,
177+
"timeout": mc.inference_parameters.timeout,
178+
},
179+
}
180+
for mc in model_configs
181+
],
182+
"valid": True,
183+
}
184+
185+
# Display as table
186+
table = Table(title="Model Configurations", border_style=NordColor.NORD8.value)
187+
table.add_column("Alias", style=NordColor.NORD14.value, no_wrap=True)
188+
table.add_column("Model ID", style=NordColor.NORD4.value)
189+
table.add_column("Provider", style=NordColor.NORD9.value, no_wrap=True)
190+
table.add_column("Temperature", style=NordColor.NORD15.value, justify="right")
191+
table.add_column("Top P", style=NordColor.NORD15.value, justify="right")
192+
table.add_column("Max Tokens", style=NordColor.NORD15.value, justify="right")
193+
194+
for mc in model_configs:
195+
# Handle distribution-based parameters
196+
temp_display = (
197+
f"{mc.inference_parameters.temperature:.2f}"
198+
if isinstance(mc.inference_parameters.temperature, (int, float))
199+
else "dist"
200+
)
201+
top_p_display = (
202+
f"{mc.inference_parameters.top_p:.2f}"
203+
if isinstance(mc.inference_parameters.top_p, (int, float))
204+
else "dist"
205+
)
206+
207+
table.add_row(
208+
mc.alias,
209+
mc.model,
210+
mc.provider or "(default)",
211+
temp_display,
212+
top_p_display,
213+
str(mc.inference_parameters.max_tokens) if mc.inference_parameters.max_tokens else "(none)",
214+
)
215+
216+
console.print(table)
217+
console.print()
218+
return None
219+
220+
except InvalidFilePathError:
221+
if not as_json:
222+
print_warning(f"Model configuration not found: {model_path}")
223+
print_info("Run 'data-designer config models' to create it")
224+
console.print()
225+
return {"file": str(model_path), "valid": False, "error": "File not found"} if as_json else None
226+
227+
except (InvalidFileFormatError, InvalidConfigError) as e:
228+
if not as_json:
229+
print_error(f"Invalid model configuration: {e}")
230+
console.print()
231+
return {"file": str(model_path), "valid": False, "error": str(e)} if as_json else None
232+
233+
except Exception as e:
234+
if not as_json:
235+
print_error(f"Error loading model configuration: {e}")
236+
console.print()
237+
return {"file": str(model_path), "valid": False, "error": str(e)} if as_json else None

0 commit comments

Comments
 (0)