eval-toolkit 0.27.1__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.
- eval_toolkit/__init__.py +238 -0
- eval_toolkit/__main__.py +156 -0
- eval_toolkit/_version.py +5 -0
- eval_toolkit/analysis.py +196 -0
- eval_toolkit/artifacts.py +376 -0
- eval_toolkit/bootstrap.py +1344 -0
- eval_toolkit/calibration.py +1143 -0
- eval_toolkit/claims.py +670 -0
- eval_toolkit/config.py +112 -0
- eval_toolkit/docs.py +305 -0
- eval_toolkit/evidence.py +90 -0
- eval_toolkit/harness.py +1193 -0
- eval_toolkit/leakage.py +1052 -0
- eval_toolkit/loaders.py +424 -0
- eval_toolkit/manifest.py +622 -0
- eval_toolkit/metrics.py +1720 -0
- eval_toolkit/operating_points.py +192 -0
- eval_toolkit/paths.py +125 -0
- eval_toolkit/plotting.py +991 -0
- eval_toolkit/protocols.py +98 -0
- eval_toolkit/provenance.py +255 -0
- eval_toolkit/py.typed +0 -0
- eval_toolkit/schemas/manifest.v1.json +155 -0
- eval_toolkit/schemas/manifest.v2.json +186 -0
- eval_toolkit/schemas/manifest.v3.json +186 -0
- eval_toolkit/schemas/results.v1.json +87 -0
- eval_toolkit/schemas/results_full.v1.json +83 -0
- eval_toolkit/seeds.py +119 -0
- eval_toolkit/splits.py +520 -0
- eval_toolkit/text_dedup.py +1403 -0
- eval_toolkit/thresholds.py +819 -0
- eval_toolkit-0.27.1.dist-info/METADATA +314 -0
- eval_toolkit-0.27.1.dist-info/RECORD +36 -0
- eval_toolkit-0.27.1.dist-info/WHEEL +4 -0
- eval_toolkit-0.27.1.dist-info/entry_points.txt +2 -0
- eval_toolkit-0.27.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Lightweight public Protocols with minimal dependency surface.
|
|
2
|
+
|
|
3
|
+
These contracts are intentionally kept out of the heavier implementation
|
|
4
|
+
modules so consumers can type adapters without importing pandas, matplotlib,
|
|
5
|
+
or filesystem-oriented helpers.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Mapping, Sequence
|
|
11
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
import pandas as pd
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"EvalSliceLike",
|
|
20
|
+
"PredictionReader",
|
|
21
|
+
"Scorer",
|
|
22
|
+
"SliceAwareScorer",
|
|
23
|
+
"Versioned",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@runtime_checkable
|
|
28
|
+
class Scorer(Protocol):
|
|
29
|
+
"""Anything exposing ``predict_proba(X) -> np.ndarray of P(positive)``.
|
|
30
|
+
|
|
31
|
+
Accepts ``list[str]``, ``np.ndarray``, or ``pd.Series`` of features.
|
|
32
|
+
Pandas is imported under ``TYPE_CHECKING`` only, so this Protocol
|
|
33
|
+
has no runtime pandas dependency.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def predict_proba( # pragma: no cover
|
|
37
|
+
self, X: Sequence[str] | np.ndarray | pd.Series
|
|
38
|
+
) -> np.ndarray:
|
|
39
|
+
"""Return one P(positive) score per input row."""
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@runtime_checkable
|
|
44
|
+
class SliceAwareScorer(Scorer, Protocol):
|
|
45
|
+
"""Optional scorer contract for cost-controlled slice skipping."""
|
|
46
|
+
|
|
47
|
+
def should_score_slice(self, slice_name: str) -> bool: # pragma: no cover
|
|
48
|
+
"""Return whether this scorer should run on the named slice."""
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@runtime_checkable
|
|
53
|
+
class Versioned(Protocol):
|
|
54
|
+
"""Anything exposing a stable version string."""
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def version(self) -> str: # pragma: no cover
|
|
58
|
+
"""Stable version string for this implementation."""
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@runtime_checkable
|
|
63
|
+
class EvalSliceLike(Protocol):
|
|
64
|
+
"""Pandas-free slice surface needed by evaluation contracts."""
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def name(self) -> str: # pragma: no cover
|
|
68
|
+
"""Stable slice identifier."""
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def y_true(self) -> np.ndarray: # pragma: no cover
|
|
73
|
+
"""Binary labels as a 1-D array."""
|
|
74
|
+
...
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def features(self) -> Sequence[str]: # pragma: no cover
|
|
78
|
+
"""Feature values passed to a scorer."""
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def strata(self) -> np.ndarray | None: # pragma: no cover
|
|
83
|
+
"""Optional stratum labels for slice-aware reporting."""
|
|
84
|
+
...
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@runtime_checkable
|
|
88
|
+
class PredictionReader(Protocol):
|
|
89
|
+
"""Reads manifest-referenced prediction artifacts into column arrays."""
|
|
90
|
+
|
|
91
|
+
def read_predictions( # pragma: no cover
|
|
92
|
+
self,
|
|
93
|
+
uri: str,
|
|
94
|
+
*,
|
|
95
|
+
columns: Mapping[str, str],
|
|
96
|
+
) -> Mapping[str, Sequence[object]]:
|
|
97
|
+
"""Return a column-oriented table for the requested artifact URI."""
|
|
98
|
+
...
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""Provenance helpers: file hashing, run-directory layout, optional git SHA, figure metadata.
|
|
2
|
+
|
|
3
|
+
All filesystem-touching functions are isolated here so library callers can
|
|
4
|
+
import :mod:`eval_toolkit.metrics` and :mod:`eval_toolkit.bootstrap` without
|
|
5
|
+
pulling in any IO surface.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import subprocess
|
|
12
|
+
import time
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from datetime import UTC, datetime
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"FileHash",
|
|
19
|
+
"FileHashMissing",
|
|
20
|
+
"capture_git_sha",
|
|
21
|
+
"compute_file_hash",
|
|
22
|
+
"figure_metadata",
|
|
23
|
+
"file_sha256",
|
|
24
|
+
"make_run_dir",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True, slots=True)
|
|
29
|
+
class FileHash:
|
|
30
|
+
"""Sentinel-style hit for a successful file hash.
|
|
31
|
+
|
|
32
|
+
Returned (alongside :class:`FileHashMissing`) from
|
|
33
|
+
:func:`compute_file_hash` so callers can pattern-match on the outcome
|
|
34
|
+
rather than testing for a magic ``None`` (F5.1).
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
sha256: str
|
|
38
|
+
|
|
39
|
+
def __post_init__(self) -> None:
|
|
40
|
+
"""Validate the hex-digest shape."""
|
|
41
|
+
if not isinstance(self.sha256, str) or len(self.sha256) != 64:
|
|
42
|
+
raise ValueError(
|
|
43
|
+
f"FileHash.sha256 must be a 64-character hex digest, got {self.sha256!r}"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True, slots=True)
|
|
48
|
+
class FileHashMissing:
|
|
49
|
+
"""Sentinel-style miss for a file hash that could not be computed.
|
|
50
|
+
|
|
51
|
+
Carries the reason ``"missing"`` (path does not exist) or
|
|
52
|
+
``"not_a_file"`` (path exists but is not a regular file). Pair with
|
|
53
|
+
:class:`FileHash` for exhaustive pattern matching.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
reason: str
|
|
57
|
+
path: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def compute_file_hash(path: Path | str) -> FileHash | FileHashMissing:
|
|
61
|
+
"""SHA-256 hex digest of an existing file (sentinel-typed).
|
|
62
|
+
|
|
63
|
+
Replaces the v1 :func:`file_sha256` ``str | None`` shape with an
|
|
64
|
+
explicit union (F5.1). Callers can match on the union members
|
|
65
|
+
instead of testing the return for ``None``:
|
|
66
|
+
|
|
67
|
+
.. code-block:: python
|
|
68
|
+
|
|
69
|
+
match compute_file_hash(path):
|
|
70
|
+
case FileHash(sha256=h):
|
|
71
|
+
record["digest"] = f"sha256:{h}"
|
|
72
|
+
case FileHashMissing(reason="missing"):
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
:func:`file_sha256` remains available as a backward-compatible thin
|
|
76
|
+
wrapper that flattens to ``str | None``.
|
|
77
|
+
|
|
78
|
+
Examples
|
|
79
|
+
--------
|
|
80
|
+
>>> import tempfile
|
|
81
|
+
>>> from pathlib import Path
|
|
82
|
+
>>> with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
|
|
83
|
+
... _ = f.write("hello\\n")
|
|
84
|
+
... p = Path(f.name)
|
|
85
|
+
>>> result = compute_file_hash(p)
|
|
86
|
+
>>> isinstance(result, FileHash) and len(result.sha256) == 64
|
|
87
|
+
True
|
|
88
|
+
>>> compute_file_hash("/no/such/file")
|
|
89
|
+
FileHashMissing(reason='missing', path='/no/such/file')
|
|
90
|
+
"""
|
|
91
|
+
p = Path(path)
|
|
92
|
+
if not p.exists():
|
|
93
|
+
return FileHashMissing(reason="missing", path=str(path))
|
|
94
|
+
if not p.is_file():
|
|
95
|
+
return FileHashMissing(reason="not_a_file", path=str(path))
|
|
96
|
+
h = hashlib.sha256()
|
|
97
|
+
with p.open("rb") as fh:
|
|
98
|
+
for chunk in iter(lambda: fh.read(1024 * 1024), b""):
|
|
99
|
+
h.update(chunk)
|
|
100
|
+
return FileHash(sha256=h.hexdigest())
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def file_sha256(path: Path | str, *, strict: bool = False) -> str | None:
|
|
104
|
+
"""SHA-256 hex digest of an existing file.
|
|
105
|
+
|
|
106
|
+
Default behavior is permissive: returns ``None`` if the path does not
|
|
107
|
+
exist or is not a regular file. Pass ``strict=True`` to raise
|
|
108
|
+
:class:`FileNotFoundError` instead — useful when the caller's invariants
|
|
109
|
+
require the digest to exist.
|
|
110
|
+
|
|
111
|
+
.. note::
|
|
112
|
+
New code should prefer :func:`compute_file_hash`, which returns a
|
|
113
|
+
:class:`FileHash` / :class:`FileHashMissing` union and avoids the
|
|
114
|
+
``None`` ambiguity (F5.1). This helper is retained for backward
|
|
115
|
+
compatibility.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
path : pathlib.Path or str
|
|
120
|
+
strict : bool, optional
|
|
121
|
+
If ``True``, raise :class:`FileNotFoundError` when ``path`` is missing
|
|
122
|
+
or not a regular file. Default ``False`` (return ``None``).
|
|
123
|
+
|
|
124
|
+
Returns
|
|
125
|
+
-------
|
|
126
|
+
str or None
|
|
127
|
+
64-character hex digest. ``None`` only when ``strict=False`` and the
|
|
128
|
+
path is absent / not a regular file.
|
|
129
|
+
|
|
130
|
+
Raises
|
|
131
|
+
------
|
|
132
|
+
FileNotFoundError
|
|
133
|
+
Only when ``strict=True`` and ``path`` is missing or not a regular file.
|
|
134
|
+
|
|
135
|
+
Examples
|
|
136
|
+
--------
|
|
137
|
+
>>> import tempfile
|
|
138
|
+
>>> from pathlib import Path
|
|
139
|
+
>>> with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
|
|
140
|
+
... _ = f.write("hello\\n")
|
|
141
|
+
... p = Path(f.name)
|
|
142
|
+
>>> digest = file_sha256(p)
|
|
143
|
+
>>> len(digest)
|
|
144
|
+
64
|
|
145
|
+
>>> file_sha256("/no/such/file") is None
|
|
146
|
+
True
|
|
147
|
+
>>> try:
|
|
148
|
+
... file_sha256("/no/such/file", strict=True)
|
|
149
|
+
... except FileNotFoundError:
|
|
150
|
+
... print("raised")
|
|
151
|
+
raised
|
|
152
|
+
"""
|
|
153
|
+
result = compute_file_hash(path)
|
|
154
|
+
if isinstance(result, FileHash):
|
|
155
|
+
return result.sha256
|
|
156
|
+
if strict:
|
|
157
|
+
raise FileNotFoundError(f"file_sha256: path missing or not a regular file: {result.path}")
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def make_run_dir(base: Path | str, prefix: str = "run") -> Path:
|
|
162
|
+
"""Create and return a timestamped run directory.
|
|
163
|
+
|
|
164
|
+
Format: ``{base}/{prefix}_{YYYY-MM-DD_HHMMSS}``.
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
base : pathlib.Path or str
|
|
169
|
+
Parent directory for run dirs (e.g., ``evals/``).
|
|
170
|
+
prefix : str, optional
|
|
171
|
+
Prefix before the timestamp. Default ``"run"``.
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
pathlib.Path
|
|
176
|
+
The newly-created (or pre-existing) run directory.
|
|
177
|
+
"""
|
|
178
|
+
ts = time.strftime("%Y-%m-%d_%H%M%S")
|
|
179
|
+
run_dir = Path(base) / f"{prefix}_{ts}"
|
|
180
|
+
run_dir.mkdir(parents=True, exist_ok=True)
|
|
181
|
+
return run_dir
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def capture_git_sha(repo_root: Path | str | None = None) -> str | None:
|
|
185
|
+
"""Current HEAD full SHA, or ``None`` if not in a git repo.
|
|
186
|
+
|
|
187
|
+
Optional and injectable: callers that want a specific SHA (e.g., from CI
|
|
188
|
+
metadata) should pass it directly to whichever function consumes it
|
|
189
|
+
rather than calling this helper.
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
repo_root : pathlib.Path or str or None, optional
|
|
194
|
+
Working directory for ``git``. ``None`` uses the current process cwd.
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
str or None
|
|
199
|
+
Full SHA hex string, or ``None`` if ``git`` is unavailable or the
|
|
200
|
+
directory is not inside a repository.
|
|
201
|
+
"""
|
|
202
|
+
cwd = Path(repo_root) if repo_root is not None else None
|
|
203
|
+
try:
|
|
204
|
+
result = subprocess.run(
|
|
205
|
+
["git", "rev-parse", "HEAD"],
|
|
206
|
+
capture_output=True,
|
|
207
|
+
text=True,
|
|
208
|
+
check=True,
|
|
209
|
+
cwd=cwd,
|
|
210
|
+
)
|
|
211
|
+
return result.stdout.strip()
|
|
212
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def figure_metadata(provenance: dict[str, str], dpi: int = 300) -> dict[str, str]:
|
|
217
|
+
"""Build a JSON-serializable provenance dict for ``save_figure``.
|
|
218
|
+
|
|
219
|
+
Combines caller-supplied keys with auto-fields ``timestamp_utc``
|
|
220
|
+
(ISO-8601), ``matplotlib_version``, and ``figure_dpi``. Matches the
|
|
221
|
+
schema produced inside :func:`eval_toolkit.plotting.save_figure`.
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
provenance : dict[str, str]
|
|
226
|
+
Caller-supplied provenance keys (e.g., ``git_sha``, ``run_id``).
|
|
227
|
+
dpi : int, optional
|
|
228
|
+
DPI to record. Default 300.
|
|
229
|
+
|
|
230
|
+
Returns
|
|
231
|
+
-------
|
|
232
|
+
dict[str, str]
|
|
233
|
+
Combined provenance dict.
|
|
234
|
+
|
|
235
|
+
Examples
|
|
236
|
+
--------
|
|
237
|
+
>>> meta = figure_metadata({"run_id": "test-001"}, dpi=150)
|
|
238
|
+
>>> sorted(meta.keys())
|
|
239
|
+
['figure_dpi', 'matplotlib_version', 'run_id', 'timestamp_utc']
|
|
240
|
+
>>> meta["figure_dpi"]
|
|
241
|
+
'150'
|
|
242
|
+
"""
|
|
243
|
+
try:
|
|
244
|
+
import matplotlib as mpl # noqa: PLC0415
|
|
245
|
+
|
|
246
|
+
matplotlib_version = str(mpl.__version__)
|
|
247
|
+
except ImportError:
|
|
248
|
+
matplotlib_version = "unavailable"
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
**provenance,
|
|
252
|
+
"timestamp_utc": datetime.now(UTC).isoformat(timespec="seconds"),
|
|
253
|
+
"matplotlib_version": matplotlib_version,
|
|
254
|
+
"figure_dpi": str(dpi),
|
|
255
|
+
}
|
eval_toolkit/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://eval-toolkit/schemas/manifest.v1.json",
|
|
4
|
+
"version": "1",
|
|
5
|
+
"title": "eval-toolkit RunManifest",
|
|
6
|
+
"description": "Schema for run_dir/manifest.json. Produced by eval_toolkit.manifest.write_manifest. NeurIPS Reproducibility Checklist-aligned.",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"required": ["schema_version", "run_id", "code_versions", "env"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": {
|
|
11
|
+
"const": "v1"
|
|
12
|
+
},
|
|
13
|
+
"run_id": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"minLength": 1
|
|
16
|
+
},
|
|
17
|
+
"git_sha": {
|
|
18
|
+
"type": ["string", "null"]
|
|
19
|
+
},
|
|
20
|
+
"dirty_flag": {
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"description": "True iff the working tree had uncommitted changes at run time (NeurIPS clean-replay concern)."
|
|
23
|
+
},
|
|
24
|
+
"code_versions": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"description": "{package_name: version_string} — at minimum eval_toolkit.",
|
|
27
|
+
"additionalProperties": {
|
|
28
|
+
"type": "string"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"seeds": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"description": "{source: seed} for global / bootstrap / torch / dataloader RNGs.",
|
|
34
|
+
"additionalProperties": {
|
|
35
|
+
"type": "integer"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"data_hashes": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"description": "{logical_name: 'sha256:...'} for input artifacts.",
|
|
41
|
+
"additionalProperties": {
|
|
42
|
+
"type": "string"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"config_hash": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "SHA-256 over the canonical-JSON encoding of the eval config."
|
|
48
|
+
},
|
|
49
|
+
"env": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"description": "Environment fingerprint (python, platform, key dep versions).",
|
|
52
|
+
"additionalProperties": {
|
|
53
|
+
"type": "string"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"gpu_info": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"description": "{name, count, memory_gb} from nvidia-smi (empty dict if unavailable).",
|
|
59
|
+
"additionalProperties": {
|
|
60
|
+
"type": "string"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"cuda_version": {
|
|
64
|
+
"type": ["string", "null"]
|
|
65
|
+
},
|
|
66
|
+
"wall_clock_seconds": {
|
|
67
|
+
"type": ["number", "null"]
|
|
68
|
+
},
|
|
69
|
+
"versioned_objects": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"description": "{name: version} for any Tier-2 implementation exposing a `version` attribute.",
|
|
72
|
+
"additionalProperties": {
|
|
73
|
+
"type": "string"
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"leakage_report": {
|
|
77
|
+
"type": ["object", "null"],
|
|
78
|
+
"description": "Serialized LeakageReport (or null if no leakage checks ran).",
|
|
79
|
+
"properties": {
|
|
80
|
+
"findings": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"items": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"required": ["check_name", "severity", "drop_indices", "evidence", "message", "n_affected"],
|
|
85
|
+
"properties": {
|
|
86
|
+
"check_name": {"type": "string"},
|
|
87
|
+
"severity": {"enum": ["error", "warning", "info"]},
|
|
88
|
+
"drop_indices": {"type": "object"},
|
|
89
|
+
"evidence": {"type": "object"},
|
|
90
|
+
"message": {"type": "string"},
|
|
91
|
+
"n_affected": {"type": "integer", "minimum": 0}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"source_roles": {
|
|
98
|
+
"type": "array",
|
|
99
|
+
"description": "Optional generic source-role metadata for data-quality and evidence audits. Recommended roles: train, calibration, locked_eval, external_diagnostic, excluded; custom roles are allowed.",
|
|
100
|
+
"items": {
|
|
101
|
+
"type": "object",
|
|
102
|
+
"required": ["source", "role"],
|
|
103
|
+
"properties": {
|
|
104
|
+
"source": {"type": "string", "minLength": 1},
|
|
105
|
+
"role": {"type": "string", "minLength": 1},
|
|
106
|
+
"n_rows": {"type": "integer", "minimum": 0},
|
|
107
|
+
"notes": {"type": "string"},
|
|
108
|
+
"metadata": {"type": "object"}
|
|
109
|
+
},
|
|
110
|
+
"additionalProperties": true
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"guardrails": {
|
|
114
|
+
"type": "array",
|
|
115
|
+
"description": "Optional predeclared guardrails for claim/evidence discipline.",
|
|
116
|
+
"items": {"type": "string", "minLength": 1}
|
|
117
|
+
},
|
|
118
|
+
"prediction_artifacts": {
|
|
119
|
+
"type": "array",
|
|
120
|
+
"description": "Optional references to retained prediction artifacts. The toolkit records artifact metadata and column mappings without mandating a storage format.",
|
|
121
|
+
"items": {
|
|
122
|
+
"type": "object",
|
|
123
|
+
"required": ["uri", "media_type", "columns"],
|
|
124
|
+
"properties": {
|
|
125
|
+
"uri": {"type": "string", "minLength": 1},
|
|
126
|
+
"media_type": {"type": "string", "minLength": 1},
|
|
127
|
+
"sha256": {"type": "string"},
|
|
128
|
+
"n_rows": {"type": "integer", "minimum": 0},
|
|
129
|
+
"role": {"type": "string", "minLength": 1},
|
|
130
|
+
"metadata": {"type": "object"},
|
|
131
|
+
"columns": {
|
|
132
|
+
"type": "object",
|
|
133
|
+
"required": ["label", "score"],
|
|
134
|
+
"properties": {
|
|
135
|
+
"row_id": {"type": "string", "minLength": 1},
|
|
136
|
+
"content_hash": {"type": "string", "minLength": 1},
|
|
137
|
+
"label": {"type": "string", "minLength": 1},
|
|
138
|
+
"score": {"type": "string", "minLength": 1},
|
|
139
|
+
"scorer": {"type": "string", "minLength": 1},
|
|
140
|
+
"slice": {"type": "string", "minLength": 1},
|
|
141
|
+
"text": {"type": "string", "minLength": 1},
|
|
142
|
+
"provenance": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"additionalProperties": {"type": "string"}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"additionalProperties": true
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"additionalProperties": true
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"additionalProperties": true
|
|
155
|
+
}
|