Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
UniformSamplerParams,
UUIDSamplerParams,
)
from data_designer.config.script_params import DataDesignerScriptParams # noqa: F401
from data_designer.config.seed import ( # noqa: F401
IndexRange,
PartitionBlock,
Expand Down Expand Up @@ -204,6 +205,8 @@
"PartitionBlock": (_MOD_SEED, "PartitionBlock"),
"SamplingStrategy": (_MOD_SEED, "SamplingStrategy"),
"SeedConfig": (_MOD_SEED, "SeedConfig"),
# script params
"DataDesignerScriptParams": (f"{_MOD_BASE}.script_params", "DataDesignerScriptParams"),
# seed_source
"DataFrameSeedSource": (f"{_MOD_BASE}.seed_source_dataframe", "DataFrameSeedSource"),
"AgentRolloutFormat": (_MOD_SEED_SOURCE, "AgentRolloutFormat"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

from __future__ import annotations

from dataclasses import dataclass


@dataclass(frozen=True, slots=True)
class DataDesignerScriptParams:
"""Runtime parameters forwarded to Python config workflows.

Attributes:
argv: Raw workflow arguments passed after the CLI ``--`` separator.
"""

argv: tuple[str, ...] = ()
30 changes: 24 additions & 6 deletions packages/data-designer/src/data_designer/cli/commands/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,36 @@

from __future__ import annotations

from typing import Annotated

import click
import typer

from data_designer.cli.commands.generation_args import resolve_generation_config_target
from data_designer.cli.controllers.generation_controller import GenerationController
from data_designer.config.utils.constants import DEFAULT_NUM_RECORDS
from data_designer.interface.results import SUPPORTED_EXPORT_FORMATS


def create_command(
config_source: str = typer.Argument(
help=(
"Path or URL to a config file (.yaml/.yml/.json), or a local Python module (.py)"
" that defines a load_config_builder() function."
workflow_args: Annotated[
list[str] | None,
typer.Argument(
metavar="[CONFIG_SOURCE] [-- WORKFLOW_ARGS]",
help=(
"Path or URL to a config file (.yaml/.yml/.json), or a local Python module (.py)"
" that defines a load_config_builder() function. Extra arguments after '--' are forwarded to Python"
" workflows."
),
),
),
] = None,
recipe: Annotated[
str | None,
typer.Option(
"--recipe",
help="Name of an installed Data Designer recipe to run instead of a config source.",
),
] = None,
num_records: int = typer.Option(
DEFAULT_NUM_RECORDS,
"--num-records",
Expand Down Expand Up @@ -67,9 +82,12 @@ def create_command(
# Create from a Python module with custom output path
data-designer create my_config.py --artifact-path /path/to/output
"""
target = resolve_generation_config_target(workflow_args, recipe)
controller = GenerationController()
controller.run_create(
config_source=config_source,
config_source=target.config_source,
recipe=target.recipe,
workflow_args=target.workflow_args,
num_records=num_records,
dataset_name=dataset_name,
artifact_path=artifact_path,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

from __future__ import annotations

from dataclasses import dataclass

import click


@dataclass(frozen=True)
class GenerationConfigTarget:
"""Resolved config target for create, preview, and validate commands."""

config_source: str | None
recipe: str | None
workflow_args: tuple[str, ...]


def resolve_generation_config_target(
raw_args: list[str] | None,
recipe: str | None,
) -> GenerationConfigTarget:
"""Split variadic CLI args into a config source or recipe plus workflow args."""
args = tuple(raw_args or ())
if recipe is not None:
return GenerationConfigTarget(config_source=None, recipe=recipe, workflow_args=args)

if not args:
raise click.UsageError("Missing argument 'CONFIG_SOURCE'. Provide a config source or use --recipe.")

config_source, *workflow_args = args
return GenerationConfigTarget(config_source=config_source, recipe=None, workflow_args=tuple(workflow_args))
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,35 @@

from __future__ import annotations

from typing import Annotated

import click
import typer

from data_designer.cli.commands.generation_args import resolve_generation_config_target
from data_designer.cli.controllers.generation_controller import GenerationController
from data_designer.config.utils.constants import DEFAULT_DISPLAY_WIDTH, DEFAULT_NUM_RECORDS


def preview_command(
config_source: str = typer.Argument(
help=(
"Path or URL to a config file (.yaml/.yml/.json), or a local Python module (.py)"
" that defines a load_config_builder() function."
workflow_args: Annotated[
list[str] | None,
typer.Argument(
metavar="[CONFIG_SOURCE] [-- WORKFLOW_ARGS]",
help=(
"Path or URL to a config file (.yaml/.yml/.json), or a local Python module (.py)"
" that defines a load_config_builder() function. Extra arguments after '--' are forwarded to Python"
" workflows."
),
),
),
] = None,
recipe: Annotated[
str | None,
typer.Option(
"--recipe",
help="Name of an installed Data Designer recipe to run instead of a config source.",
),
] = None,
num_records: int = typer.Option(
DEFAULT_NUM_RECORDS,
"--num-records",
Expand Down Expand Up @@ -54,9 +69,12 @@ def preview_command(
),
) -> None:
"""Generate a preview dataset for fast iteration on your configuration."""
target = resolve_generation_config_target(workflow_args, recipe)
controller = GenerationController()
controller.run_preview(
config_source=config_source,
config_source=target.config_source,
recipe=target.recipe,
workflow_args=target.workflow_args,
num_records=num_records,
non_interactive=non_interactive,
save_results=save_results,
Expand Down
108 changes: 108 additions & 0 deletions packages/data-designer/src/data_designer/cli/commands/recipes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

from __future__ import annotations

from typing import Annotated

import click
import typer

from data_designer.cli.ui import console, print_error, print_header
from data_designer.cli.utils.config_loader import ConfigLoadError
from data_designer.cli.utils.recipe_loader import get_recipe_details, get_recipe_help_text, list_recipes

OutputFormat = click.Choice(["text", "json"], case_sensitive=False)


def list_command(
output: Annotated[
str,
typer.Option(
"--output",
"-o",
click_type=OutputFormat,
help="Output format.",
),
] = "text",
) -> None:
"""List installed Data Designer recipes."""
recipes = list_recipes()
if output == "json":
console.print_json(
data={
"schema_version": "data-designer.recipes.list.v1",
"recipes": [recipe.to_dict() for recipe in recipes],
}
)
return

print_header("Installed Data Designer Recipes")
if not recipes:
console.print(" No recipes found.")
return

for recipe in recipes:
package = recipe.package or "unknown package"
version = f" {recipe.version}" if recipe.version is not None else ""
console.print(f" [bold]{recipe.name}[/bold] ({package}{version})")


def show_command(
recipe_name: Annotated[str, typer.Argument(help="Installed recipe name.")],
output: Annotated[
str,
typer.Option(
"--output",
"-o",
click_type=OutputFormat,
help="Output format.",
),
] = "text",
) -> None:
"""Show metadata for an installed Data Designer recipe."""
try:
details = get_recipe_details(recipe_name)
except ConfigLoadError as exc:
print_error(str(exc))
raise typer.Exit(code=1) from exc

if output == "json":
console.print_json(
data={
"schema_version": "data-designer.recipes.show.v1",
"recipe": details.to_dict(),
}
)
return

print_header(f"Recipe: {details.summary.name}")
console.print(f" Entry point: [bold]{details.summary.entry_point}[/bold]")
if details.summary.package is not None:
version = f" {details.summary.version}" if details.summary.version is not None else ""
console.print(f" Package: [bold]{details.summary.package}{version}[/bold]")
if details.description is not None:
console.print(f" Description: {details.description}")

if not details.arguments:
console.print(" Structured argument metadata: unavailable")
return

console.print(" Arguments:")
for argument in details.arguments:
flags = ", ".join(argument["flags"]) if argument["flags"] else argument["name"]
required = " required" if argument["required"] else ""
default = "" if argument["default"] is None else f" default={argument['default']!r}"
help_text = "" if argument["help"] is None else f" — {argument['help']}"
console.print(f" [bold]{flags}[/bold]{required}{default}{help_text}")


def help_command(
recipe_name: Annotated[str, typer.Argument(help="Installed recipe name.")],
) -> None:
"""Show recipe-specific workflow argument help."""
try:
console.print(get_recipe_help_text(recipe_name), markup=False)
except ConfigLoadError as exc:
print_error(str(exc))
raise typer.Exit(code=1) from exc
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,33 @@

from __future__ import annotations

from typing import Annotated

import typer

from data_designer.cli.commands.generation_args import resolve_generation_config_target
from data_designer.cli.controllers.generation_controller import GenerationController


def validate_command(
config_source: str = typer.Argument(
help=(
"Path or URL to a config file (.yaml/.yml/.json), or a local Python module (.py)"
" that defines a load_config_builder() function."
workflow_args: Annotated[
list[str] | None,
typer.Argument(
metavar="[CONFIG_SOURCE] [-- WORKFLOW_ARGS]",
help=(
"Path or URL to a config file (.yaml/.yml/.json), or a local Python module (.py)"
" that defines a load_config_builder() function. Extra arguments after '--' are forwarded to Python"
" workflows."
),
),
] = None,
recipe: Annotated[
str | None,
typer.Option(
"--recipe",
help="Name of an installed Data Designer recipe to validate instead of a config source.",
),
),
] = None,
) -> None:
"""Validate a Data Designer configuration.

Expand All @@ -31,5 +46,8 @@ def validate_command(
# Validate a Python module
data-designer validate my_config.py
"""
target = resolve_generation_config_target(workflow_args, recipe)
controller = GenerationController()
controller.run_validate(config_source=config_source)
controller.run_validate(
config_source=target.config_source, recipe=target.recipe, workflow_args=target.workflow_args
)
Loading
Loading