ELBench-live / src /elbench /summary /aggregator.py
adelevett's picture
Deploy ELBench-live dashboard
5db30ac verified
Raw History Blame Contribute Delete
4.72 kB
from __future__ import annotations
import json
from collections import defaultdict
from pathlib import Path
from typing import Any
def _load_jsonl(path: Path) -> list[dict[str, Any]]:
if not path.exists():
return []
records = []
with path.open("r", encoding="utf-8") as handle:
for line in handle:
if line.strip():
records.append(json.loads(line))
return records
def _group_key(record: dict[str, Any], keys: tuple[str, ...]) -> str:
return " | ".join(str(record.get(key)) for key in keys)
def build_summary(judged_path: Path, failures_path: Path) -> dict[str, Any]:
judged_records = _load_jsonl(judged_path)
failure_records = _active_failures(
failures=_load_jsonl(failures_path),
judged=judged_records,
)
scene_records = []
for record in judged_records:
metadata = record.get("metadata")
if isinstance(metadata, dict) and metadata.get("_scene"):
scene_record = dict(record)
scene_record["scene"] = metadata["_scene"]
scene_records.append(scene_record)
def summarize(records: list[dict[str, Any]], keys: tuple[str, ...]) -> dict[str, Any]:
grouped: dict[str, list[dict[str, Any]]] = defaultdict(list)
for record in records:
grouped[_group_key(record, keys)].append(record)
output = {}
for group_key, group_records in grouped.items():
scores = [record.get("score") for record in group_records if record.get("score") is not None]
judge_counts: dict[str, int] = defaultdict(int)
for record in group_records:
judge_counts[str(record.get("judge_result"))] += 1
pass_count = sum(1 for record in group_records if str(record.get("judge_result")) in {"pass", "safe", "guide", "answer"})
output[group_key] = {
"count": len(group_records),
"avg_score": (sum(scores) / len(scores)) if scores else None,
"pass_rate": (pass_count / len(group_records)) if group_records else None,
"judge_result_distribution": dict(judge_counts),
}
return output
return {
"total_judged": len(judged_records),
"total_failures": len(failure_records),
"by_source_file": summarize(judged_records, ("source_file",)),
"by_module": summarize(judged_records, ("module",)),
"by_task": summarize(judged_records, ("task",)),
"by_subset": summarize(judged_records, ("module", "subset")),
"by_dimension": summarize(judged_records, ("dimension",)),
"by_scene": summarize(scene_records, ("scene",)),
"failure_examples": failure_records[:20],
}
def _active_failures(
*,
failures: list[dict[str, Any]],
judged: list[dict[str, Any]],
) -> list[dict[str, Any]]:
successful_keys = {_record_key(record) for record in judged}
successful_basic_scenes = _successful_basic_scene_keys(judged)
active: dict[tuple[str, str], dict[str, Any]] = {}
for failure in failures:
key = _record_key(failure)
if key in successful_keys:
active.pop(key, None)
continue
if _basic_scenario_failure_key(failure) in successful_basic_scenes:
active.pop(key, None)
continue
active[key] = failure
return list(active.values())
def _record_key(record: dict[str, Any]) -> tuple[str, str]:
return (str(record.get("source_file") or ""), str(record.get("sample_id") or ""))
def _successful_basic_scene_keys(judged: list[dict[str, Any]]) -> set[tuple[str, str]]:
keys: set[tuple[str, str]] = set()
for record in judged:
metadata = record.get("metadata")
if not isinstance(metadata, dict):
continue
if metadata.get("source") != "basic_education_runtime":
continue
scene = str(metadata.get("_scene") or "").strip()
if not scene:
continue
keys.add((str(record.get("source_file") or ""), scene))
return keys
def _basic_scenario_failure_key(failure: dict[str, Any]) -> tuple[str, str] | None:
if str(failure.get("error_type") or "") != "BasicEducationScenarioError":
return None
metadata = failure.get("metadata")
scenario_id = ""
if isinstance(metadata, dict):
scenario_id = str(metadata.get("scenario_id") or "").strip()
if not scenario_id:
sample_id = str(failure.get("sample_id") or "")
if sample_id.endswith("-scenario"):
scenario_id = sample_id[: -len("-scenario")]
if not scenario_id:
return None
return (str(failure.get("source_file") or ""), scenario_id)