Leon4gr45 commited on
Commit
3537231
·
verified ·
1 Parent(s): 4bb804c

Delete benchmarks/beam/report.py

Browse files
Files changed (1) hide show
  1. benchmarks/beam/report.py +0 -455
benchmarks/beam/report.py DELETED
@@ -1,455 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import tempfile
5
- from datetime import datetime, timezone
6
- from pathlib import Path
7
- from typing import Any
8
-
9
- from rich.console import Console
10
- from rich.table import Table
11
-
12
- from beam.config import ABILITY_NAMES, BENCHMARKS_ROOT
13
- from beam.ingest import load_jsonl
14
- from beam.metrics import aggregate_answers
15
-
16
- console = Console()
17
-
18
- REPORTS_PATH = BENCHMARKS_ROOT / "REPORTS.json"
19
- _BENCHMARK_ID = "beam"
20
- _BENCHMARK_NAME = "BEAM"
21
- _REPORTS_DESCRIPTION = (
22
- "BrainAPI benchmark results. Top published scores across suites. "
23
- "Updated when a suite evaluate/report completes successfully."
24
- )
25
- _LEADERBOARD_ENTRY_KEYS = (
26
- "run_id",
27
- "track",
28
- "bench_profile",
29
- "scope",
30
- "size",
31
- "samples",
32
- "n_questions",
33
- "headline_score",
34
- "headline_score_pct",
35
- "per_ability",
36
- "answer_model",
37
- "judge_model",
38
- "brain",
39
- "git_sha",
40
- "report_path",
41
- "recorded_at",
42
- )
43
- _MIN_LEADERBOARD_N = 20
44
-
45
-
46
- def _latest_ingest_rows(ingest_rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
47
- latest: dict[tuple[str, str], dict[str, Any]] = {}
48
- for row in ingest_rows:
49
- key = (
50
- str(row.get("sample_id") or ""),
51
- str(row.get("unit_id") or row.get("session_key") or ""),
52
- )
53
- latest[key] = row
54
- return list(latest.values())
55
-
56
-
57
- def _latest_answer_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
58
- latest: dict[tuple[str, str, int], dict[str, Any]] = {}
59
- for row in rows:
60
- key = (
61
- str(row.get("sample_id") or ""),
62
- str(row.get("ability") or ""),
63
- int(row.get("qa_index") or 0),
64
- )
65
- latest[key] = row
66
- return list(latest.values())
67
-
68
-
69
- def build_report(run_dir: Path) -> dict[str, Any]:
70
- answers_path = run_dir / "answers.jsonl"
71
- ingest_path = run_dir / "ingest.jsonl"
72
- manifest_path = run_dir / "manifest.json"
73
-
74
- all_answer_rows = load_jsonl(answers_path)
75
- scored_rows = [r for r in all_answer_rows if not r.get("error")]
76
- answers = _latest_answer_rows(scored_rows)
77
- errored = [r for r in all_answer_rows if r.get("error")]
78
- duplicates_dropped = len(scored_rows) - len(answers)
79
- empty_predictions = sum(
80
- 1 for r in answers if not str(r.get("prediction") or "").strip()
81
- )
82
- ingest_rows = load_jsonl(ingest_path)
83
- ingest_latest = _latest_ingest_rows(ingest_rows)
84
- manifest = {}
85
- if manifest_path.exists():
86
- manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
87
-
88
- metrics = aggregate_answers(answers)
89
- ingest_summary = {
90
- "n": len(ingest_latest),
91
- "completed": sum(1 for r in ingest_latest if r.get("status") == "completed"),
92
- "partial_failed": sum(
93
- 1 for r in ingest_latest if r.get("status") == "partial_failed"
94
- ),
95
- "failed": sum(1 for r in ingest_latest if r.get("status") == "failed"),
96
- "dry_run": sum(1 for r in ingest_latest if r.get("status") == "dry_run"),
97
- "attempts": len(ingest_rows),
98
- }
99
-
100
- integrity = {
101
- "rows_in_file": len(all_answer_rows),
102
- "errored_rows": len(errored),
103
- "scored_rows": len(answers),
104
- "duplicates_dropped": duplicates_dropped,
105
- "empty_predictions": empty_predictions,
106
- }
107
-
108
- report: dict[str, Any] = {
109
- "run_dir": str(run_dir),
110
- "manifest": manifest,
111
- "ingest": ingest_summary,
112
- "integrity": integrity,
113
- "metrics": metrics,
114
- "status": "ok",
115
- "warnings": [],
116
- }
117
- if ingest_summary["partial_failed"] or ingest_summary["failed"]:
118
- report["warnings"].append(
119
- "Some ingest units failed or partially failed; scores may be skewed."
120
- )
121
- if duplicates_dropped:
122
- report["warnings"].append(
123
- f"{duplicates_dropped} duplicate answer rows dropped."
124
- )
125
- if errored:
126
- report["warnings"].append(
127
- f"{len(errored)} rows errored and are excluded from every metric."
128
- )
129
-
130
- failure_reasons: list[str] = []
131
- if not answers:
132
- failure_reasons.append("no answer rows were scored")
133
- else:
134
- if empty_predictions == len(answers):
135
- failure_reasons.append("every scored answer is empty")
136
- if metrics.get("headline_score") == 0.0:
137
- failure_reasons.append("headline score is 0.0 across scored abilities")
138
- if failure_reasons:
139
- report["status"] = "failed"
140
- report["warnings"].insert(
141
- 0,
142
- "RUN FAILED WHOLESALE: "
143
- + "; ".join(failure_reasons)
144
- + ". The percentages below are not a measurement of quality.",
145
- )
146
- elif errored or duplicates_dropped:
147
- report["status"] = "degraded"
148
- return report
149
-
150
-
151
- def render_markdown(report: dict[str, Any]) -> str:
152
- metrics = report["metrics"]
153
- manifest = report.get("manifest") or {}
154
- integrity = report.get("integrity") or {}
155
- lines = ["# BEAM BrainAPI Benchmark Report", ""]
156
- if report.get("status") == "failed":
157
- lines.extend(
158
- [
159
- "> **RUN FAILED WHOLESALE — the numbers below are not a measurement.**",
160
- f"> {report['warnings'][0]}",
161
- "",
162
- ]
163
- )
164
- elif report.get("status") == "degraded":
165
- lines.extend(
166
- [
167
- "> **Run degraded** — errored or duplicate rows were dropped; "
168
- "see Warnings.",
169
- "",
170
- ]
171
- )
172
- lines.extend(
173
- [
174
- f"- Run dir: `{report['run_dir']}`",
175
- f"- Created: {manifest.get('created_at', 'n/a')}",
176
- f"- Size: {manifest.get('size', 'n/a')}",
177
- f"- Answer model: {manifest.get('answer_model', 'n/a')}",
178
- f"- Judge model: {manifest.get('judge_model', 'n/a')} "
179
- f"(provider: {manifest.get('judge_provider', 'n/a')}, "
180
- f"same family as answerer: "
181
- f"{manifest.get('judge_shares_answer_family', 'n/a')})",
182
- f"- Judge prompt variant: {manifest.get('judge_prompt_variant', 'n/a')}",
183
- f"- Git SHA: {manifest.get('git_sha', 'n/a')} "
184
- f"(dirty: {manifest.get('git_dirty', 'n/a')})",
185
- f"- Samples: {', '.join(manifest.get('samples', []) or ['n/a'])}",
186
- "",
187
- "## Run integrity",
188
- "",
189
- f"- Rows in answers.jsonl: {integrity.get('rows_in_file', 'n/a')}",
190
- f"- Scored unique questions: {integrity.get('scored_rows', 'n/a')}",
191
- f"- Errored rows (excluded): {integrity.get('errored_rows', 'n/a')}",
192
- f"- Duplicate rows dropped: {integrity.get('duplicates_dropped', 'n/a')}",
193
- f"- Empty predictions: {integrity.get('empty_predictions', 'n/a')}",
194
- "",
195
- "## Headline",
196
- "",
197
- f"- Mean ability score: **{_pct(metrics.get('headline_score'))}** "
198
- f"(n_questions={metrics.get('n_questions', 0)}, "
199
- f"abilities={metrics.get('n_abilities_scored', 0)})",
200
- "",
201
- "## Per-ability",
202
- "",
203
- "| Ability | N | Mean |",
204
- "|---|---:|---:|",
205
- ]
206
- )
207
- for ability in ABILITY_NAMES:
208
- bucket = (metrics.get("per_ability") or {}).get(ability) or {}
209
- lines.append(
210
- f"| {ability} | {bucket.get('n', 0)} | {_pct(bucket.get('mean'))} |"
211
- )
212
-
213
- lat = metrics.get("retrieval_latency_ms") or {}
214
- lines.extend(
215
- [
216
- "",
217
- "## Latency & tokens",
218
- "",
219
- f"- Retrieval latency p50: {_num(lat.get('p50'))} ms",
220
- f"- Retrieval latency p95: {_num(lat.get('p95'))} ms",
221
- f"- Retrieval latency mean: {_num(lat.get('mean'))} ms",
222
- f"- Total LLM tokens (answer+judge): {metrics.get('total_llm_tokens', 0)}",
223
- "",
224
- "## Ingest",
225
- "",
226
- f"- Units: {report['ingest'].get('n', 0)}",
227
- f"- Completed: {report['ingest'].get('completed', 0)}",
228
- f"- Partial failed: {report['ingest'].get('partial_failed', 0)}",
229
- f"- Failed: {report['ingest'].get('failed', 0)}",
230
- "",
231
- ]
232
- )
233
- if report.get("warnings"):
234
- lines.append("## Warnings")
235
- lines.append("")
236
- for warning in report["warnings"]:
237
- lines.append(f"- {warning}")
238
- lines.append("")
239
- return "\n".join(lines)
240
-
241
-
242
- def entry_from_report(report: dict[str, Any]) -> dict[str, Any] | None:
243
- if report.get("status") == "failed":
244
- return None
245
- metrics = report.get("metrics") or {}
246
- headline = metrics.get("headline_score")
247
- n_questions = int(metrics.get("n_questions") or 0)
248
- if headline is None or n_questions < _MIN_LEADERBOARD_N:
249
- return None
250
-
251
- manifest = report.get("manifest") or {}
252
- run_id = str(manifest.get("run_id") or "").strip()
253
- if not run_id:
254
- run_dir = str(report.get("run_dir") or "").rstrip("/")
255
- run_id = Path(run_dir).name if run_dir else ""
256
- if not run_id:
257
- return None
258
-
259
- samples = list(manifest.get("samples") or [])
260
- size = str(manifest.get("size") or "unknown")
261
- if len(samples) == 1:
262
- scope = str(samples[0])
263
- elif samples:
264
- scope = f"{size}:{len(samples)}"
265
- else:
266
- scope = size
267
-
268
- per_ability = {
269
- ability: ((metrics.get("per_ability") or {}).get(ability) or {}).get("mean")
270
- for ability in ABILITY_NAMES
271
- }
272
- acc = float(headline)
273
- now = datetime.now(timezone.utc).isoformat()
274
- bench_profile = manifest.get("bench_profile") or "product"
275
- track = "sota" if bench_profile == "sota" else "product"
276
- return {
277
- "run_id": run_id,
278
- "track": track,
279
- "bench_profile": bench_profile,
280
- "scope": scope,
281
- "size": size,
282
- "samples": samples,
283
- "n_questions": n_questions,
284
- "headline_score": acc,
285
- "headline_score_pct": round(acc * 100, 2),
286
- "per_ability": per_ability,
287
- "answer_model": manifest.get("answer_model"),
288
- "judge_model": manifest.get("judge_model"),
289
- "brain": manifest.get("brain_override"),
290
- "git_sha": manifest.get("git_sha"),
291
- "report_path": f"runs/{run_id}/report.json",
292
- "recorded_at": now,
293
- }
294
-
295
-
296
- def _empty_reports_ledger() -> dict[str, Any]:
297
- return {
298
- "schema_version": 2,
299
- "updated_at": datetime.now(timezone.utc).isoformat(),
300
- "description": _REPORTS_DESCRIPTION,
301
- "benchmarks": {
302
- _BENCHMARK_ID: {
303
- "name": _BENCHMARK_NAME,
304
- "leaderboard": [],
305
- }
306
- },
307
- }
308
-
309
-
310
- def _public_leaderboard_entry(row: dict[str, Any]) -> dict[str, Any]:
311
- return {key: row[key] for key in _LEADERBOARD_ENTRY_KEYS if key in row}
312
-
313
-
314
- def _sorted_leaderboard(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
315
- leaderboard = [
316
- _public_leaderboard_entry(row)
317
- for row in rows
318
- if isinstance(row, dict) and row.get("run_id")
319
- ]
320
- leaderboard.sort(
321
- key=lambda row: float(row.get("headline_score") or 0.0),
322
- reverse=True,
323
- )
324
- return leaderboard
325
-
326
-
327
- def _normalize_reports_ledger(data: dict[str, Any]) -> dict[str, Any]:
328
- benchmarks: dict[str, Any] = {}
329
- raw_benchmarks = data.get("benchmarks")
330
- if isinstance(raw_benchmarks, dict):
331
- for suite_id, suite in raw_benchmarks.items():
332
- if not isinstance(suite, dict):
333
- continue
334
- if str(suite_id) == _BENCHMARK_ID:
335
- leaderboard = _sorted_leaderboard(list(suite.get("leaderboard") or []))
336
- else:
337
- leaderboard = list(suite.get("leaderboard") or [])
338
- benchmarks[str(suite_id)] = {
339
- "name": suite.get("name") or str(suite_id),
340
- "leaderboard": leaderboard,
341
- }
342
-
343
- if _BENCHMARK_ID not in benchmarks:
344
- benchmarks[_BENCHMARK_ID] = {
345
- "name": _BENCHMARK_NAME,
346
- "leaderboard": [],
347
- }
348
-
349
- return {
350
- "schema_version": 2,
351
- "updated_at": data.get("updated_at")
352
- or datetime.now(timezone.utc).isoformat(),
353
- "description": data.get("description") or _REPORTS_DESCRIPTION,
354
- "benchmarks": benchmarks,
355
- }
356
-
357
-
358
- def update_reports_json(report: dict[str, Any]) -> None:
359
- entry = entry_from_report(report)
360
- if entry is None:
361
- return
362
-
363
- if REPORTS_PATH.exists():
364
- data = json.loads(REPORTS_PATH.read_text(encoding="utf-8"))
365
- else:
366
- data = _empty_reports_ledger()
367
-
368
- data = _normalize_reports_ledger(data)
369
- suite = data["benchmarks"].setdefault(
370
- _BENCHMARK_ID,
371
- {"name": _BENCHMARK_NAME, "leaderboard": []},
372
- )
373
- run_id = entry["run_id"]
374
- leaderboard = list(suite.get("leaderboard") or [])
375
- lb_idx = next(
376
- (i for i, row in enumerate(leaderboard) if row.get("run_id") == run_id),
377
- None,
378
- )
379
- if lb_idx is not None:
380
- leaderboard[lb_idx] = entry
381
- else:
382
- leaderboard.append(entry)
383
-
384
- suite["name"] = suite.get("name") or _BENCHMARK_NAME
385
- suite["leaderboard"] = leaderboard
386
- data["benchmarks"][_BENCHMARK_ID] = suite
387
- data["updated_at"] = datetime.now(timezone.utc).isoformat()
388
- data = _normalize_reports_ledger(data)
389
-
390
- REPORTS_PATH.parent.mkdir(parents=True, exist_ok=True)
391
- fd, tmp_name = tempfile.mkstemp(
392
- prefix="REPORTS.",
393
- suffix=".json.tmp",
394
- dir=str(REPORTS_PATH.parent),
395
- )
396
- tmp_path = Path(tmp_name)
397
- try:
398
- with open(fd, "w", encoding="utf-8") as fh:
399
- json.dump(data, fh, indent=2)
400
- fh.write("\n")
401
- tmp_path.replace(REPORTS_PATH)
402
- except Exception:
403
- tmp_path.unlink(missing_ok=True)
404
- raise
405
-
406
-
407
- def write_report(run_dir: Path) -> dict[str, Any]:
408
- report = build_report(run_dir)
409
- md = render_markdown(report)
410
- (run_dir / "report.json").write_text(
411
- json.dumps(report, indent=2), encoding="utf-8"
412
- )
413
- (run_dir / "report.md").write_text(md, encoding="utf-8")
414
- if report.get("status") == "ok":
415
- update_reports_json(report)
416
- return report
417
-
418
-
419
- def print_report_table(report: dict[str, Any]) -> None:
420
- metrics = report["metrics"]
421
- table = Table(title="BEAM per-ability results")
422
- table.add_column("Ability")
423
- table.add_column("N", justify="right")
424
- table.add_column("Mean", justify="right")
425
- for ability in ABILITY_NAMES:
426
- bucket = (metrics.get("per_ability") or {}).get(ability) or {}
427
- table.add_row(
428
- ability,
429
- str(bucket.get("n", 0)),
430
- _pct(bucket.get("mean")),
431
- )
432
- if report.get("status") == "failed":
433
- console.print(
434
- f"[bold red]{(report.get('warnings') or ['RUN FAILED'])[0]}[/bold red]"
435
- )
436
- console.print(table)
437
- console.print(
438
- f"Headline mean ability score: "
439
- f"[bold]{_pct(metrics.get('headline_score'))}[/bold] "
440
- f"(n={metrics.get('n_questions', 0)})"
441
- )
442
- for warning in report.get("warnings") or []:
443
- console.print(f"[yellow]Warning: {warning}[/yellow]")
444
-
445
-
446
- def _pct(value: float | None) -> str:
447
- if value is None:
448
- return "n/a"
449
- return f"{value * 100:.1f}%"
450
-
451
-
452
- def _num(value: float | None) -> str:
453
- if value is None:
454
- return "n/a"
455
- return f"{value:.3f}"