dataeval-flow 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. dataeval_flow/__init__.py +93 -0
  2. dataeval_flow/__main__.py +149 -0
  3. dataeval_flow/_app/__init__.py +5 -0
  4. dataeval_flow/_app/_model/__init__.py +5 -0
  5. dataeval_flow/_app/_model/_coerce.py +126 -0
  6. dataeval_flow/_app/_model/_discover.py +171 -0
  7. dataeval_flow/_app/_model/_execution.py +108 -0
  8. dataeval_flow/_app/_model/_introspect.py +280 -0
  9. dataeval_flow/_app/_model/_item.py +213 -0
  10. dataeval_flow/_app/_model/_registry.py +255 -0
  11. dataeval_flow/_app/_model/_state.py +322 -0
  12. dataeval_flow/_app/_model/_undo.py +61 -0
  13. dataeval_flow/_app/_panes/__init__.py +35 -0
  14. dataeval_flow/_app/_panes/_config_pane.py +173 -0
  15. dataeval_flow/_app/_panes/_result_pane.py +125 -0
  16. dataeval_flow/_app/_panes/_task_pane.py +91 -0
  17. dataeval_flow/_app/_panes/_widgets.py +111 -0
  18. dataeval_flow/_app/_screens/__init__.py +25 -0
  19. dataeval_flow/_app/_screens/_base.py +242 -0
  20. dataeval_flow/_app/_screens/_detail.py +333 -0
  21. dataeval_flow/_app/_screens/_model.py +102 -0
  22. dataeval_flow/_app/_screens/_params.py +80 -0
  23. dataeval_flow/_app/_screens/_pathpicker.py +68 -0
  24. dataeval_flow/_app/_screens/_section.py +621 -0
  25. dataeval_flow/_app/_screens/_settings.py +183 -0
  26. dataeval_flow/_app/_viewmodel/__init__.py +15 -0
  27. dataeval_flow/_app/_viewmodel/_builder_vm.py +272 -0
  28. dataeval_flow/_app/_viewmodel/_model_vm.py +70 -0
  29. dataeval_flow/_app/_viewmodel/_rendering.py +189 -0
  30. dataeval_flow/_app/_viewmodel/_result_vm.py +210 -0
  31. dataeval_flow/_app/_viewmodel/_section_vm.py +224 -0
  32. dataeval_flow/_app/app.py +742 -0
  33. dataeval_flow/_app/cli.py +592 -0
  34. dataeval_flow/_logging.py +102 -0
  35. dataeval_flow/cache.py +1355 -0
  36. dataeval_flow/config/__init__.py +80 -0
  37. dataeval_flow/config/_loader.py +79 -0
  38. dataeval_flow/config/_merge.py +92 -0
  39. dataeval_flow/config/_models.py +115 -0
  40. dataeval_flow/config/_paths.py +85 -0
  41. dataeval_flow/config/schemas/__init__.py +112 -0
  42. dataeval_flow/config/schemas/_dataset.py +111 -0
  43. dataeval_flow/config/schemas/_extractor.py +119 -0
  44. dataeval_flow/config/schemas/_metadata.py +28 -0
  45. dataeval_flow/config/schemas/_preprocessor.py +18 -0
  46. dataeval_flow/config/schemas/_selection.py +100 -0
  47. dataeval_flow/config/schemas/_task.py +89 -0
  48. dataeval_flow/config/schemas/_workflow.py +135 -0
  49. dataeval_flow/dataset.py +635 -0
  50. dataeval_flow/embeddings.py +135 -0
  51. dataeval_flow/metadata.py +48 -0
  52. dataeval_flow/preprocessing.py +141 -0
  53. dataeval_flow/py.typed +0 -0
  54. dataeval_flow/runner.py +118 -0
  55. dataeval_flow/selection.py +50 -0
  56. dataeval_flow/workflow/__init__.py +328 -0
  57. dataeval_flow/workflow/_text_report.py +511 -0
  58. dataeval_flow/workflow/base.py +69 -0
  59. dataeval_flow/workflow/orchestrator.py +454 -0
  60. dataeval_flow/workflows/__init__.py +1 -0
  61. dataeval_flow/workflows/analysis/__init__.py +38 -0
  62. dataeval_flow/workflows/analysis/outputs.py +202 -0
  63. dataeval_flow/workflows/analysis/params.py +114 -0
  64. dataeval_flow/workflows/analysis/workflow.py +1313 -0
  65. dataeval_flow/workflows/cleaning/__init__.py +23 -0
  66. dataeval_flow/workflows/cleaning/outputs.py +200 -0
  67. dataeval_flow/workflows/cleaning/params.py +160 -0
  68. dataeval_flow/workflows/cleaning/report.py +304 -0
  69. dataeval_flow/workflows/cleaning/workflow.py +794 -0
  70. dataeval_flow/workflows/drift/__init__.py +1 -0
  71. dataeval_flow/workflows/drift/outputs.py +144 -0
  72. dataeval_flow/workflows/drift/params.py +332 -0
  73. dataeval_flow/workflows/drift/report.py +201 -0
  74. dataeval_flow/workflows/drift/workflow.py +647 -0
  75. dataeval_flow/workflows/ood/__init__.py +1 -0
  76. dataeval_flow/workflows/ood/outputs.py +134 -0
  77. dataeval_flow/workflows/ood/params.py +161 -0
  78. dataeval_flow/workflows/ood/report.py +311 -0
  79. dataeval_flow/workflows/ood/workflow.py +728 -0
  80. dataeval_flow/workflows/prioritization/__init__.py +1 -0
  81. dataeval_flow/workflows/prioritization/outputs.py +122 -0
  82. dataeval_flow/workflows/prioritization/params.py +124 -0
  83. dataeval_flow/workflows/prioritization/report.py +117 -0
  84. dataeval_flow/workflows/prioritization/workflow.py +587 -0
  85. dataeval_flow/workflows/splitting/__init__.py +25 -0
  86. dataeval_flow/workflows/splitting/outputs.py +101 -0
  87. dataeval_flow/workflows/splitting/params.py +61 -0
  88. dataeval_flow/workflows/splitting/report.py +485 -0
  89. dataeval_flow/workflows/splitting/workflow.py +371 -0
  90. dataeval_flow-0.1.0.dist-info/METADATA +305 -0
  91. dataeval_flow-0.1.0.dist-info/RECORD +94 -0
  92. dataeval_flow-0.1.0.dist-info/WHEEL +4 -0
  93. dataeval_flow-0.1.0.dist-info/entry_points.txt +2 -0
  94. dataeval_flow-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,80 @@
1
+ """Config layer — single public API for all configuration types.
2
+
3
+ Import everything from here::
4
+
5
+ from dataeval_flow.config import PipelineConfig, OnnxExtractorConfig, ...
6
+ """
7
+
8
+ __all__ = [
9
+ # Dataset configs
10
+ "CocoDatasetConfig",
11
+ "DatasetProtocolConfig",
12
+ "HuggingFaceDatasetConfig",
13
+ "ImageFolderDatasetConfig",
14
+ "YoloDatasetConfig",
15
+ # Extractor configs
16
+ "BoVWExtractorConfig",
17
+ "FlattenExtractorConfig",
18
+ "OnnxExtractorConfig",
19
+ "TorchExtractorConfig",
20
+ "UncertaintyExtractorConfig",
21
+ # Workflow configs
22
+ "DataAnalysisWorkflowConfig",
23
+ "DataCleaningWorkflowConfig",
24
+ "DataPrioritizationWorkflowConfig",
25
+ "DriftMonitoringWorkflowConfig",
26
+ "OODDetectionWorkflowConfig",
27
+ # Task configs
28
+ "DataAnalysisTaskConfig",
29
+ "DataCleaningTaskConfig",
30
+ "DataPrioritizationTaskConfig",
31
+ "DriftMonitoringTaskConfig",
32
+ "OODDetectionTaskConfig",
33
+ "TaskConfig",
34
+ # Composition / pipeline
35
+ "PipelineConfig",
36
+ "SourceConfig",
37
+ # Other schemas
38
+ "PreprocessorConfig",
39
+ "ResultMetadata",
40
+ "SelectionConfig",
41
+ "SelectionStep",
42
+ # Loader functions
43
+ "export_params_schema",
44
+ "load_config",
45
+ "load_config_folder",
46
+ ]
47
+
48
+ from dataeval_flow.config._loader import (
49
+ export_params_schema,
50
+ load_config,
51
+ load_config_folder,
52
+ )
53
+ from dataeval_flow.config._models import PipelineConfig, SourceConfig
54
+ from dataeval_flow.config.schemas import (
55
+ BoVWExtractorConfig,
56
+ CocoDatasetConfig,
57
+ DataAnalysisTaskConfig,
58
+ DataAnalysisWorkflowConfig,
59
+ DataCleaningTaskConfig,
60
+ DataCleaningWorkflowConfig,
61
+ DataPrioritizationTaskConfig,
62
+ DataPrioritizationWorkflowConfig,
63
+ DatasetProtocolConfig,
64
+ DriftMonitoringTaskConfig,
65
+ DriftMonitoringWorkflowConfig,
66
+ FlattenExtractorConfig,
67
+ HuggingFaceDatasetConfig,
68
+ ImageFolderDatasetConfig,
69
+ OnnxExtractorConfig,
70
+ OODDetectionTaskConfig,
71
+ OODDetectionWorkflowConfig,
72
+ PreprocessorConfig,
73
+ ResultMetadata,
74
+ SelectionConfig,
75
+ SelectionStep,
76
+ TaskConfig,
77
+ TorchExtractorConfig,
78
+ UncertaintyExtractorConfig,
79
+ YoloDatasetConfig,
80
+ )
@@ -0,0 +1,79 @@
1
+ """Config loading - YAML/JSON single-file, multi-file merge, schema export."""
2
+
3
+ import json
4
+ import logging
5
+ import os
6
+ from pathlib import Path
7
+
8
+ import yaml
9
+
10
+ from dataeval_flow.config._models import PipelineConfig
11
+ from dataeval_flow.config._paths import relativize_to_data_dir, validate_config_path
12
+
13
+ __all__ = [
14
+ "export_params_schema",
15
+ "get_data_dir",
16
+ "load_config",
17
+ "load_config_folder",
18
+ "relativize_to_data_dir",
19
+ "resolve_path",
20
+ "validate_config_path",
21
+ ]
22
+
23
+ logger: logging.Logger = logging.getLogger(__name__)
24
+
25
+ DEFAULT_DATA_DIR = Path(".")
26
+ _DATAEVAL_DATA_ENV = "DATAEVAL_DATA"
27
+
28
+
29
+ def get_data_dir(data_dir: Path | None = None) -> Path:
30
+ """Resolve the data root directory.
31
+
32
+ Priority: explicit argument > ``DATAEVAL_DATA`` env var > current directory.
33
+ """
34
+ if data_dir is not None:
35
+ return data_dir
36
+ return Path(os.environ.get(_DATAEVAL_DATA_ENV, str(DEFAULT_DATA_DIR)))
37
+
38
+
39
+ def resolve_path(relative: str | Path, data_dir: Path | None = None) -> Path:
40
+ """Resolve a user-provided path against *data_dir*.
41
+
42
+ Absolute paths are returned as-is. Relative paths are joined to
43
+ *data_dir* (which itself defaults via :func:`get_data_dir`).
44
+ """
45
+ p = Path(relative)
46
+ if p.is_absolute():
47
+ return p
48
+ root = data_dir if data_dir is not None else get_data_dir()
49
+ return root / p
50
+
51
+
52
+ def load_config(config_path: Path) -> PipelineConfig:
53
+ """Load pipeline configuration from a single YAML or JSON file."""
54
+ logger.debug("Loading config from %s", config_path)
55
+
56
+ if not config_path.exists():
57
+ msg = f"Config file not found: {config_path}"
58
+ raise FileNotFoundError(msg)
59
+
60
+ with open(config_path, encoding="utf-8") as f:
61
+ data = json.load(f) if config_path.suffix.lower() == ".json" else yaml.safe_load(f) or {}
62
+
63
+ return PipelineConfig.model_validate(data)
64
+
65
+
66
+ def load_config_folder(config_path: Path) -> PipelineConfig:
67
+ """Load and merge all YAML/JSON files from config folder."""
68
+ from dataeval_flow.config._merge import merge_config_folder
69
+
70
+ logger.debug("Loading config folder %s", config_path)
71
+ merged = merge_config_folder(config_path)
72
+ return PipelineConfig.model_validate(merged)
73
+
74
+
75
+ def export_params_schema(output_path: Path) -> None:
76
+ """Export JSON Schema for params.yaml (PipelineConfig) for IDE validation."""
77
+ output_path.parent.mkdir(parents=True, exist_ok=True)
78
+ schema = PipelineConfig.model_json_schema()
79
+ output_path.write_text(json.dumps(schema, indent=2), encoding="utf-8")
@@ -0,0 +1,92 @@
1
+ """Multi-file YAML/JSON configuration loader with schema validation."""
2
+
3
+ import json
4
+ import logging
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ import yaml
9
+
10
+ logger: logging.Logger = logging.getLogger(__name__)
11
+
12
+ _YAML_EXTS = frozenset({".yaml", ".yml"})
13
+ _JSON_EXTS = frozenset({".json"})
14
+ _CONFIG_EXTS = _YAML_EXTS | _JSON_EXTS
15
+
16
+
17
+ def _load_file(path: Path) -> dict[str, Any] | list[str]:
18
+ """Load a single YAML or JSON file and return its contents as a dict."""
19
+ with open(path, encoding="utf-8") as f:
20
+ return json.load(f) if path.suffix.lower() in _JSON_EXTS else yaml.safe_load(f) or []
21
+
22
+
23
+ def _is_valid_config(data: dict[str, Any]) -> bool:
24
+ """Return True if *data* looks like a pipeline config fragment.
25
+
26
+ A file is accepted when it contains at least one key that is a known
27
+ ``PipelineConfig`` field **and** no keys that are completely unknown.
28
+ This allows partial configs (e.g. only ``datasets:``) while rejecting
29
+ unrelated files like JSON schemas.
30
+ """
31
+ from dataeval_flow.config._models import PipelineConfig
32
+
33
+ known_keys = set(PipelineConfig.model_fields)
34
+ file_keys = set(data.keys())
35
+ return bool(file_keys) and file_keys <= known_keys
36
+
37
+
38
+ def merge_config_folder(config_path: Path) -> dict[str, Any]:
39
+ """Scan folder, merge all valid config files alphabetically.
40
+
41
+ Each candidate file is validated against the ``PipelineConfig`` schema
42
+ individually. Files that fail validation (e.g. JSON schemas, unrelated
43
+ YAML) are silently skipped. If no valid files are found, a
44
+ ``FileNotFoundError`` is raised.
45
+
46
+ Files are loaded in sorted order (00-base.yaml before 01-datasets.yaml).
47
+ Later files override earlier ones for duplicate keys.
48
+
49
+ Returns raw dict - use load_config_folder() for validated PipelineConfig.
50
+ """
51
+ config: dict[str, Any] = {}
52
+
53
+ if not config_path.is_dir():
54
+ raise ValueError(f"Config path is not a directory: {config_path}")
55
+
56
+ candidates = sorted(f for f in config_path.iterdir() if f.is_file() and f.suffix.lower() in _CONFIG_EXTS)
57
+ logger.debug("Found %d candidate file(s): %s", len(candidates), [f.name for f in candidates])
58
+
59
+ accepted: list[Path] = []
60
+ for config_file in candidates:
61
+ try:
62
+ file_config = _load_file(config_file)
63
+ except (json.JSONDecodeError, yaml.YAMLError) as exc:
64
+ logger.debug("Skipping %s (parse error: %s)", config_file.name, exc)
65
+ continue
66
+
67
+ if not isinstance(file_config, dict) or not _is_valid_config(file_config):
68
+ logger.debug("Skipping %s (not a valid pipeline config)", config_file.name)
69
+ continue
70
+
71
+ _deep_merge(config, file_config)
72
+ accepted.append(config_file)
73
+
74
+ if not accepted:
75
+ raise FileNotFoundError(f"No valid pipeline config files found in {config_path}")
76
+
77
+ logger.debug("Accepted %d config file(s): %s", len(accepted), [f.name for f in accepted])
78
+ return config
79
+
80
+
81
+ def _deep_merge(base: dict, overlay: dict) -> None:
82
+ """Recursively merge overlay into base.
83
+
84
+ Rules: dicts merge recursively, lists extend, scalars replace.
85
+ """
86
+ for key, value in overlay.items():
87
+ if key in base and isinstance(base[key], dict) and isinstance(value, dict):
88
+ _deep_merge(base[key], value)
89
+ elif key in base and isinstance(base[key], list) and isinstance(value, list):
90
+ base[key].extend(value)
91
+ else:
92
+ base[key] = value
@@ -0,0 +1,115 @@
1
+ """Pipeline and workflow composition models — SourceConfig, PipelineConfig."""
2
+
3
+ __all__ = [
4
+ "PipelineConfig",
5
+ "SourceConfig",
6
+ ]
7
+
8
+ from collections.abc import Sequence
9
+ from typing import Literal
10
+
11
+ from pydantic import BaseModel, Field, model_validator
12
+
13
+ from dataeval_flow.config.schemas import (
14
+ DatasetConfig,
15
+ DatasetProtocolConfig,
16
+ ExtractorConfig,
17
+ PreprocessorConfig,
18
+ SelectionConfig,
19
+ TaskConfig,
20
+ WorkflowConfig,
21
+ )
22
+
23
+ # ---------------------------------------------------------------------------
24
+ # Source — dataset + optional selection
25
+ # ---------------------------------------------------------------------------
26
+
27
+
28
+ class SourceConfig(BaseModel):
29
+ """Named source definition — bundles a dataset with an optional selection.
30
+
31
+ YAML example::
32
+
33
+ sources:
34
+ - name: cifar_train_subset
35
+ dataset: cifar10_train
36
+ selection: first_5k
37
+ """
38
+
39
+ name: str = Field(description="Identifier for the source")
40
+ dataset: str = Field(description="Reference to a dataset name")
41
+ selection: str | None = Field(default=None, description="Reference to a selection name (optional)")
42
+
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # Logging
46
+ # ---------------------------------------------------------------------------
47
+
48
+
49
+ class LoggingConfig(BaseModel):
50
+ """Logging level configuration."""
51
+
52
+ app_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "DEBUG"
53
+ lib_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "WARNING"
54
+
55
+
56
+ # ---------------------------------------------------------------------------
57
+ # Pipeline (top-level)
58
+ # ---------------------------------------------------------------------------
59
+
60
+
61
+ class PipelineConfig(BaseModel):
62
+ """Top-level pipeline configuration.
63
+
64
+ All sections use a define-once, reference-by-name pattern.
65
+ Sources compose datasets with optional selections; extractors
66
+ compose model type/params with optional preprocessors.
67
+ Tasks reference workflows, sources, and extractors by name.
68
+ """
69
+
70
+ # Logging
71
+ logging: LoggingConfig | None = None
72
+
73
+ # Named resource pools
74
+ datasets: Sequence[DatasetConfig | DatasetProtocolConfig] | None = None
75
+ preprocessors: Sequence[PreprocessorConfig] | None = None
76
+ selections: Sequence[SelectionConfig] | None = None
77
+
78
+ # Composition layers
79
+ sources: Sequence[SourceConfig] | None = Field(
80
+ default=None,
81
+ description="Named source definitions (dataset + optional selection)",
82
+ )
83
+ extractors: Sequence[ExtractorConfig] | None = Field(
84
+ default=None,
85
+ description="Named extractor definitions (model type + params + optional preprocessor + batch_size)",
86
+ )
87
+
88
+ # Execution
89
+ workflows: Sequence[WorkflowConfig] | None = Field(
90
+ default=None,
91
+ description="Named workflow configurations (type + params), referenced by tasks",
92
+ )
93
+ tasks: Sequence[TaskConfig] | None = None
94
+
95
+ @model_validator(mode="after")
96
+ def _check_unique_names(self) -> "PipelineConfig":
97
+ """Raise if any section contains duplicate names."""
98
+ sections: dict[str, Sequence | None] = {
99
+ "datasets": self.datasets,
100
+ "preprocessors": self.preprocessors,
101
+ "selections": self.selections,
102
+ "sources": self.sources,
103
+ "extractors": self.extractors,
104
+ "workflows": self.workflows,
105
+ "tasks": self.tasks,
106
+ }
107
+ for section_name, items in sections.items():
108
+ if items is None:
109
+ continue
110
+ seen: set[str] = set()
111
+ for item in items:
112
+ if item.name in seen:
113
+ raise ValueError(f"Duplicate name '{item.name}' in {section_name}")
114
+ seen.add(item.name)
115
+ return self
@@ -0,0 +1,85 @@
1
+ """Config path validation and relativization utilities.
2
+
3
+ These are separated from ``_loader`` to avoid circular imports — the
4
+ schema modules (``_dataset``, ``_extractor``) need them at import time,
5
+ while ``_loader`` depends on ``_models`` which depends on schemas.
6
+ """
7
+
8
+ import os
9
+ from pathlib import Path, PurePosixPath
10
+
11
+ __all__ = [
12
+ "relativize_to_data_dir",
13
+ "validate_config_path",
14
+ ]
15
+
16
+
17
+ def validate_config_path(value: str) -> str:
18
+ """Validate that a config path is relative and stays under the data root.
19
+
20
+ Config paths (dataset ``path``, extractor ``model_path``) must be
21
+ stored as relative paths so that configs remain portable across
22
+ machines and into containers where the data root is mounted at an
23
+ arbitrary location (e.g. ``/dataeval``).
24
+
25
+ Raises
26
+ ------
27
+ ValueError
28
+ If *value* is an absolute path or escapes upward via ``..``.
29
+ """
30
+ if not value:
31
+ raise ValueError("Path must not be empty")
32
+
33
+ p = PurePosixPath(value)
34
+ if p.is_absolute():
35
+ raise ValueError(
36
+ f"Config paths must be relative (got absolute path '{value}'). "
37
+ "Use a path relative to the data root instead."
38
+ )
39
+
40
+ # Normalize and check for upward escape via ".."
41
+ normalized = os.path.normpath(value)
42
+ if normalized.startswith(".."):
43
+ raise ValueError(
44
+ f"Config path '{value}' escapes the data root directory. "
45
+ "All paths must resolve to locations under the data root."
46
+ )
47
+
48
+ return value
49
+
50
+
51
+ def relativize_to_data_dir(absolute_path: str | Path, data_dir: Path) -> str:
52
+ """Convert an absolute path to a relative path under *data_dir*.
53
+
54
+ Used by the TUI browse to convert OS-absolute paths into portable
55
+ relative paths suitable for config storage.
56
+
57
+ Parameters
58
+ ----------
59
+ absolute_path : str | Path
60
+ The absolute path to relativize.
61
+ data_dir : Path
62
+ The data root to relativize against.
63
+
64
+ Returns
65
+ -------
66
+ str
67
+ A relative path string under *data_dir*.
68
+
69
+ Raises
70
+ ------
71
+ ValueError
72
+ If *absolute_path* is not under *data_dir*.
73
+ """
74
+ p = Path(absolute_path).resolve()
75
+ root = data_dir.resolve()
76
+
77
+ try:
78
+ rel = p.relative_to(root)
79
+ except ValueError:
80
+ raise ValueError(
81
+ f"Path '{absolute_path}' is not under the data root '{root}'. "
82
+ "Config paths must point to locations inside the data directory."
83
+ ) from None
84
+
85
+ return str(rel)
@@ -0,0 +1,112 @@
1
+ """Schema catalog — concrete types and discriminated-union aliases.
2
+
3
+ Private ``_*.py`` modules define the concrete schema classes. This
4
+ ``__init__`` re-exports them and defines the discriminated-union type
5
+ aliases (``DatasetConfig``, ``ExtractorConfig``, ``WorkflowConfig``)
6
+ consumed by :class:`~dataeval_flow.config._models.PipelineConfig`.
7
+ """
8
+
9
+ from typing import Annotated
10
+
11
+ from pydantic import Field
12
+
13
+ from dataeval_flow.config.schemas._dataset import (
14
+ CocoDatasetConfig,
15
+ DatasetProtocolConfig,
16
+ HuggingFaceDatasetConfig,
17
+ ImageFolderDatasetConfig,
18
+ YoloDatasetConfig,
19
+ )
20
+ from dataeval_flow.config.schemas._extractor import (
21
+ BoVWExtractorConfig,
22
+ FlattenExtractorConfig,
23
+ OnnxExtractorConfig,
24
+ TorchExtractorConfig,
25
+ UncertaintyExtractorConfig,
26
+ )
27
+ from dataeval_flow.config.schemas._metadata import ResultMetadata
28
+ from dataeval_flow.config.schemas._preprocessor import PreprocessorConfig
29
+ from dataeval_flow.config.schemas._selection import SelectionConfig, SelectionStep
30
+ from dataeval_flow.config.schemas._task import (
31
+ AutoBinMethod,
32
+ DataAnalysisTaskConfig,
33
+ DataCleaningTaskConfig,
34
+ DataPrioritizationTaskConfig,
35
+ DataSplittingTaskConfig,
36
+ DriftMonitoringTaskConfig,
37
+ OODDetectionTaskConfig,
38
+ TaskConfig,
39
+ )
40
+ from dataeval_flow.config.schemas._workflow import (
41
+ DataAnalysisWorkflowConfig,
42
+ DataCleaningWorkflowConfig,
43
+ DataPrioritizationWorkflowConfig,
44
+ DataSplittingWorkflowConfig,
45
+ DriftMonitoringWorkflowConfig,
46
+ OODDetectionWorkflowConfig,
47
+ )
48
+
49
+ # -- discriminated-union aliases (internal) ---------------------------------
50
+
51
+ DatasetConfig = Annotated[
52
+ HuggingFaceDatasetConfig | ImageFolderDatasetConfig | CocoDatasetConfig | YoloDatasetConfig,
53
+ Field(discriminator="format"),
54
+ ]
55
+
56
+ ExtractorConfig = Annotated[
57
+ OnnxExtractorConfig
58
+ | BoVWExtractorConfig
59
+ | FlattenExtractorConfig
60
+ | TorchExtractorConfig
61
+ | UncertaintyExtractorConfig,
62
+ Field(discriminator="model"),
63
+ ]
64
+
65
+ WorkflowConfig = Annotated[
66
+ DataAnalysisWorkflowConfig
67
+ | DataCleaningWorkflowConfig
68
+ | DataPrioritizationWorkflowConfig
69
+ | DataSplittingWorkflowConfig
70
+ | DriftMonitoringWorkflowConfig
71
+ | OODDetectionWorkflowConfig,
72
+ Field(discriminator="type"),
73
+ ]
74
+
75
+ __all__ = [
76
+ # Dataset
77
+ "CocoDatasetConfig",
78
+ "DatasetConfig",
79
+ "DatasetProtocolConfig",
80
+ "HuggingFaceDatasetConfig",
81
+ "ImageFolderDatasetConfig",
82
+ "YoloDatasetConfig",
83
+ # Extractor
84
+ "BoVWExtractorConfig",
85
+ "ExtractorConfig",
86
+ "FlattenExtractorConfig",
87
+ "OnnxExtractorConfig",
88
+ "TorchExtractorConfig",
89
+ "UncertaintyExtractorConfig",
90
+ # Workflow
91
+ "DataAnalysisWorkflowConfig",
92
+ "DataCleaningWorkflowConfig",
93
+ "DataPrioritizationWorkflowConfig",
94
+ "DataSplittingWorkflowConfig",
95
+ "DriftMonitoringWorkflowConfig",
96
+ "OODDetectionWorkflowConfig",
97
+ "WorkflowConfig",
98
+ # Task
99
+ "AutoBinMethod",
100
+ "DataAnalysisTaskConfig",
101
+ "DataCleaningTaskConfig",
102
+ "DataPrioritizationTaskConfig",
103
+ "DataSplittingTaskConfig",
104
+ "DriftMonitoringTaskConfig",
105
+ "OODDetectionTaskConfig",
106
+ "TaskConfig",
107
+ # Other
108
+ "PreprocessorConfig",
109
+ "ResultMetadata",
110
+ "SelectionConfig",
111
+ "SelectionStep",
112
+ ]
@@ -0,0 +1,111 @@
1
+ """Dataset configuration schemas — one class per format."""
2
+
3
+ from typing import Any, ClassVar, Literal
4
+
5
+ from pydantic import BaseModel, ConfigDict, field_validator
6
+
7
+ from dataeval_flow.config._paths import validate_config_path
8
+
9
+
10
+ class _DatasetConfigBase(BaseModel):
11
+ """Common fields shared by all dataset formats."""
12
+
13
+ model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
14
+
15
+ format: Any
16
+ name: str
17
+ path: str
18
+
19
+ @field_validator("path")
20
+ @classmethod
21
+ def _path_must_be_relative(cls, v: str) -> str:
22
+ return validate_config_path(v)
23
+
24
+
25
+ class HuggingFaceDatasetConfig(_DatasetConfigBase):
26
+ """Dataset config for HuggingFace format.
27
+
28
+ YAML example::
29
+
30
+ datasets:
31
+ - name: cifar10_train
32
+ format: huggingface
33
+ path: ./cifar10
34
+ split: train
35
+ """
36
+
37
+ format: Literal["huggingface"] = "huggingface"
38
+ split: str | None = None
39
+
40
+
41
+ class ImageFolderDatasetConfig(_DatasetConfigBase):
42
+ """Dataset config for image_folder format.
43
+
44
+ YAML example::
45
+
46
+ datasets:
47
+ - name: photos
48
+ format: image_folder
49
+ path: photos
50
+ recursive: true
51
+ infer_labels: true
52
+ """
53
+
54
+ format: Literal["image_folder"] = "image_folder"
55
+ recursive: bool = False
56
+ infer_labels: bool = False
57
+
58
+
59
+ class CocoDatasetConfig(_DatasetConfigBase):
60
+ """Dataset config for COCO format.
61
+
62
+ YAML example::
63
+
64
+ datasets:
65
+ - name: coco_train
66
+ format: coco
67
+ path: coco
68
+ annotations_file: instances_train.json
69
+ images_dir: train2017
70
+ """
71
+
72
+ format: Literal["coco"] = "coco"
73
+ annotations_file: str | None = None
74
+ images_dir: str | None = None
75
+ classes_file: str | None = None
76
+
77
+
78
+ class YoloDatasetConfig(_DatasetConfigBase):
79
+ """Dataset config for YOLO format.
80
+
81
+ YAML example::
82
+
83
+ datasets:
84
+ - name: yolo_train
85
+ format: yolo
86
+ path: yolo
87
+ images_dir: images
88
+ labels_dir: labels
89
+ classes_file: classes.txt
90
+ """
91
+
92
+ format: Literal["yolo"] = "yolo"
93
+ images_dir: str | None = None
94
+ labels_dir: str | None = None
95
+ classes_file: str | None = None
96
+
97
+
98
+ class DatasetProtocolConfig(BaseModel):
99
+ """Dataset Configuration schema for an in-memory dataset.
100
+
101
+ Not serializable — for programmatic use only. Cannot be loaded from
102
+ YAML/JSON config files or edited in the builder UI.
103
+ """
104
+
105
+ serializable: ClassVar[bool] = False
106
+ model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
107
+
108
+ name: str
109
+ format: Literal["maite", "torchvision"] = "maite"
110
+ dataset: Any
111
+ version: str = "1"