data-designer 0.9.2__tar.gz → 0.9.4__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.9.2 → data_designer-0.9.4}/PKG-INFO +5 -5
- {data_designer-0.9.2 → data_designer-0.9.4}/README.md +1 -1
- {data_designer-0.9.2 → data_designer-0.9.4}/pyproject.toml +1 -1
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/integrations/huggingface/client.py +8 -8
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/interface/__init__.py +22 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/interface/composite_workflow.py +41 -4
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/interface/data_designer.py +7 -8
- data_designer-0.9.4/src/data_designer/interface/workflow_metadata.py +97 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_create_command.py +54 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/controllers/test_generation_controller.py +3 -2
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/utils/test_config_loader.py +13 -3
- data_designer-0.9.4/tests/docs/test_chart_qa_recipe.py +136 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/interface/test_composite_workflow.py +107 -1
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/interface/test_data_designer.py +161 -5
- data_designer-0.9.4/tests/interface/test_workflow_metadata.py +191 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/test_lazy_imports.py +12 -0
- data_designer-0.9.4/tests/test_pr_linked_issue_workflow.py +215 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/.gitignore +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/dev-tools/hatch_build.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/README.md +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/__init__.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/agent_command_defs.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/__init__.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/agent.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/check_models.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/create.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/download.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/list.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/mcp.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/models.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/plugin.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/preview.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/providers.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/reset.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/tools.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/commands/validate.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/controllers/__init__.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/controllers/download_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/controllers/generation_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/controllers/mcp_provider_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/controllers/model_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/controllers/plugin_catalog_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/controllers/provider_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/controllers/tool_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/forms/__init__.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/forms/builder.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/forms/field.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/forms/form.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/forms/mcp_provider_builder.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/forms/model_builder.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/forms/provider_builder.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/forms/tool_builder.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/lazy_group.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/main.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/plugin_catalog.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/repositories/__init__.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/repositories/base.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/repositories/mcp_provider_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/repositories/model_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/repositories/persona_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/repositories/plugin_catalog_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/repositories/provider_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/repositories/tool_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/runtime.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/services/__init__.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/services/download_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/services/mcp_provider_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/services/model_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/services/plugin_catalog_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/services/plugin_install_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/services/provider_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/services/tool_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/ui.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/utils/__init__.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/utils/agent_introspection.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/utils/agent_text_formatter.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/utils/config_loader.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/utils/sample_records_pager.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/cli/version_notice.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/integrations/huggingface/__init__.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/integrations/huggingface/dataset_card.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/integrations/huggingface/dataset_card_template.md +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/integrations/opentelemetry.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/interface/errors.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/interface/results.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_agent_command.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_check_models_command.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_download_command.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_list_command.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_models_command.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_plugin_command.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_preview_command.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_providers_command.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_reset_command.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/commands/test_validate_command.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/conftest.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/controllers/test_download_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/controllers/test_mcp_provider_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/controllers/test_model_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/controllers/test_plugin_catalog_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/controllers/test_provider_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/controllers/test_tool_controller.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/fixtures/upstream-catalogs/catalog-invalid-install.json +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/fixtures/upstream-catalogs/catalog-unsupported-version.json +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/fixtures/upstream-catalogs/catalog-valid.json +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/forms/test_field.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/forms/test_form.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/forms/test_mcp_provider_builder.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/forms/test_model_builder.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/forms/test_provider_builder.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/forms/test_tool_builder.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/repositories/test_mcp_provider_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/repositories/test_model_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/repositories/test_persona_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/repositories/test_plugin_catalog_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/repositories/test_provider_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/repositories/test_tool_repository.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/services/test_download_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/services/test_mcp_provider_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/services/test_model_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/services/test_plugin_catalog_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/services/test_plugin_install_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/services/test_provider_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/services/test_tool_service.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/test_cli_utils.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/test_main.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/test_plugin_catalog.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/test_runtime.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/test_version_notice.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/utils/test_agent_introspection.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/utils/test_agent_introspection_integration.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/utils/test_agent_text_formatter.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/utils/test_sample_records_pager.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/conftest.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/docs/test_build_notebooks_cached.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/docs/test_docs_workflows.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/docs/test_document_review_gate_recipe.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/docs/test_fern_notebook_snapshot.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/docs/test_fern_published_branch.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/docs/test_image_generation_recipes.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/integrations/huggingface/__init__.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/integrations/huggingface/test_client.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/integrations/huggingface/test_dataset_card.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/integrations/test_opentelemetry.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/interface/test_acreate.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/interface/test_results.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/test_agentic_ci_attempts.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/test_dependency_audit.py +0 -0
- {data_designer-0.9.2 → data_designer-0.9.4}/tests/test_import_perf.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: data-designer
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.4
|
|
4
4
|
Summary: General framework for synthetic data generation
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -15,14 +15,14 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
15
15
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
16
|
Classifier: Topic :: Software Development
|
|
17
17
|
Requires-Python: >=3.10
|
|
18
|
-
Requires-Dist: data-designer-config==0.9.
|
|
19
|
-
Requires-Dist: data-designer-engine==0.9.
|
|
18
|
+
Requires-Dist: data-designer-config==0.9.4
|
|
19
|
+
Requires-Dist: data-designer-engine==0.9.4
|
|
20
20
|
Requires-Dist: huggingface-hub<2,>=1.0.1
|
|
21
21
|
Requires-Dist: opentelemetry-api<1.44,>=1.43
|
|
22
22
|
Requires-Dist: opentelemetry-exporter-prometheus<0.65,>=0.64b0
|
|
23
23
|
Requires-Dist: opentelemetry-sdk<1.44,>=1.43
|
|
24
24
|
Requires-Dist: packaging<27,>=25
|
|
25
|
-
Requires-Dist: pandas<
|
|
25
|
+
Requires-Dist: pandas<4,>=2.3.3
|
|
26
26
|
Requires-Dist: prometheus-client<1,>=0.24
|
|
27
27
|
Requires-Dist: prompt-toolkit<4,>=3.0.0
|
|
28
28
|
Requires-Dist: pyarrow<25,>=24
|
|
@@ -173,7 +173,7 @@ After installation, invoke the `data-designer` skill or describe the dataset you
|
|
|
173
173
|
|
|
174
174
|
This repository supports agent-assisted development — see [CONTRIBUTING.md](CONTRIBUTING.md) for the recommended workflow.
|
|
175
175
|
|
|
176
|
-
- **[Contributing Guide](CONTRIBUTING.md)** – How to contribute, including agent-assisted workflows
|
|
176
|
+
- **[Contributing Guide](CONTRIBUTING.md)** – How to contribute, including issue triage before external PRs and agent-assisted workflows
|
|
177
177
|
- **[GitHub Issues](https://github.com/NVIDIA-NeMo/DataDesigner/issues)** – Report bugs or make a feature request
|
|
178
178
|
|
|
179
179
|
---
|
|
@@ -139,7 +139,7 @@ After installation, invoke the `data-designer` skill or describe the dataset you
|
|
|
139
139
|
|
|
140
140
|
This repository supports agent-assisted development — see [CONTRIBUTING.md](CONTRIBUTING.md) for the recommended workflow.
|
|
141
141
|
|
|
142
|
-
- **[Contributing Guide](CONTRIBUTING.md)** – How to contribute, including agent-assisted workflows
|
|
142
|
+
- **[Contributing Guide](CONTRIBUTING.md)** – How to contribute, including issue triage before external PRs and agent-assisted workflows
|
|
143
143
|
- **[GitHub Issues](https://github.com/NVIDIA-NeMo/DataDesigner/issues)** – Report bugs or make a feature request
|
|
144
144
|
|
|
145
145
|
---
|
|
@@ -47,7 +47,7 @@ dependencies = [
|
|
|
47
47
|
"opentelemetry-exporter-prometheus>=0.64b0,<0.65",
|
|
48
48
|
"opentelemetry-sdk>=1.43,<1.44",
|
|
49
49
|
"packaging>=25,<27",
|
|
50
|
-
"pandas>=2.3.3,<
|
|
50
|
+
"pandas>=2.3.3,<4",
|
|
51
51
|
"prometheus-client>=0.24,<1",
|
|
52
52
|
"prompt-toolkit>=3.0.0,<4",
|
|
53
53
|
"pyarrow>=24,<25",
|
{data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/integrations/huggingface/client.py
RENAMED
|
@@ -119,7 +119,7 @@ class HuggingFaceHubClient:
|
|
|
119
119
|
URL to the uploaded dataset
|
|
120
120
|
|
|
121
121
|
Raises:
|
|
122
|
-
|
|
122
|
+
HuggingFaceHubClientUploadError: If validation fails or upload encounters errors
|
|
123
123
|
"""
|
|
124
124
|
logger.info(f"🤗 Uploading dataset to Hugging Face Hub: {repo_id}")
|
|
125
125
|
|
|
@@ -162,7 +162,7 @@ class HuggingFaceHubClient:
|
|
|
162
162
|
private: Whether to create private repo
|
|
163
163
|
|
|
164
164
|
Raises:
|
|
165
|
-
|
|
165
|
+
HuggingFaceHubClientUploadError: If repository creation fails
|
|
166
166
|
"""
|
|
167
167
|
logger.info(f"{LOG_INDENT}{RandomEmoji.working()} Checking if repository exists...")
|
|
168
168
|
try:
|
|
@@ -203,7 +203,7 @@ class HuggingFaceHubClient:
|
|
|
203
203
|
parquet_folder: Path to folder containing parquet files
|
|
204
204
|
|
|
205
205
|
Raises:
|
|
206
|
-
|
|
206
|
+
HuggingFaceHubClientUploadError: If upload fails
|
|
207
207
|
"""
|
|
208
208
|
logger.info(f"{LOG_INDENT}{RandomEmoji.loading()} Uploading main dataset files...")
|
|
209
209
|
try:
|
|
@@ -225,7 +225,7 @@ class HuggingFaceHubClient:
|
|
|
225
225
|
images_folder: Path to images folder
|
|
226
226
|
|
|
227
227
|
Raises:
|
|
228
|
-
|
|
228
|
+
HuggingFaceHubClientUploadError: If upload fails
|
|
229
229
|
"""
|
|
230
230
|
if not images_folder.exists():
|
|
231
231
|
return
|
|
@@ -255,7 +255,7 @@ class HuggingFaceHubClient:
|
|
|
255
255
|
processors_folder: Path to folder containing processor output directories
|
|
256
256
|
|
|
257
257
|
Raises:
|
|
258
|
-
|
|
258
|
+
HuggingFaceHubClientUploadError: If upload fails
|
|
259
259
|
"""
|
|
260
260
|
if not processors_folder.exists():
|
|
261
261
|
return
|
|
@@ -290,7 +290,7 @@ class HuggingFaceHubClient:
|
|
|
290
290
|
builder_config_path: Path to builder_config.json file
|
|
291
291
|
|
|
292
292
|
Raises:
|
|
293
|
-
|
|
293
|
+
HuggingFaceHubClientUploadError: If upload fails
|
|
294
294
|
"""
|
|
295
295
|
logger.info(f"{LOG_INDENT}{RandomEmoji.loading()} Uploading configuration files...")
|
|
296
296
|
|
|
@@ -345,7 +345,7 @@ class HuggingFaceHubClient:
|
|
|
345
345
|
tags: Additional custom tags for the dataset
|
|
346
346
|
|
|
347
347
|
Raises:
|
|
348
|
-
|
|
348
|
+
HuggingFaceHubClientUploadError: If dataset card generation or upload fails
|
|
349
349
|
"""
|
|
350
350
|
try:
|
|
351
351
|
with open(metadata_path) as f:
|
|
@@ -455,7 +455,7 @@ class HuggingFaceHubClient:
|
|
|
455
455
|
base_dataset_path: Path to dataset directory
|
|
456
456
|
|
|
457
457
|
Raises:
|
|
458
|
-
|
|
458
|
+
HuggingFaceHubClientUploadError: If directory structure is invalid
|
|
459
459
|
"""
|
|
460
460
|
if not base_dataset_path.exists():
|
|
461
461
|
raise HuggingFaceHubClientUploadError(f"Dataset path does not exist: {base_dataset_path}")
|
|
@@ -22,19 +22,41 @@ if TYPE_CHECKING:
|
|
|
22
22
|
DataDesignerWorkflowError,
|
|
23
23
|
)
|
|
24
24
|
from data_designer.interface.results import DatasetCreationResults # noqa: F401
|
|
25
|
+
from data_designer.interface.workflow_metadata import ( # noqa: F401
|
|
26
|
+
CompletedWorkflowStageMetadata,
|
|
27
|
+
FailedWorkflowStageMetadata,
|
|
28
|
+
RunningWorkflowStageMetadata,
|
|
29
|
+
SkippedWorkflowStageMetadata,
|
|
30
|
+
WorkflowMetadata,
|
|
31
|
+
WorkflowStageMetadata,
|
|
32
|
+
validate_workflow_stage_metadata,
|
|
33
|
+
)
|
|
25
34
|
|
|
26
35
|
_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
|
|
27
36
|
"CompositeWorkflow": ("data_designer.interface.composite_workflow", "CompositeWorkflow"),
|
|
28
37
|
"CompositeWorkflowResults": ("data_designer.interface.composite_workflow", "CompositeWorkflowResults"),
|
|
38
|
+
"CompletedWorkflowStageMetadata": (
|
|
39
|
+
"data_designer.interface.workflow_metadata",
|
|
40
|
+
"CompletedWorkflowStageMetadata",
|
|
41
|
+
),
|
|
29
42
|
"DataDesigner": ("data_designer.interface.data_designer", "DataDesigner"),
|
|
30
43
|
"DataDesignerEarlyShutdownError": ("data_designer.interface.errors", "DataDesignerEarlyShutdownError"),
|
|
31
44
|
"DataDesignerGenerationError": ("data_designer.interface.errors", "DataDesignerGenerationError"),
|
|
32
45
|
"DataDesignerProfilingError": ("data_designer.interface.errors", "DataDesignerProfilingError"),
|
|
33
46
|
"DataDesignerWorkflowError": ("data_designer.interface.errors", "DataDesignerWorkflowError"),
|
|
34
47
|
"DatasetCreationResults": ("data_designer.interface.results", "DatasetCreationResults"),
|
|
48
|
+
"FailedWorkflowStageMetadata": ("data_designer.interface.workflow_metadata", "FailedWorkflowStageMetadata"),
|
|
35
49
|
"ResumeMode": ("data_designer.config.run_config", "ResumeMode"),
|
|
50
|
+
"RunningWorkflowStageMetadata": ("data_designer.interface.workflow_metadata", "RunningWorkflowStageMetadata"),
|
|
36
51
|
"SkippedStageResult": ("data_designer.interface.composite_workflow", "SkippedStageResult"),
|
|
37
52
|
"SkippedStageStatus": ("data_designer.interface.composite_workflow", "SkippedStageStatus"),
|
|
53
|
+
"SkippedWorkflowStageMetadata": ("data_designer.interface.workflow_metadata", "SkippedWorkflowStageMetadata"),
|
|
54
|
+
"WorkflowMetadata": ("data_designer.interface.workflow_metadata", "WorkflowMetadata"),
|
|
55
|
+
"WorkflowStageMetadata": ("data_designer.interface.workflow_metadata", "WorkflowStageMetadata"),
|
|
56
|
+
"validate_workflow_stage_metadata": (
|
|
57
|
+
"data_designer.interface.workflow_metadata",
|
|
58
|
+
"validate_workflow_stage_metadata",
|
|
59
|
+
),
|
|
38
60
|
}
|
|
39
61
|
|
|
40
62
|
__all__ = list(_LAZY_IMPORTS.keys())
|
{data_designer-0.9.2 → data_designer-0.9.4}/src/data_designer/interface/composite_workflow.py
RENAMED
|
@@ -40,6 +40,7 @@ from data_designer.interface.results import (
|
|
|
40
40
|
_export_jsonl,
|
|
41
41
|
_export_parquet,
|
|
42
42
|
)
|
|
43
|
+
from data_designer.interface.workflow_metadata import WorkflowMetadata, validate_workflow_stage_metadata
|
|
43
44
|
|
|
44
45
|
if TYPE_CHECKING:
|
|
45
46
|
import pandas as pd
|
|
@@ -219,8 +220,19 @@ class CompositeWorkflow:
|
|
|
219
220
|
_validate_dir_name(name, "stage name")
|
|
220
221
|
if any(stage.name == name for stage in self._stages):
|
|
221
222
|
raise DataDesignerWorkflowError(f"Stage name {name!r} is already used in workflow {self.name!r}.")
|
|
222
|
-
if num_records is not None
|
|
223
|
-
|
|
223
|
+
if num_records is not None:
|
|
224
|
+
if not isinstance(num_records, int) or isinstance(num_records, bool):
|
|
225
|
+
raise DataDesignerWorkflowError("Stage num_records must be an integer.")
|
|
226
|
+
if num_records < 1:
|
|
227
|
+
raise DataDesignerWorkflowError("Stage num_records must be at least 1.")
|
|
228
|
+
if on_success_version is not None and not isinstance(on_success_version, str):
|
|
229
|
+
raise DataDesignerWorkflowError("Stage on_success_version must be a string.")
|
|
230
|
+
if not isinstance(allow_empty, bool):
|
|
231
|
+
raise DataDesignerWorkflowError("Stage allow_empty must be a boolean.")
|
|
232
|
+
if not isinstance(sampling_strategy, SamplingStrategy):
|
|
233
|
+
raise DataDesignerWorkflowError("Stage sampling_strategy must be a SamplingStrategy.")
|
|
234
|
+
if selection_strategy is not None and not isinstance(selection_strategy, (IndexRange, PartitionBlock)):
|
|
235
|
+
raise DataDesignerWorkflowError("Stage selection_strategy must be an IndexRange or PartitionBlock.")
|
|
224
236
|
_validate_stage_output(output)
|
|
225
237
|
output_processors = output_processors or []
|
|
226
238
|
_validate_distinct_output_processors(config_builder, output_processors)
|
|
@@ -276,6 +288,7 @@ class CompositeWorkflow:
|
|
|
276
288
|
workflow_path.mkdir(parents=True, exist_ok=True)
|
|
277
289
|
prior_metadata = _read_prior_workflow_metadata(workflow_path, self.name, resume)
|
|
278
290
|
metadata: dict[str, Any] = {
|
|
291
|
+
**(prior_metadata or {}),
|
|
279
292
|
"name": self.name,
|
|
280
293
|
"library_version": get_library_version(),
|
|
281
294
|
"stages": [],
|
|
@@ -388,6 +401,10 @@ class CompositeWorkflow:
|
|
|
388
401
|
f"Cannot resume workflow {self.name!r}: stage {stage.name!r} is not reusable."
|
|
389
402
|
)
|
|
390
403
|
|
|
404
|
+
if stage_resume == ResumeMode.ALWAYS and prior_stage_metadata is not None:
|
|
405
|
+
prior_stage = validate_workflow_stage_metadata(prior_stage_metadata)
|
|
406
|
+
stage_metadata.update(prior_stage.model_extra or {})
|
|
407
|
+
|
|
391
408
|
if stage_resume == ResumeMode.NEVER and stage_path.exists():
|
|
392
409
|
shutil.rmtree(stage_path)
|
|
393
410
|
|
|
@@ -598,6 +615,22 @@ def _read_prior_workflow_metadata(
|
|
|
598
615
|
raise DataDesignerWorkflowError(
|
|
599
616
|
f"Cannot resume workflow {workflow_name!r}: workflow metadata has invalid shape."
|
|
600
617
|
)
|
|
618
|
+
try:
|
|
619
|
+
validated_metadata = WorkflowMetadata.model_validate(metadata)
|
|
620
|
+
except ValidationError as exc:
|
|
621
|
+
if resume != ResumeMode.ALWAYS:
|
|
622
|
+
error = exc.errors(include_url=False, include_input=False)[0]
|
|
623
|
+
location = ".".join(str(part) for part in error["loc"])
|
|
624
|
+
logger.warning(
|
|
625
|
+
"Workflow metadata for %r has invalid field %s (%s); starting fresh.",
|
|
626
|
+
workflow_name,
|
|
627
|
+
location,
|
|
628
|
+
error["msg"],
|
|
629
|
+
)
|
|
630
|
+
return None
|
|
631
|
+
raise DataDesignerWorkflowError(
|
|
632
|
+
f"Cannot resume workflow {workflow_name!r}: workflow metadata has invalid shape."
|
|
633
|
+
) from exc
|
|
601
634
|
if metadata.get("name") != workflow_name:
|
|
602
635
|
if resume != ResumeMode.ALWAYS:
|
|
603
636
|
logger.warning("Workflow metadata for %r has a different name; starting fresh.", workflow_name)
|
|
@@ -605,7 +638,7 @@ def _read_prior_workflow_metadata(
|
|
|
605
638
|
raise DataDesignerWorkflowError(
|
|
606
639
|
f"Cannot resume workflow {workflow_name!r}: workflow metadata name does not match."
|
|
607
640
|
)
|
|
608
|
-
return
|
|
641
|
+
return validated_metadata.model_dump(mode="json", exclude_unset=True)
|
|
609
642
|
|
|
610
643
|
|
|
611
644
|
def _get_prior_stage_metadata(
|
|
@@ -870,9 +903,13 @@ def _parquet_files(path: Path) -> list[Path]:
|
|
|
870
903
|
def _write_workflow_metadata(workflow_path: Path, metadata: dict[str, Any]) -> None:
|
|
871
904
|
path = workflow_path / WORKFLOW_METADATA_FILENAME
|
|
872
905
|
tmp_path = path.with_name(f"{path.name}.tmp.{os.getpid()}.{uuid.uuid4().hex}")
|
|
906
|
+
try:
|
|
907
|
+
validated_metadata = WorkflowMetadata.model_validate(metadata)
|
|
908
|
+
except ValidationError as exc:
|
|
909
|
+
raise DataDesignerWorkflowError("Workflow metadata has invalid shape.") from exc
|
|
873
910
|
try:
|
|
874
911
|
with tmp_path.open("w", encoding="utf-8") as f:
|
|
875
|
-
json.dump(
|
|
912
|
+
json.dump(validated_metadata.model_dump(mode="json", exclude_unset=True), f, indent=2, sort_keys=True)
|
|
876
913
|
f.flush()
|
|
877
914
|
os.fsync(f.fileno())
|
|
878
915
|
os.replace(tmp_path, path)
|
|
@@ -354,11 +354,10 @@ class DataDesigner(DataDesignerInterface[DatasetCreationResults]):
|
|
|
354
354
|
)
|
|
355
355
|
|
|
356
356
|
try:
|
|
357
|
-
profiler_column_configs = config_builder.get_column_configs() or builder.data_designer_config.columns
|
|
358
357
|
profiler = self._create_dataset_profiler(
|
|
359
358
|
config_builder,
|
|
360
359
|
resource_provider,
|
|
361
|
-
column_configs=
|
|
360
|
+
column_configs=builder.data_designer_config.columns,
|
|
362
361
|
)
|
|
363
362
|
analysis = profiler.profile_dataset(num_records, dataset_for_profiler)
|
|
364
363
|
except Exception as e:
|
|
@@ -457,7 +456,11 @@ class DataDesigner(DataDesignerInterface[DatasetCreationResults]):
|
|
|
457
456
|
dataset_for_profiler = processed_dataset
|
|
458
457
|
|
|
459
458
|
try:
|
|
460
|
-
profiler = self._create_dataset_profiler(
|
|
459
|
+
profiler = self._create_dataset_profiler(
|
|
460
|
+
config_builder,
|
|
461
|
+
resource_provider,
|
|
462
|
+
column_configs=builder.data_designer_config.columns,
|
|
463
|
+
)
|
|
461
464
|
analysis = profiler.profile_dataset(num_records, dataset_for_profiler)
|
|
462
465
|
except Exception as e:
|
|
463
466
|
raise DataDesignerProfilingError(f"🛑 Error profiling preview dataset: {e}") from e
|
|
@@ -623,11 +626,7 @@ class DataDesigner(DataDesignerInterface[DatasetCreationResults]):
|
|
|
623
626
|
"""Get the runtime configuration applied to dataset generation.
|
|
624
627
|
|
|
625
628
|
Returns:
|
|
626
|
-
The active RunConfig instance.
|
|
627
|
-
some fields on construction (e.g., ``shutdown_error_rate`` becomes
|
|
628
|
-
``1.0`` when ``disable_early_shutdown=True``), so the returned
|
|
629
|
-
object may not exactly equal the one originally passed to
|
|
630
|
-
``set_run_config``.
|
|
629
|
+
The active RunConfig instance.
|
|
631
630
|
"""
|
|
632
631
|
return self._run_config
|
|
633
632
|
|
|
@@ -0,0 +1,97 @@
|
|
|
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 typing import Annotated, Any, Literal, TypeAlias
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field, TypeAdapter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _WorkflowStageMetadataBase(BaseModel):
|
|
12
|
+
"""Common metadata for a composite workflow stage."""
|
|
13
|
+
|
|
14
|
+
model_config = ConfigDict(extra="allow")
|
|
15
|
+
|
|
16
|
+
status: Literal["running", "failed", "completed", "completed_empty", "skipped_empty_upstream"]
|
|
17
|
+
index: int
|
|
18
|
+
name: str
|
|
19
|
+
stage_dir: str
|
|
20
|
+
depends_on: list[str]
|
|
21
|
+
allow_empty: bool
|
|
22
|
+
on_success_version: str | None
|
|
23
|
+
output_processors: list[dict[str, Any]]
|
|
24
|
+
output: str
|
|
25
|
+
sampling_strategy: str
|
|
26
|
+
selection_strategy: dict[str, Any] | None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _StartedWorkflowStageMetadata(_WorkflowStageMetadataBase):
|
|
30
|
+
fingerprint: str
|
|
31
|
+
num_records_requested: int
|
|
32
|
+
seeded_from_stage: str | None
|
|
33
|
+
seed_path: str | None
|
|
34
|
+
config: dict[str, Any]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class RunningWorkflowStageMetadata(_StartedWorkflowStageMetadata):
|
|
38
|
+
"""Metadata for a running workflow stage."""
|
|
39
|
+
|
|
40
|
+
status: Literal["running"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class FailedWorkflowStageMetadata(_StartedWorkflowStageMetadata):
|
|
44
|
+
"""Metadata for a failed workflow stage."""
|
|
45
|
+
|
|
46
|
+
status: Literal["failed"]
|
|
47
|
+
duration_sec: float | None = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CompletedWorkflowStageMetadata(_StartedWorkflowStageMetadata):
|
|
51
|
+
"""Metadata for a completed workflow stage."""
|
|
52
|
+
|
|
53
|
+
status: Literal["completed", "completed_empty"]
|
|
54
|
+
num_records_actual: int
|
|
55
|
+
output_records: int
|
|
56
|
+
output_seed_path: str
|
|
57
|
+
callback_output_path: str | None
|
|
58
|
+
stage_output_override_path: str | None = None
|
|
59
|
+
output_processor_output_path: str | None
|
|
60
|
+
duration_sec: float
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class SkippedWorkflowStageMetadata(_WorkflowStageMetadataBase):
|
|
64
|
+
"""Metadata for a stage skipped after an empty upstream stage."""
|
|
65
|
+
|
|
66
|
+
status: Literal["skipped_empty_upstream"]
|
|
67
|
+
upstream_stage: str
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
WorkflowStageMetadata: TypeAlias = Annotated[
|
|
71
|
+
RunningWorkflowStageMetadata
|
|
72
|
+
| FailedWorkflowStageMetadata
|
|
73
|
+
| CompletedWorkflowStageMetadata
|
|
74
|
+
| SkippedWorkflowStageMetadata,
|
|
75
|
+
Field(discriminator="status"),
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
_WORKFLOW_STAGE_METADATA_ADAPTER = TypeAdapter(WorkflowStageMetadata)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def validate_workflow_stage_metadata(value: object) -> WorkflowStageMetadata:
|
|
82
|
+
"""Validate the stage union and return its concrete status model.
|
|
83
|
+
|
|
84
|
+
Use the concrete status models for runtime type checks.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
return _WORKFLOW_STAGE_METADATA_ADAPTER.validate_python(value)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class WorkflowMetadata(BaseModel):
|
|
91
|
+
"""Metadata persisted for a composite workflow run."""
|
|
92
|
+
|
|
93
|
+
model_config = ConfigDict(extra="allow")
|
|
94
|
+
|
|
95
|
+
name: str
|
|
96
|
+
library_version: str
|
|
97
|
+
stages: list[WorkflowStageMetadata]
|
|
@@ -3,11 +3,20 @@
|
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
from pathlib import Path
|
|
6
7
|
from unittest.mock import MagicMock, patch
|
|
7
8
|
|
|
9
|
+
import pytest
|
|
10
|
+
from typer.testing import CliRunner
|
|
11
|
+
|
|
8
12
|
from data_designer.cli.commands.create import create_command
|
|
13
|
+
from data_designer.cli.main import app
|
|
14
|
+
from data_designer.config.run_config import RunConfig
|
|
9
15
|
from data_designer.engine.storage.artifact_storage import ResumeMode
|
|
10
16
|
|
|
17
|
+
runner = CliRunner()
|
|
18
|
+
_CTRL = "data_designer.cli.controllers.generation_controller"
|
|
19
|
+
|
|
11
20
|
# ---------------------------------------------------------------------------
|
|
12
21
|
# create_command delegation tests
|
|
13
22
|
# ---------------------------------------------------------------------------
|
|
@@ -229,3 +238,48 @@ def test_create_command_passes_tui_override(mock_ctrl_cls: MagicMock) -> None:
|
|
|
229
238
|
tui=False,
|
|
230
239
|
script_args=None,
|
|
231
240
|
)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# ---------------------------------------------------------------------------
|
|
244
|
+
# create --run-config overlay tests
|
|
245
|
+
# ---------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@pytest.mark.parametrize(
|
|
249
|
+
("yaml_text", "expected_rate", "expected_effective_rate"),
|
|
250
|
+
[
|
|
251
|
+
("disable_early_shutdown: true\n", 0.3, 1.0),
|
|
252
|
+
("disable_early_shutdown: true\nshutdown_error_rate: 0.45\n", 0.45, 1.0),
|
|
253
|
+
("shutdown_error_rate: 0.45\n", 0.45, 0.45),
|
|
254
|
+
],
|
|
255
|
+
ids=["flag-only-keeps-baseline-rate", "flag-with-explicit-rate", "rate-only"],
|
|
256
|
+
)
|
|
257
|
+
@patch(f"{_CTRL}.DataDesigner")
|
|
258
|
+
@patch(f"{_CTRL}.load_config_builder")
|
|
259
|
+
def test_create_run_config_overlay_preserves_shutdown_error_rate(
|
|
260
|
+
mock_load_config: MagicMock,
|
|
261
|
+
mock_dd_cls: MagicMock,
|
|
262
|
+
tmp_path: Path,
|
|
263
|
+
yaml_text: str,
|
|
264
|
+
expected_rate: float,
|
|
265
|
+
expected_effective_rate: float,
|
|
266
|
+
) -> None:
|
|
267
|
+
"""`create --run-config` overlays the YAML file without discarding the baseline error rate."""
|
|
268
|
+
run_config_file = tmp_path / "run-config.yaml"
|
|
269
|
+
run_config_file.write_text(yaml_text)
|
|
270
|
+
|
|
271
|
+
mock_load_config.return_value = MagicMock()
|
|
272
|
+
mock_results = MagicMock()
|
|
273
|
+
mock_results.count_records.return_value = 10
|
|
274
|
+
mock_results.load_analysis.return_value = None
|
|
275
|
+
mock_dd = MagicMock()
|
|
276
|
+
mock_dd.run_config = RunConfig(shutdown_error_rate=0.3)
|
|
277
|
+
mock_dd.create.return_value = mock_results
|
|
278
|
+
mock_dd_cls.return_value = mock_dd
|
|
279
|
+
|
|
280
|
+
result = runner.invoke(app, ["create", "dataset.yaml", "--run-config", str(run_config_file)])
|
|
281
|
+
|
|
282
|
+
assert result.exit_code == 0, result.output
|
|
283
|
+
effective = mock_dd.set_run_config.call_args.args[0]
|
|
284
|
+
assert effective.shutdown_error_rate == expected_rate
|
|
285
|
+
assert effective.effective_shutdown_error_rate == expected_effective_rate
|
{data_designer-0.9.2 → data_designer-0.9.4}/tests/cli/controllers/test_generation_controller.py
RENAMED
|
@@ -866,7 +866,7 @@ def test_run_create_applies_run_config_precedence(
|
|
|
866
866
|
}
|
|
867
867
|
)
|
|
868
868
|
mock_dd = MagicMock()
|
|
869
|
-
mock_dd.run_config = RunConfig(buffer_size=64, progress_interval=11.0, display_tui=True)
|
|
869
|
+
mock_dd.run_config = RunConfig(buffer_size=64, progress_interval=11.0, display_tui=True, shutdown_error_rate=0.3)
|
|
870
870
|
mock_dd.create.return_value = _make_mock_create_results()
|
|
871
871
|
mock_dd_cls.return_value = mock_dd
|
|
872
872
|
|
|
@@ -886,7 +886,8 @@ def test_run_create_applies_run_config_precedence(
|
|
|
886
886
|
assert effective.buffer_size == 1000
|
|
887
887
|
assert effective.progress_interval == 11.0
|
|
888
888
|
assert effective.preserve_dropped_columns is False
|
|
889
|
-
assert effective.shutdown_error_rate ==
|
|
889
|
+
assert effective.shutdown_error_rate == 0.3
|
|
890
|
+
assert effective.effective_shutdown_error_rate == 1.0
|
|
890
891
|
assert effective.display_tui is False
|
|
891
892
|
assert [item[0] for item in mock_dd.method_calls] == ["set_run_config", "create"]
|
|
892
893
|
assert "Run config: run.yaml" in capsys.readouterr().out
|
|
@@ -68,13 +68,23 @@ def test_load_run_config_accepts_empty_mapping(tmp_path: Path) -> None:
|
|
|
68
68
|
assert loaded.model_dump(exclude_unset=True) == {}
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
@pytest.mark.parametrize(
|
|
72
|
+
("yaml_text", "expected"),
|
|
73
|
+
[
|
|
74
|
+
("buffer_size: 250\ndisplay_tui: true\n", {"buffer_size": 250, "display_tui": True}),
|
|
75
|
+
("disable_early_shutdown: true\n", {"disable_early_shutdown": True}),
|
|
76
|
+
],
|
|
77
|
+
ids=["buffer-size-and-tui", "disable-early-shutdown-only"],
|
|
78
|
+
)
|
|
79
|
+
def test_load_run_config_preserves_explicit_partial_fields(
|
|
80
|
+
tmp_path: Path, yaml_text: str, expected: dict[str, object]
|
|
81
|
+
) -> None:
|
|
72
82
|
run_config_file = tmp_path / "run-config.yaml"
|
|
73
|
-
run_config_file.write_text(
|
|
83
|
+
run_config_file.write_text(yaml_text)
|
|
74
84
|
|
|
75
85
|
loaded = load_run_config(str(run_config_file))
|
|
76
86
|
|
|
77
|
-
assert loaded.model_dump(exclude_unset=True) ==
|
|
87
|
+
assert loaded.model_dump(exclude_unset=True) == expected
|
|
78
88
|
|
|
79
89
|
|
|
80
90
|
def test_load_run_config_preserves_partial_nested_fields(tmp_path: Path) -> None:
|
|
@@ -0,0 +1,136 @@
|
|
|
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 importlib.util
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from types import ModuleType
|
|
9
|
+
from unittest.mock import MagicMock
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
REPO_ROOT = Path(__file__).resolve().parents[4]
|
|
14
|
+
RECIPE_PATH = REPO_ROOT / "docs" / "assets" / "recipes" / "chart_qa" / "chart_qa.py"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _load_recipe() -> ModuleType:
|
|
18
|
+
spec = importlib.util.spec_from_file_location("chart_qa_recipe", RECIPE_PATH)
|
|
19
|
+
assert spec is not None
|
|
20
|
+
assert spec.loader is not None
|
|
21
|
+
module = importlib.util.module_from_spec(spec)
|
|
22
|
+
spec.loader.exec_module(module)
|
|
23
|
+
return module
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.fixture(scope="module")
|
|
27
|
+
def recipe() -> ModuleType:
|
|
28
|
+
return _load_recipe()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.fixture
|
|
32
|
+
def seed_path(tmp_path: Path) -> Path:
|
|
33
|
+
path = tmp_path / "chart_seed.jsonl"
|
|
34
|
+
path.write_text('{"image_url": "https://example.com/chart.png"}\n', encoding="utf-8")
|
|
35
|
+
return path
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize("prompt_version", ["level1", "level2"])
|
|
39
|
+
def test_chart_prompt_versions_match_pipeline_config(recipe: ModuleType, seed_path: Path, prompt_version: str) -> None:
|
|
40
|
+
config = recipe.build_config(seed_path=str(seed_path), prompt_version=prompt_version).build()
|
|
41
|
+
columns = {column.name: column for column in config.columns}
|
|
42
|
+
models = {model.alias: model for model in config.model_configs}
|
|
43
|
+
|
|
44
|
+
assert list(columns) == ["question_type", "question", "answer", "quality_check"]
|
|
45
|
+
assert columns["question_type"].params.values == list(recipe.QUESTION_TYPES)
|
|
46
|
+
assert columns["question_type"].params.weights == list(recipe.QUESTION_TYPE_WEIGHTS)
|
|
47
|
+
assert columns["question"].model_alias == "chart-vlm-question"
|
|
48
|
+
assert columns["answer"].model_alias == "chart-vlm"
|
|
49
|
+
assert columns["quality_check"].model_alias == "chart-vlm-quality"
|
|
50
|
+
assert columns["answer"].extract_reasoning_content is True
|
|
51
|
+
assert columns["question"].multi_modal_context[0].column_name == "image_url"
|
|
52
|
+
assert columns["question"].multi_modal_context[0].data_type == "url"
|
|
53
|
+
assert columns["question"].multi_modal_context[0].image_format is None
|
|
54
|
+
assert columns["answer"].multi_modal_context == columns["question"].multi_modal_context
|
|
55
|
+
assert columns["quality_check"].multi_modal_context == columns["question"].multi_modal_context
|
|
56
|
+
assert "{{ answer__reasoning_content }}" in columns["quality_check"].prompt
|
|
57
|
+
assert config.seed_config.sampling_strategy == recipe.dd.SamplingStrategy.ORDERED
|
|
58
|
+
assert list(models) == ["chart-vlm", "chart-vlm-question", "chart-vlm-quality"]
|
|
59
|
+
|
|
60
|
+
for model in models.values():
|
|
61
|
+
inference = model.inference_parameters
|
|
62
|
+
assert model.model == recipe.DEFAULT_VLM_MODEL
|
|
63
|
+
assert model.provider == recipe.VLLM_PROVIDER_NAME
|
|
64
|
+
assert model.skip_health_check is False
|
|
65
|
+
assert inference.max_tokens == 50000
|
|
66
|
+
assert inference.timeout == 1200
|
|
67
|
+
assert inference.temperature == 0.6
|
|
68
|
+
assert inference.top_p == 0.95
|
|
69
|
+
assert inference.max_parallel_requests == 20
|
|
70
|
+
assert inference.extra_body == recipe.PRODUCTION_SAMPLING
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@pytest.mark.parametrize(
|
|
74
|
+
("prompt_version", "raw_prompts"),
|
|
75
|
+
[
|
|
76
|
+
(
|
|
77
|
+
"level1",
|
|
78
|
+
("LEVEL1_QUESTION_PROMPT", "LEVEL1_ANSWER_PROMPT", "LEVEL1_QUALITY_PROMPT"),
|
|
79
|
+
),
|
|
80
|
+
(
|
|
81
|
+
"level2",
|
|
82
|
+
(
|
|
83
|
+
"LEVEL2_QUESTION_PROMPT",
|
|
84
|
+
"LEVEL2_ANSWER_PROMPT",
|
|
85
|
+
"LEVEL2_QUALITY_PROMPT",
|
|
86
|
+
),
|
|
87
|
+
),
|
|
88
|
+
],
|
|
89
|
+
)
|
|
90
|
+
def test_prompt_pack_matches_runtime_whitespace_handling(
|
|
91
|
+
recipe: ModuleType, prompt_version: str, raw_prompts: tuple[str, str, str]
|
|
92
|
+
) -> None:
|
|
93
|
+
prompt_pack = recipe.get_prompt_pack(prompt_version)
|
|
94
|
+
|
|
95
|
+
assert tuple(prompt_pack) == tuple(getattr(recipe, name).rstrip() for name in raw_prompts)
|
|
96
|
+
assert all(not prompt.endswith(("\n", "\r", " ", "\t")) for prompt in prompt_pack)
|
|
97
|
+
|
|
98
|
+
if prompt_version == "level2":
|
|
99
|
+
assert "DIFFICULTY FLOOR (required)" in prompt_pack.question
|
|
100
|
+
assert "NO-ATTEMPT HANDLING" in prompt_pack.answer
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_unknown_prompt_version_is_rejected(recipe: ModuleType) -> None:
|
|
104
|
+
with pytest.raises(ValueError, match="Unknown prompt version"):
|
|
105
|
+
recipe.get_prompt_pack("level3")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_create_dataset_uses_production_provider_settings(
|
|
109
|
+
recipe: ModuleType, seed_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
110
|
+
) -> None:
|
|
111
|
+
config_builder = recipe.build_config(seed_path=str(seed_path))
|
|
112
|
+
dataset_results = MagicMock()
|
|
113
|
+
data_designer = MagicMock()
|
|
114
|
+
data_designer.create.return_value = dataset_results
|
|
115
|
+
data_designer_class = MagicMock(return_value=data_designer)
|
|
116
|
+
monkeypatch.setattr(recipe, "DataDesigner", data_designer_class)
|
|
117
|
+
|
|
118
|
+
result = recipe.create_dataset(
|
|
119
|
+
config_builder,
|
|
120
|
+
num_records=7,
|
|
121
|
+
vllm_endpoint="http://localhost:8000/v1",
|
|
122
|
+
prompt_version="level1",
|
|
123
|
+
artifact_path="artifacts",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
assert result is dataset_results
|
|
127
|
+
provider = data_designer_class.call_args.kwargs["model_providers"][0]
|
|
128
|
+
assert provider.name == "vllm-vl"
|
|
129
|
+
assert provider.endpoint == "http://localhost:8000/v1"
|
|
130
|
+
assert provider.provider_type == "openai"
|
|
131
|
+
assert provider.api_key is None
|
|
132
|
+
data_designer.create.assert_called_once_with(
|
|
133
|
+
config_builder,
|
|
134
|
+
num_records=7,
|
|
135
|
+
dataset_name="chart_qa_level1",
|
|
136
|
+
)
|