trustbench 0.2.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 (110) hide show
  1. trustbench/__init__.py +121 -0
  2. trustbench/api/__init__.py +5 -0
  3. trustbench/api/app.py +45 -0
  4. trustbench/api/routes/experiments.py +82 -0
  5. trustbench/benchmark.py +264 -0
  6. trustbench/cli/__init__.py +5 -0
  7. trustbench/cli/main.py +303 -0
  8. trustbench/config/__init__.py +22 -0
  9. trustbench/config/loader.py +71 -0
  10. trustbench/config/schema.py +149 -0
  11. trustbench/core/__init__.py +30 -0
  12. trustbench/core/evaluator.py +163 -0
  13. trustbench/core/exceptions.py +17 -0
  14. trustbench/core/inference.py +137 -0
  15. trustbench/core/protocols.py +57 -0
  16. trustbench/core/reproducibility.py +74 -0
  17. trustbench/core/results.py +56 -0
  18. trustbench/datasets/__init__.py +23 -0
  19. trustbench/datasets/base.py +31 -0
  20. trustbench/datasets/cifar10.py +164 -0
  21. trustbench/datasets/memory.py +49 -0
  22. trustbench/datasets/torch_dataset.py +108 -0
  23. trustbench/evaluation/__init__.py +164 -0
  24. trustbench/evaluation/artifacts.py +13 -0
  25. trustbench/evaluation/calibration.py +251 -0
  26. trustbench/evaluation/classification.py +145 -0
  27. trustbench/evaluation/distribution_shift.py +269 -0
  28. trustbench/evaluation/error_detection.py +281 -0
  29. trustbench/evaluation/fairness.py +103 -0
  30. trustbench/evaluation/inference.py +16 -0
  31. trustbench/evaluation/regression.py +119 -0
  32. trustbench/evaluation/reproducibility.py +13 -0
  33. trustbench/evaluation/retrieval.py +76 -0
  34. trustbench/evaluation/runner.py +99 -0
  35. trustbench/evaluation/segmentation.py +104 -0
  36. trustbench/evaluation/selective.py +221 -0
  37. trustbench/evaluation/streaming.py +237 -0
  38. trustbench/evaluation/uncertainty.py +341 -0
  39. trustbench/experiments/__init__.py +48 -0
  40. trustbench/experiments/comparison.py +284 -0
  41. trustbench/experiments/lineage.py +234 -0
  42. trustbench/experiments/manager.py +387 -0
  43. trustbench/experiments/registry.py +184 -0
  44. trustbench/experiments/schemas.py +78 -0
  45. trustbench/metrics/__init__.py +45 -0
  46. trustbench/metrics/calibration.py +15 -0
  47. trustbench/metrics/classification.py +11 -0
  48. trustbench/metrics/error_detection.py +17 -0
  49. trustbench/metrics/regression.py +96 -0
  50. trustbench/metrics/retrieval.py +108 -0
  51. trustbench/metrics/segmentation.py +116 -0
  52. trustbench/metrics/selective.py +17 -0
  53. trustbench/metrics/uncertainty.py +23 -0
  54. trustbench/models/__init__.py +74 -0
  55. trustbench/models/base.py +48 -0
  56. trustbench/models/checkpoint.py +98 -0
  57. trustbench/models/densenet.py +100 -0
  58. trustbench/models/dla.py +124 -0
  59. trustbench/models/dla_simple.py +117 -0
  60. trustbench/models/dpn.py +90 -0
  61. trustbench/models/efficientnet.py +165 -0
  62. trustbench/models/googlenet.py +98 -0
  63. trustbench/models/jax.py +95 -0
  64. trustbench/models/keras.py +105 -0
  65. trustbench/models/lenet.py +24 -0
  66. trustbench/models/mobilenet.py +53 -0
  67. trustbench/models/mobilenetv2.py +79 -0
  68. trustbench/models/onnx.py +134 -0
  69. trustbench/models/pnasnet.py +118 -0
  70. trustbench/models/preact_resnet.py +110 -0
  71. trustbench/models/pytorch.py +208 -0
  72. trustbench/models/regnet.py +143 -0
  73. trustbench/models/resnet.py +124 -0
  74. trustbench/models/resnext.py +95 -0
  75. trustbench/models/senet.py +113 -0
  76. trustbench/models/shufflenet.py +101 -0
  77. trustbench/models/shufflenetv2.py +153 -0
  78. trustbench/models/sklearn.py +141 -0
  79. trustbench/models/vgg.py +37 -0
  80. trustbench/registry/__init__.py +77 -0
  81. trustbench/registry/corruptions.py +181 -0
  82. trustbench/registry/datasets.py +192 -0
  83. trustbench/registry/metrics.py +244 -0
  84. trustbench/registry/models.py +212 -0
  85. trustbench/registry/plugins.py +222 -0
  86. trustbench/reporting/__init__.py +57 -0
  87. trustbench/reporting/artifacts.py +131 -0
  88. trustbench/reporting/hashing.py +21 -0
  89. trustbench/reporting/html_report.py +717 -0
  90. trustbench/reporting/plots.py +549 -0
  91. trustbench/reporting/result_io.py +109 -0
  92. trustbench/reporting/schema.py +195 -0
  93. trustbench/reporting/tables.py +64 -0
  94. trustbench/robustness/__init__.py +70 -0
  95. trustbench/robustness/aggregation.py +159 -0
  96. trustbench/robustness/corruptions.py +426 -0
  97. trustbench/robustness/evaluator.py +313 -0
  98. trustbench/robustness/report.py +11 -0
  99. trustbench/robustness/severity.py +525 -0
  100. trustbench/storage/__init__.py +66 -0
  101. trustbench/storage/base.py +171 -0
  102. trustbench/storage/database.py +67 -0
  103. trustbench/storage/filesystem.py +489 -0
  104. trustbench/storage/models.py +55 -0
  105. trustbench/storage/repositories.py +168 -0
  106. trustbench/storage/sqlite.py +470 -0
  107. trustbench-0.2.0.dist-info/METADATA +296 -0
  108. trustbench-0.2.0.dist-info/RECORD +110 -0
  109. trustbench-0.2.0.dist-info/WHEEL +4 -0
  110. trustbench-0.2.0.dist-info/entry_points.txt +3 -0
trustbench/__init__.py ADDED
@@ -0,0 +1,121 @@
1
+ """TrustBench: Evaluation framework for machine learning model reliability."""
2
+
3
+ __version__ = "0.2.0"
4
+
5
+ from trustbench.benchmark import TrustBench
6
+ from trustbench.core.evaluator import evaluate
7
+ from trustbench.core.exceptions import (
8
+ SecurityError,
9
+ ShapeError,
10
+ TrustBenchError,
11
+ UnsupportedCapabilityError,
12
+ )
13
+ from trustbench.core.protocols import DatasetAdapter, ModelAdapter
14
+ from trustbench.core.results import EvaluationResult
15
+ from trustbench.evaluation.distribution_shift import (
16
+ ShiftType,
17
+ compute_label_distribution_shift,
18
+ compute_ood_detection_metrics,
19
+ evaluate_distribution_shift,
20
+ )
21
+ from trustbench.evaluation.fairness import compute_fairness_metrics
22
+ from trustbench.evaluation.regression import RegressionResult, evaluate_regression
23
+ from trustbench.evaluation.retrieval import RetrievalResult, evaluate_retrieval
24
+ from trustbench.evaluation.segmentation import (
25
+ SegmentationResult,
26
+ evaluate_segmentation,
27
+ )
28
+ from trustbench.metrics.regression import compute_regression_metrics
29
+ from trustbench.metrics.retrieval import compute_retrieval_metrics
30
+ from trustbench.metrics.segmentation import compute_segmentation_metrics
31
+ from trustbench.registry import (
32
+ GenericDatasetAdapter,
33
+ GenericModelAdapter,
34
+ discover_plugins,
35
+ list_registered_corruptions,
36
+ list_registered_metrics,
37
+ load_plugin,
38
+ register_corruption,
39
+ register_dataset_adapter,
40
+ register_metric,
41
+ register_model_adapter,
42
+ validate_dataset_adapter,
43
+ validate_model_adapter,
44
+ )
45
+
46
+ __all__ = [
47
+ "DatasetAdapter",
48
+ "EvaluationResult",
49
+ "GenericDatasetAdapter",
50
+ "GenericModelAdapter",
51
+ "JAXModelAdapter",
52
+ "KerasModelAdapter",
53
+ "ModelAdapter",
54
+ "ONNXModelAdapter",
55
+ "PyTorchModelAdapter",
56
+ "RegressionResult",
57
+ "RetrievalResult",
58
+ "SecurityError",
59
+ "SegmentationResult",
60
+ "ShapeError",
61
+ "ShiftType",
62
+ "SklearnModelAdapter",
63
+ "TrustBench",
64
+ "TrustBenchError",
65
+ "UnsupportedCapabilityError",
66
+ "__version__",
67
+ "compute_fairness_metrics",
68
+ "compute_label_distribution_shift",
69
+ "compute_ood_detection_metrics",
70
+ "compute_regression_metrics",
71
+ "compute_retrieval_metrics",
72
+ "compute_segmentation_metrics",
73
+ "discover_plugins",
74
+ "evaluate",
75
+ "evaluate_distribution_shift",
76
+ "evaluate_regression",
77
+ "evaluate_retrieval",
78
+ "evaluate_segmentation",
79
+ "list_registered_corruptions",
80
+ "list_registered_metrics",
81
+ "load_plugin",
82
+ "register_corruption",
83
+ "register_dataset_adapter",
84
+ "register_metric",
85
+ "register_model_adapter",
86
+ "validate_dataset_adapter",
87
+ "validate_model_adapter",
88
+ ]
89
+
90
+
91
+ def __getattr__(name: str):
92
+ if name == "ExperimentManager":
93
+ from trustbench.experiments.manager import ExperimentManager
94
+
95
+ return ExperimentManager
96
+ if name in ("FilesystemExperimentStore", "SQLiteExperimentStore"):
97
+ from trustbench import storage
98
+
99
+ return getattr(storage, name)
100
+ if name == "SklearnModelAdapter":
101
+ from trustbench.models.sklearn import SklearnModelAdapter
102
+
103
+ return SklearnModelAdapter
104
+ if name == "PyTorchModelAdapter":
105
+ from trustbench.models.pytorch import PyTorchModelAdapter
106
+
107
+ return PyTorchModelAdapter
108
+ if name == "ONNXModelAdapter":
109
+ from trustbench.models.onnx import ONNXModelAdapter
110
+
111
+ return ONNXModelAdapter
112
+ if name == "KerasModelAdapter":
113
+ from trustbench.models.keras import KerasModelAdapter
114
+
115
+ return KerasModelAdapter
116
+ if name == "JAXModelAdapter":
117
+ from trustbench.models.jax import JAXModelAdapter
118
+
119
+ return JAXModelAdapter
120
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
121
+
@@ -0,0 +1,5 @@
1
+ """API module for TrustBench."""
2
+
3
+ from trustbench.api.app import app, create_app
4
+
5
+ __all__ = ["app", "create_app"]
trustbench/api/app.py ADDED
@@ -0,0 +1,45 @@
1
+ """FastAPI application factory for TrustBench API."""
2
+
3
+ from contextlib import asynccontextmanager
4
+
5
+ from fastapi import FastAPI
6
+ from fastapi.middleware.cors import CORSMiddleware
7
+
8
+ from trustbench.api.routes.experiments import router as experiments_router
9
+ from trustbench.storage.database import init_db
10
+
11
+
12
+ @asynccontextmanager
13
+ async def lifespan(app: FastAPI):
14
+ # Initialize database tables on startup
15
+ init_db()
16
+ yield
17
+
18
+
19
+ def create_app() -> FastAPI:
20
+ """Create and configure TrustBench FastAPI application."""
21
+ app = FastAPI(
22
+ title="TrustBench API",
23
+ description="ML Reliability, Robustness & Uncertainty Evaluation Framework API",
24
+ version="0.1.0",
25
+ lifespan=lifespan,
26
+ )
27
+
28
+ app.add_middleware(
29
+ CORSMiddleware,
30
+ allow_origins=["*"],
31
+ allow_credentials=True,
32
+ allow_methods=["*"],
33
+ allow_headers=["*"],
34
+ )
35
+
36
+ app.include_router(experiments_router)
37
+
38
+ @app.get("/health", tags=["health"])
39
+ def health_check():
40
+ return {"status": "ok", "service": "trustbench-api"}
41
+
42
+ return app
43
+
44
+
45
+ app = create_app()
@@ -0,0 +1,82 @@
1
+ """FastAPI routes for experiment management, metrics querying, artifacts, and comparisons."""
2
+
3
+ from fastapi import APIRouter, Depends, HTTPException, Query
4
+ from sqlalchemy.orm import Session
5
+
6
+ from trustbench.experiments.manager import ExperimentManager
7
+ from trustbench.experiments.schemas import (
8
+ ExperimentComparisonRequest,
9
+ ExperimentComparisonResponse,
10
+ ExperimentDetail,
11
+ ExperimentResponse,
12
+ )
13
+ from trustbench.storage.database import get_db_session
14
+
15
+ router = APIRouter(prefix="/experiments", tags=["experiments"])
16
+
17
+
18
+ def get_manager(session: Session = Depends(get_db_session)) -> ExperimentManager:
19
+ """Dependency for ExperimentManager."""
20
+ return ExperimentManager(session=session)
21
+
22
+
23
+ @router.get("", response_model=list[ExperimentResponse])
24
+ def list_experiments(
25
+ experiment_type: str | None = Query(None, description="Filter by experiment type (e.g. baseline, robustness)"),
26
+ status: str | None = Query(None, description="Filter by status (pending, running, completed, failed)"),
27
+ checkpoint_sha256: str | None = Query(None, description="Filter by checkpoint SHA-256 hash"),
28
+ manager: ExperimentManager = Depends(get_manager),
29
+ ):
30
+ """List all registered experiments."""
31
+ return manager.list_experiments(
32
+ experiment_type=experiment_type,
33
+ status=status,
34
+ checkpoint_sha256=checkpoint_sha256,
35
+ )
36
+
37
+
38
+ @router.get("/{experiment_id}", response_model=ExperimentDetail)
39
+ def get_experiment_detail(
40
+ experiment_id: str,
41
+ manager: ExperimentManager = Depends(get_manager),
42
+ ):
43
+ """Get full details of a specific experiment."""
44
+ exp = manager.get_experiment_detail(experiment_id)
45
+ if not exp:
46
+ raise HTTPException(status_code=404, detail=f"Experiment '{experiment_id}' not found.")
47
+ return exp
48
+
49
+
50
+ @router.post("/compare", response_model=ExperimentComparisonResponse)
51
+ def compare_experiments(
52
+ req: ExperimentComparisonRequest,
53
+ manager: ExperimentManager = Depends(get_manager),
54
+ ):
55
+ """Compare multiple experiments across shared conditions and metrics."""
56
+ result = manager.compare_experiments(
57
+ experiment_ids=req.experiment_ids,
58
+ conditions=req.conditions,
59
+ metrics=req.metrics,
60
+ )
61
+ return result
62
+
63
+
64
+ @router.post("/index-directory")
65
+ def index_artifact_directory(
66
+ directory_path: str = Query(..., description="Path to on-disk artifact directory"),
67
+ experiment_name: str | None = Query(None, description="Optional custom name"),
68
+ manager: ExperimentManager = Depends(get_manager),
69
+ ):
70
+ """Index an on-disk artifact folder into the database registry."""
71
+ try:
72
+ model = manager.registry.index_artifact_directory(directory_path, experiment_name=experiment_name)
73
+ return {
74
+ "message": "Experiment indexed successfully.",
75
+ "experiment_id": model.id,
76
+ "name": model.name,
77
+ "status": model.status,
78
+ }
79
+ except FileNotFoundError as e:
80
+ raise HTTPException(status_code=404, detail=str(e))
81
+ except (ValueError, RuntimeError, OSError) as e:
82
+ raise HTTPException(status_code=500, detail=str(e))
@@ -0,0 +1,264 @@
1
+ """TrustBench — public high-level API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from trustbench.core.evaluator import evaluate as _core_evaluate
9
+ from trustbench.core.protocols import DatasetAdapter, ModelAdapter
10
+ from trustbench.core.results import EvaluationResult
11
+
12
+
13
+ def _wrap_model(model: Any, **kwargs: Any) -> ModelAdapter:
14
+ """Coerce *model* to a :class:`ModelAdapter`."""
15
+ # Already a ModelAdapter — use directly
16
+ if isinstance(model, ModelAdapter):
17
+ return model
18
+
19
+ # torch.nn.Module — wrap in PyTorchModelAdapter
20
+ try:
21
+ from torch import nn
22
+
23
+ from trustbench.models.pytorch import PyTorchModelAdapter
24
+ except ImportError as exc:
25
+ raise ImportError(
26
+ "PyTorch is required to auto-wrap an nn.Module. "
27
+ "Install it with: pip install torch"
28
+ ) from exc
29
+
30
+ if isinstance(model, nn.Module):
31
+ num_classes = kwargs.get("num_classes")
32
+ if num_classes is None:
33
+ raise ValueError(
34
+ "Pass num_classes=<int> when providing a raw nn.Module to TrustBench."
35
+ )
36
+ device = kwargs.get("device")
37
+ return PyTorchModelAdapter(model, num_classes=num_classes, device=device)
38
+
39
+ # Scikit-learn estimator
40
+ if hasattr(model, "predict") and hasattr(model, "fit"):
41
+ from trustbench.models.sklearn import SklearnModelAdapter
42
+
43
+ task = kwargs.get("task", "classification")
44
+ num_classes = kwargs.get("num_classes")
45
+ return SklearnModelAdapter(model, num_classes=num_classes, task=task)
46
+
47
+ # ONNX InferenceSession
48
+ if hasattr(model, "run") and hasattr(model, "get_inputs"):
49
+ from trustbench.models.onnx import ONNXModelAdapter
50
+
51
+ num_classes = kwargs.get("num_classes", 2)
52
+ task = kwargs.get("task", "classification")
53
+ return ONNXModelAdapter(model, num_classes=num_classes, task=task)
54
+
55
+ raise TypeError(
56
+ f"model must be a ModelAdapter, nn.Module, scikit-learn estimator, or ONNX session, got {type(model).__name__}."
57
+ )
58
+
59
+
60
+ def _wrap_dataset(dataset: Any) -> DatasetAdapter:
61
+ """Coerce *dataset* to a :class:`DatasetAdapter`."""
62
+ if isinstance(dataset, DatasetAdapter):
63
+ return dataset
64
+
65
+ try:
66
+ from torch.utils.data import DataLoader, Dataset
67
+
68
+ from trustbench.datasets.torch_dataset import TorchDatasetAdapter
69
+ except ImportError as exc:
70
+ raise ImportError(
71
+ "PyTorch is required to auto-wrap a Dataset/DataLoader. "
72
+ "Install it with: pip install torch"
73
+ ) from exc
74
+
75
+ if isinstance(dataset, DataLoader):
76
+ # Extract the underlying Dataset from the DataLoader
77
+ dataset = dataset.dataset # type: ignore[assignment]
78
+
79
+ if isinstance(dataset, Dataset):
80
+ return TorchDatasetAdapter(dataset)
81
+
82
+ raise TypeError(
83
+ f"dataset must be a DatasetAdapter, torch Dataset, or DataLoader, "
84
+ f"got {type(dataset).__name__}."
85
+ )
86
+
87
+
88
+ class TrustBench:
89
+ """High-level evaluation interface.
90
+
91
+ Example::
92
+
93
+ import torch.nn as nn
94
+ from trustbench import TrustBench
95
+
96
+ model = nn.Linear(4, 3)
97
+ # dataset is any torch.utils.data.Dataset or DatasetAdapter
98
+ tb = TrustBench(model, dataset, num_classes=3)
99
+ result = tb.evaluate()
100
+
101
+ Args:
102
+ model: A :class:`~trustbench.core.protocols.ModelAdapter`, ``nn.Module``
103
+ (requires *num_classes* kwarg), DataLoader, or Dataset.
104
+ dataset: A :class:`~trustbench.core.protocols.DatasetAdapter`,
105
+ ``torch.utils.data.Dataset``, or ``torch.utils.data.DataLoader``.
106
+ metrics: Metric group names to compute. Defaults to
107
+ ``("classification", "calibration", "uncertainty")``.
108
+ batch_size: Inference batch size.
109
+ class_names: Optional list of human-readable class labels.
110
+ seed: Global random seed.
111
+ output_dir: If provided, artifacts are saved here after :meth:`evaluate`.
112
+ num_classes: Required when *model* is a raw ``nn.Module``.
113
+ device: Device string passed to :class:`PyTorchModelAdapter`.
114
+ """
115
+
116
+ def __init__(
117
+ self,
118
+ model: Any,
119
+ dataset: Any,
120
+ *,
121
+ metrics: list[str] | tuple[str, ...] | None = None,
122
+ batch_size: int = 100,
123
+ class_names: list[str] | tuple[str, ...] | None = None,
124
+ seed: int | None = None,
125
+ output_dir: str | Path | None = None,
126
+ # forwarded to _wrap_model when model is nn.Module
127
+ num_classes: int | None = None,
128
+ device: str | None = None,
129
+ ) -> None:
130
+ self._model_adapter: ModelAdapter = _wrap_model(
131
+ model, num_classes=num_classes, device=device
132
+ )
133
+ self._dataset_adapter: DatasetAdapter = _wrap_dataset(dataset)
134
+ self._metrics = metrics
135
+ self._batch_size = batch_size
136
+ self._class_names = class_names
137
+ self._seed = seed
138
+ self._output_dir = Path(output_dir) if output_dir is not None else None
139
+
140
+
141
+ # Class-method constructor from config
142
+
143
+
144
+ @classmethod
145
+ def from_config(cls, config_path: str | Path) -> TrustBench:
146
+ """Construct a :class:`TrustBench` instance from a YAML config file.
147
+
148
+ The *model* and *dataset* must be resolvable from the config (i.e. a
149
+ checkpoint path and architecture name must be provided).
150
+
151
+ Args:
152
+ config_path: Path to a ``trustbench.yaml`` config file.
153
+
154
+ Returns:
155
+ Configured :class:`TrustBench` instance ready to call :meth:`evaluate`.
156
+ """
157
+ from trustbench.config.loader import load_config
158
+ from trustbench.models.checkpoint import load_model_from_checkpoint
159
+ from trustbench.models.pytorch import PyTorchModelAdapter
160
+
161
+ cfg = load_config(config_path)
162
+ mcfg = cfg.model
163
+ dcfg = cfg.dataset
164
+ rcfg = cfg.reporting
165
+
166
+ # Model
167
+ if mcfg.checkpoint_path is None:
168
+ raise ValueError(
169
+ "config.model.checkpoint_path is required for TrustBench.from_config()."
170
+ )
171
+ if mcfg.arch_name is None:
172
+ raise ValueError(
173
+ "config.model.arch_name is required for TrustBench.from_config()."
174
+ )
175
+
176
+ ckpt_path = cfg.resolve_path(mcfg.checkpoint_path)
177
+ nn_module, _ = load_model_from_checkpoint(
178
+ ckpt_path, model_name=mcfg.arch_name, device=mcfg.device
179
+ )
180
+ model_adapter = PyTorchModelAdapter(
181
+ nn_module, num_classes=mcfg.num_classes, device=mcfg.device
182
+ )
183
+
184
+ # Dataset
185
+ dataset_adapter = cls._build_dataset_from_config(cfg)
186
+
187
+ output_dir: Path | None = Path(
188
+ cfg.resolve_path(rcfg.output_dir)
189
+ )
190
+
191
+ class_names = None
192
+ if dcfg.dataset_type.lower() == "cifar10":
193
+ from trustbench.datasets.cifar10 import CIFAR10_CLASSES
194
+
195
+ class_names = list(CIFAR10_CLASSES)
196
+
197
+ return cls(
198
+ model_adapter,
199
+ dataset_adapter,
200
+ metrics=cfg.metrics.groups,
201
+ batch_size=dcfg.batch_size,
202
+ seed=cfg.seed,
203
+ output_dir=output_dir,
204
+ class_names=class_names,
205
+ )
206
+
207
+ @staticmethod
208
+ def _build_dataset_from_config(cfg: Any) -> DatasetAdapter:
209
+ """Instantiate the correct DatasetAdapter from config."""
210
+ dcfg = cfg.dataset
211
+ dtype = dcfg.dataset_type.lower()
212
+
213
+ if dtype == "cifar10":
214
+ from trustbench.datasets.cifar10 import (
215
+ CIFAR10DatasetAdapter,
216
+ get_cifar10_datasets,
217
+ )
218
+
219
+ data_dir = (
220
+ str(cfg.resolve_path(dcfg.data_dir))
221
+ if dcfg.data_dir
222
+ else "./data"
223
+ )
224
+ trainset, testset = get_cifar10_datasets(data_dir=data_dir, download=True)
225
+ split_dataset = trainset if dcfg.split == "train" else testset
226
+ return CIFAR10DatasetAdapter(split_dataset)
227
+
228
+ raise ValueError(
229
+ f"Unknown dataset_type {dcfg.dataset_type!r}. "
230
+ "Supported types: 'cifar10'."
231
+ )
232
+
233
+
234
+ # Evaluation
235
+
236
+
237
+ def evaluate(self) -> EvaluationResult:
238
+ """Run the full evaluation pipeline and return an :class:`EvaluationResult`.
239
+
240
+ If *output_dir* was provided at construction, artifacts are automatically
241
+ saved via :func:`~trustbench.reporting.save_result`.
242
+
243
+ Returns:
244
+ :class:`EvaluationResult` containing metrics, predictions, and metadata.
245
+ """
246
+ result = _core_evaluate(
247
+ self._model_adapter,
248
+ self._dataset_adapter,
249
+ metrics=self._metrics,
250
+ batch_size=self._batch_size,
251
+ class_names=self._class_names,
252
+ seed=self._seed,
253
+ )
254
+
255
+ if self._output_dir is not None:
256
+ from trustbench.reporting.result_io import save_result
257
+
258
+ save_result(result, self._output_dir)
259
+
260
+ return result
261
+
262
+ def run(self) -> EvaluationResult:
263
+ """Alias for :meth:`evaluate`."""
264
+ return self.evaluate()
@@ -0,0 +1,5 @@
1
+ """TrustBench CLI package."""
2
+
3
+ from trustbench.cli.main import app
4
+
5
+ __all__ = ["app"]