sediment-api 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.
- sediment_api/__init__.py +6 -0
- sediment_api/config.py +230 -0
- sediment_api/database.py +43 -0
- sediment_api/deps.py +191 -0
- sediment_api/main.py +212 -0
- sediment_api/mirror_gc.py +151 -0
- sediment_api/reports/__init__.py +9 -0
- sediment_api/reports/abandonment_report.py +123 -0
- sediment_api/reports/attribution_share_report.py +298 -0
- sediment_api/reports/dataset_diagnostics.py +311 -0
- sediment_api/reports/label_confidence_inspection.py +438 -0
- sediment_api/reports/lifecycle_report.py +73 -0
- sediment_api/reports/merge_retention_report.py +165 -0
- sediment_api/reports/model_report.py +1534 -0
- sediment_api/reports/precision_report.py +182 -0
- sediment_api/reports/recovery_yield_report.py +132 -0
- sediment_api/routers/__init__.py +3 -0
- sediment_api/routers/ci_vendor.py +149 -0
- sediment_api/routers/forge.py +452 -0
- sediment_api/routers/gateway.py +137 -0
- sediment_api/routers/otlp.py +120 -0
- sediment_api/routers/query.py +1010 -0
- sediment_api/routers/reports.py +116 -0
- sediment_api/routers/v1.py +170 -0
- sediment_api/services/__init__.py +2 -0
- sediment_api/services/operational_reports.py +391 -0
- sediment_api/worker.py +242 -0
- sediment_api/workers.py +382 -0
- sediment_api-0.1.0.dist-info/METADATA +20 -0
- sediment_api-0.1.0.dist-info/RECORD +32 -0
- sediment_api-0.1.0.dist-info/WHEEL +4 -0
- sediment_api-0.1.0.dist-info/licenses/LICENSE +661 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
"""DPO/SFT dataset diagnostics.
|
|
3
|
+
|
|
4
|
+
Prints ``sediment_export.generate_dataset_diagnostics`` for one org: compact
|
|
5
|
+
tables on stdout by default, or ``--json`` for machines.
|
|
6
|
+
|
|
7
|
+
sediment report dataset-diagnostics --org acme-corp
|
|
8
|
+
sediment report dataset-diagnostics --org acme-corp --json
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
from dataclasses import asdict
|
|
18
|
+
|
|
19
|
+
from sediment_core import normalize_org_id
|
|
20
|
+
from sediment_derive import MirrorManager
|
|
21
|
+
from sediment_export import (
|
|
22
|
+
DEFAULT_DPO_NEAR_DUPLICATE_THRESHOLD,
|
|
23
|
+
DPOPolicy,
|
|
24
|
+
DatasetDiagnostics,
|
|
25
|
+
SFTPolicy,
|
|
26
|
+
generate_dataset_diagnostics,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from ..database import add_database_url_argument, one_shot_fact_store
|
|
30
|
+
|
|
31
|
+
_DEFAULT_MIRROR_PATH = "./mirrors"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _recipe_label(recipe_id: str, recipe_version: int) -> str:
|
|
35
|
+
return f"{recipe_id} v{recipe_version}"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _source_label(
|
|
39
|
+
eligibility_source: str | None,
|
|
40
|
+
chosen_label_source: str | None,
|
|
41
|
+
rejected_label_source: str | None,
|
|
42
|
+
) -> str:
|
|
43
|
+
if eligibility_source is not None:
|
|
44
|
+
return eligibility_source
|
|
45
|
+
return f"{chosen_label_source}/{rejected_label_source}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _print_balance(report: DatasetDiagnostics) -> None:
|
|
49
|
+
print("class/model balance")
|
|
50
|
+
if not report.model_balance:
|
|
51
|
+
print(" no data")
|
|
52
|
+
return
|
|
53
|
+
print(f"{'dataset':<8} {'recipe':<16} {'source':<32} {'model':<28} {'rows':>8}")
|
|
54
|
+
for row in report.model_balance:
|
|
55
|
+
source = _source_label(
|
|
56
|
+
row.eligibility_source,
|
|
57
|
+
row.chosen_label_source,
|
|
58
|
+
row.rejected_label_source,
|
|
59
|
+
)
|
|
60
|
+
print(
|
|
61
|
+
f"{row.dataset:<8} {_recipe_label(row.recipe_id, row.recipe_version):<16} "
|
|
62
|
+
f"{source:<32} {row.model:<28} {row.rows:>8}"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _print_distributions(report: DatasetDiagnostics) -> None:
|
|
67
|
+
print("confidence distribution")
|
|
68
|
+
if not report.confidence_distributions:
|
|
69
|
+
print(" no data")
|
|
70
|
+
return
|
|
71
|
+
print(
|
|
72
|
+
f"{'dataset':<8} {'recipe':<16} {'source':<32} {'metric':<12} "
|
|
73
|
+
f"{'model':<28} {'count':>7} {'mean':>8} {'median':>8} {'min':>8} "
|
|
74
|
+
f"{'max':>8} {'stdev':>8}"
|
|
75
|
+
)
|
|
76
|
+
for row in report.confidence_distributions:
|
|
77
|
+
stats = row.stats
|
|
78
|
+
source = _source_label(
|
|
79
|
+
row.eligibility_source,
|
|
80
|
+
row.chosen_label_source,
|
|
81
|
+
row.rejected_label_source,
|
|
82
|
+
)
|
|
83
|
+
print(
|
|
84
|
+
f"{row.dataset:<8} {_recipe_label(row.recipe_id, row.recipe_version):<16} "
|
|
85
|
+
f"{source:<32} {row.metric:<12} {row.model:<28} "
|
|
86
|
+
f"{stats.count:>7} {stats.mean:>8.3f} {stats.median:>8.3f} "
|
|
87
|
+
f"{stats.min:>8.3f} {stats.max:>8.3f} {stats.stdev:>8.3f}"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _print_duplicates(report: DatasetDiagnostics) -> None:
|
|
92
|
+
print("cross-split prompt duplicates")
|
|
93
|
+
for row in report.cross_split_duplicates:
|
|
94
|
+
source = _source_label(
|
|
95
|
+
row.eligibility_source,
|
|
96
|
+
row.chosen_label_source,
|
|
97
|
+
row.rejected_label_source,
|
|
98
|
+
)
|
|
99
|
+
print(
|
|
100
|
+
f"{row.dataset} {_recipe_label(row.recipe_id, row.recipe_version)} "
|
|
101
|
+
f"{source}: {row.duplicate_prompt_count}"
|
|
102
|
+
)
|
|
103
|
+
for example in row.examples:
|
|
104
|
+
print(
|
|
105
|
+
f" train={example.train_count} eval={example.eval_count} "
|
|
106
|
+
f"prompt={example.prompt}"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _print_floor_exclusions(report: DatasetDiagnostics) -> None:
|
|
111
|
+
print("confidence-floor exclusions")
|
|
112
|
+
if not report.confidence_floor_exclusions:
|
|
113
|
+
print(" no data")
|
|
114
|
+
return
|
|
115
|
+
print(
|
|
116
|
+
f"{'dataset':<8} {'recipe':<16} {'source':<32} {'model':<28} "
|
|
117
|
+
f"{'eligible':>10} {'excluded':>10} {'rate':>8}"
|
|
118
|
+
)
|
|
119
|
+
for row in report.confidence_floor_exclusions:
|
|
120
|
+
print(
|
|
121
|
+
f"{row.dataset:<8} {_recipe_label(row.recipe_id, row.recipe_version):<16} "
|
|
122
|
+
f"{row.eligibility_source:<32} {row.model:<28} "
|
|
123
|
+
f"{row.otherwise_eligible_completions:>10} "
|
|
124
|
+
f"{row.excluded_by_floor:>10} {row.exclusion_rate:>8.3f}"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _print_dpo_near_duplicates(report: DatasetDiagnostics) -> None:
|
|
129
|
+
print("dpo near-duplicate prompt buckets")
|
|
130
|
+
if not report.dpo_near_duplicate_buckets:
|
|
131
|
+
print(" no data")
|
|
132
|
+
return
|
|
133
|
+
for row in report.dpo_near_duplicate_buckets:
|
|
134
|
+
print(
|
|
135
|
+
f" recipe={row.recipe_id} v{row.recipe_version} "
|
|
136
|
+
f"sources={row.chosen_label_source}/{row.rejected_label_source} "
|
|
137
|
+
f"buckets={row.bucket_count} compared={row.comparable_pair_count} "
|
|
138
|
+
f"threshold={row.threshold:.3f} near_misses={row.near_miss_pair_count}"
|
|
139
|
+
)
|
|
140
|
+
for example in row.examples:
|
|
141
|
+
print(
|
|
142
|
+
f" org={example.org_id} model={example.model} "
|
|
143
|
+
f"similarity={example.similarity:.3f}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _print_dpo_bucket_sparsity(report: DatasetDiagnostics) -> None:
|
|
148
|
+
print("dpo prompt-bucket sparsity")
|
|
149
|
+
if not report.dpo_bucket_sparsity:
|
|
150
|
+
print(" no data")
|
|
151
|
+
return
|
|
152
|
+
for sparsity in report.dpo_bucket_sparsity:
|
|
153
|
+
print(
|
|
154
|
+
f" recipe={sparsity.recipe_id} v{sparsity.recipe_version} "
|
|
155
|
+
f"sources={sparsity.chosen_label_source}/{sparsity.rejected_label_source}"
|
|
156
|
+
)
|
|
157
|
+
print(f" buckets: {sparsity.total_buckets}")
|
|
158
|
+
print(f" candidates: {sparsity.total_candidates}")
|
|
159
|
+
print(
|
|
160
|
+
" singleton candidates: "
|
|
161
|
+
f"{sparsity.singleton_candidates} "
|
|
162
|
+
f"({sparsity.singleton_candidate_fraction:.1%})"
|
|
163
|
+
)
|
|
164
|
+
print(f" cap-hit buckets: {sparsity.cap_hit_buckets}")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _format_counts(counts: dict[str, int]) -> str:
|
|
168
|
+
return ", ".join(f"{key}={value}" for key, value in counts.items()) or "-"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _print_fate_diagnostic(report: DatasetDiagnostics) -> None:
|
|
172
|
+
print("Fate diagnostic")
|
|
173
|
+
print(f" fates: {_format_counts(report.fate.fates)}")
|
|
174
|
+
print(
|
|
175
|
+
" human-explicit accept fates: "
|
|
176
|
+
f"{_format_counts(report.fate.explicit_accept_fates)}"
|
|
177
|
+
)
|
|
178
|
+
print(
|
|
179
|
+
" fates with external changes: "
|
|
180
|
+
f"{_format_counts(report.fate.fates_with_external_changes)}"
|
|
181
|
+
)
|
|
182
|
+
print(f" derivation skipped: {_format_counts(report.fate.skipped)}")
|
|
183
|
+
print(f" provenance: {report.fate.provenance or '-'}")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _print_table(report: DatasetDiagnostics) -> None:
|
|
187
|
+
_print_balance(report)
|
|
188
|
+
print()
|
|
189
|
+
_print_distributions(report)
|
|
190
|
+
print()
|
|
191
|
+
_print_dpo_bucket_sparsity(report)
|
|
192
|
+
print()
|
|
193
|
+
_print_duplicates(report)
|
|
194
|
+
print()
|
|
195
|
+
_print_floor_exclusions(report)
|
|
196
|
+
print()
|
|
197
|
+
_print_dpo_near_duplicates(report)
|
|
198
|
+
print()
|
|
199
|
+
_print_fate_diagnostic(report)
|
|
200
|
+
print()
|
|
201
|
+
print("abandonment")
|
|
202
|
+
print(f" abandoned sessions: {report.abandonment.abandoned_sessions}")
|
|
203
|
+
print(f" grade-eligible sessions: {report.abandonment.grade_eligible_sessions}")
|
|
204
|
+
print(f" implicit-only sessions: {report.abandonment.implicit_only_sessions}")
|
|
205
|
+
print(f" negative completions: {report.abandonment.negative_completions}")
|
|
206
|
+
print(
|
|
207
|
+
f" explicit accepts unjoined: {report.abandonment.explicit_accepts_unjoined}"
|
|
208
|
+
)
|
|
209
|
+
skipped = ", ".join(
|
|
210
|
+
f"{reason}={count}"
|
|
211
|
+
for reason, count in report.abandonment.derivation_skipped.items()
|
|
212
|
+
)
|
|
213
|
+
print(f" derivation skipped: {skipped or '-'}")
|
|
214
|
+
print(f" provenance: {report.abandonment.provenance or '-'}")
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
218
|
+
"""The argv contract for this report, extracted so the generated
|
|
219
|
+
CLI reference can walk it."""
|
|
220
|
+
parser = argparse.ArgumentParser(
|
|
221
|
+
prog="dataset_diagnostics", description=__doc__.splitlines()[0]
|
|
222
|
+
)
|
|
223
|
+
parser.add_argument(
|
|
224
|
+
"--org",
|
|
225
|
+
default=os.environ.get("SEDIMENT_ORG_ID"),
|
|
226
|
+
required=not os.environ.get("SEDIMENT_ORG_ID"),
|
|
227
|
+
help="org id (default: $SEDIMENT_ORG_ID)",
|
|
228
|
+
)
|
|
229
|
+
parser.add_argument(
|
|
230
|
+
"--json", action="store_true", help="emit JSON report instead of tables"
|
|
231
|
+
)
|
|
232
|
+
parser.add_argument(
|
|
233
|
+
"--dpo-recipe",
|
|
234
|
+
choices=("dpo_human", "dpo_outcome"),
|
|
235
|
+
default="dpo_human",
|
|
236
|
+
help="DPO evidence recipe (default: dpo_human)",
|
|
237
|
+
)
|
|
238
|
+
parser.add_argument(
|
|
239
|
+
"--sft-recipe",
|
|
240
|
+
choices=("sft_curated", "sft_verified"),
|
|
241
|
+
default="sft_curated",
|
|
242
|
+
help="SFT evidence recipe (default: sft_curated)",
|
|
243
|
+
)
|
|
244
|
+
parser.add_argument(
|
|
245
|
+
"--max-examples",
|
|
246
|
+
type=int,
|
|
247
|
+
default=5,
|
|
248
|
+
help="maximum examples per diagnostics section (default: 5)",
|
|
249
|
+
)
|
|
250
|
+
parser.add_argument(
|
|
251
|
+
"--dpo-near-duplicate-threshold",
|
|
252
|
+
type=float,
|
|
253
|
+
default=DEFAULT_DPO_NEAR_DUPLICATE_THRESHOLD,
|
|
254
|
+
help=(
|
|
255
|
+
"minimum token Jaccard similarity for DPO near-duplicate prompt "
|
|
256
|
+
f"bucket flags (default: {DEFAULT_DPO_NEAR_DUPLICATE_THRESHOLD})"
|
|
257
|
+
),
|
|
258
|
+
)
|
|
259
|
+
add_database_url_argument(parser)
|
|
260
|
+
parser.add_argument(
|
|
261
|
+
"--mirror-path",
|
|
262
|
+
default=os.environ.get("SEDIMENT_MIRROR_PATH", _DEFAULT_MIRROR_PATH),
|
|
263
|
+
help="base dir for git mirrors (default: $SEDIMENT_MIRROR_PATH or "
|
|
264
|
+
f"{_DEFAULT_MIRROR_PATH!r})",
|
|
265
|
+
)
|
|
266
|
+
return parser
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def main(argv: list[str] | None = None) -> int:
|
|
270
|
+
args = build_parser().parse_args(argv)
|
|
271
|
+
|
|
272
|
+
if args.max_examples < 0:
|
|
273
|
+
print("error: --max-examples must be >= 0", file=sys.stderr)
|
|
274
|
+
return 2
|
|
275
|
+
if not 0.0 <= args.dpo_near_duplicate_threshold <= 1.0:
|
|
276
|
+
print(
|
|
277
|
+
"error: --dpo-near-duplicate-threshold must be between 0.0 and 1.0",
|
|
278
|
+
file=sys.stderr,
|
|
279
|
+
)
|
|
280
|
+
return 2
|
|
281
|
+
|
|
282
|
+
try:
|
|
283
|
+
org_id = normalize_org_id(args.org)
|
|
284
|
+
except ValueError as exc:
|
|
285
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
286
|
+
return 2
|
|
287
|
+
|
|
288
|
+
with one_shot_fact_store(
|
|
289
|
+
args.database_url, operation="generate dataset diagnostics"
|
|
290
|
+
) as store:
|
|
291
|
+
report = generate_dataset_diagnostics(
|
|
292
|
+
store,
|
|
293
|
+
MirrorManager(args.mirror_path),
|
|
294
|
+
org_id,
|
|
295
|
+
dpo_policy=DPOPolicy(recipe_id=args.dpo_recipe),
|
|
296
|
+
sft_policy=SFTPolicy(recipe_id=args.sft_recipe),
|
|
297
|
+
max_duplicate_examples=args.max_examples,
|
|
298
|
+
dpo_near_duplicate_threshold=args.dpo_near_duplicate_threshold,
|
|
299
|
+
max_near_duplicate_examples=args.max_examples,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
if args.json:
|
|
303
|
+
print(json.dumps(asdict(report), indent=2))
|
|
304
|
+
else:
|
|
305
|
+
print(f"org: {org_id}")
|
|
306
|
+
_print_table(report)
|
|
307
|
+
return 0
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
if __name__ == "__main__":
|
|
311
|
+
raise SystemExit(main())
|