Download tools/summarize_quality.py from INLEVEL9/Vons: direct link, hf CLI and curl.
- Browser
- Download file 7.75 kB
-
https://huggingface.co/INLEVEL9/Vons/resolve/main/tools/summarize_quality.py
- Command line
-
hf download hf://INLEVEL9/Vons/tools/summarize_quality.py
-
curl -L -o summarize_quality.py https://huggingface.co/INLEVEL9/Vons/resolve/main/tools/summarize_quality.py
7.75 kB
| """Recompute quality metrics from saved Vons prediction reports. | |
| This tool never fabricates unavailable external-task results. Its current | |
| reports are explicitly labeled synthetic-pilot measurements. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import hashlib | |
| import json | |
| import math | |
| import statistics | |
| from collections import defaultdict | |
| from pathlib import Path | |
| from typing import Any | |
| from vons.data import read_jsonl | |
| from vons.evaluation import Prediction, summarize | |
| def file_sha256(path: Path) -> str: | |
| digest = hashlib.sha256() | |
| with path.open("rb") as handle: | |
| for chunk in iter(lambda: handle.read(1024 * 1024), b""): | |
| digest.update(chunk) | |
| return digest.hexdigest() | |
| def load_report(path: Path) -> dict[str, Any]: | |
| value = json.loads(path.read_text(encoding="utf-8")) | |
| if not isinstance(value, dict) or not isinstance(value.get("predictions"), list): | |
| raise TypeError(f"{path} must contain a predictions list") | |
| return value | |
| def to_predictions(raw: list[dict[str, Any]], path: Path) -> list[Prediction]: | |
| predictions: list[Prediction] = [] | |
| seen: set[str] = set() | |
| for index, value in enumerate(raw, start=1): | |
| if not isinstance(value, dict): | |
| raise TypeError(f"{path}: prediction {index} must be an object") | |
| example_id = str(value.get("example_id", "")) | |
| if not example_id or example_id in seen: | |
| raise ValueError(f"{path}: duplicate or missing example_id at row {index}") | |
| seen.add(example_id) | |
| probabilities = value.get("probabilities") | |
| if not isinstance(probabilities, dict): | |
| raise TypeError(f"{path}: probabilities must be an object at row {index}") | |
| numeric_probabilities = {str(key): float(item) for key, item in probabilities.items()} | |
| confidence = float(value.get("confidence")) | |
| if not math.isfinite(confidence) or any(not math.isfinite(item) for item in numeric_probabilities.values()): | |
| raise ValueError(f"{path}: non-finite probability or confidence at row {index}") | |
| predictions.append( | |
| Prediction( | |
| example_id=example_id, | |
| choice=None if value.get("choice") is None else str(value["choice"]), | |
| probabilities=numeric_probabilities, | |
| confidence=confidence, | |
| abstained=bool(value.get("abstained", False)), | |
| latency_ms=None if value.get("latency_ms") is None else float(value["latency_ms"]), | |
| error=None if value.get("error") is None else str(value["error"]), | |
| ) | |
| ) | |
| return predictions | |
| def summarize_report(data_path: Path, report_path: Path) -> dict[str, Any]: | |
| examples = read_jsonl(data_path) | |
| report = load_report(report_path) | |
| predictions = to_predictions(report["predictions"], report_path) | |
| expected_ids = {row.id for row in examples} | |
| prediction_ids = {item.example_id for item in predictions} | |
| summary = summarize(examples, predictions) | |
| config = report.get("config", {}) | |
| if not isinstance(config, dict): | |
| raise TypeError(f"{report_path}: config must be an object") | |
| return { | |
| "report": str(report_path), | |
| "report_sha256": file_sha256(report_path), | |
| "backend": config.get("backend"), | |
| "seed": config.get("seed"), | |
| "model_id": config.get("model_id"), | |
| "dataset": str(data_path), | |
| "dataset_sha256": file_sha256(data_path), | |
| "dataset_rows": len(examples), | |
| "prediction_rows": len(predictions), | |
| "missing_prediction_rows": len(expected_ids - prediction_ids), | |
| "extra_prediction_rows": len(prediction_ids - expected_ids), | |
| "answerable_rows": sum(row.answerable for row in examples), | |
| "answerable_prediction_rows": sum(row.answerable and row.id in prediction_ids for row in examples), | |
| "abstained_rows": sum(item.abstained for item in predictions), | |
| "error_rows": sum(item.error is not None for item in predictions), | |
| "metrics": summary, | |
| "measurement_label": "measured_synthetic_pilot", | |
| "quality_scope": "synthetic test split only; not Mind2Web or browser task success", | |
| } | |
| def descriptive_seed_stats(rows: list[dict[str, Any]]) -> dict[str, Any]: | |
| grouped: dict[str, list[dict[str, Any]]] = defaultdict(list) | |
| for row in rows: | |
| grouped[str(row["backend"])].append(row) | |
| fields = ("accuracy_answerable", "macro_f1_answerable", "coverage", "selective_risk", "nll_answerable", "brier_answerable", "ece") | |
| result: dict[str, Any] = {} | |
| for backend, backend_rows in sorted(grouped.items()): | |
| result[backend] = {} | |
| for field in fields: | |
| values = [row["metrics"].get(field) for row in backend_rows] | |
| defined = [float(value) for value in values if value is not None] | |
| result[backend][field] = { | |
| "mean": statistics.mean(defined) if defined else None, | |
| "min": min(defined) if defined else None, | |
| "max": max(defined) if defined else None, | |
| "seed_count": len(backend_rows), | |
| "defined_seed_count": len(defined), | |
| "null_seed_count": len(values) - len(defined), | |
| "interpretation": "descriptive across saved seeds; not an independent test population", | |
| } | |
| return result | |
| def build_quality_summary(data_path: Path, report_paths: list[Path], *, version: str = "v1") -> dict[str, Any]: | |
| if not report_paths: | |
| raise ValueError("at least one report is required") | |
| rows = [summarize_report(data_path, path) for path in report_paths] | |
| if version not in {"v1", "v2"}: | |
| raise ValueError("version must be v1 or v2") | |
| result = { | |
| "schema": f"vons.quality-summary/{version}", | |
| "measurement_label": "measured_synthetic_pilot", | |
| "dataset": {"path": str(data_path), "sha256": file_sha256(data_path), "rows": len(read_jsonl(data_path))}, | |
| "reports": rows, | |
| "descriptive_seed_stats": descriptive_seed_stats(rows), | |
| "external_evaluation": { | |
| "mind2web": { | |
| "status": "not_evaluated", | |
| "candidate_recall": None, | |
| "conditional_selection_accuracy": None, | |
| "reason": "No Mind2Web evaluation report was supplied to this synthetic summary.", | |
| }, | |
| "jev": {"status": "not_run", "agreement": None}, | |
| }, | |
| "disclosure": "These numbers are recomputed from saved synthetic-pilot predictions. They do not establish external-task quality, browser task success, or generalization.", | |
| } | |
| if version == "v2": | |
| result["metric_version"] = "vons.metrics/v2" | |
| result["input_sha256"] = { | |
| "dataset": file_sha256(data_path), | |
| "reports": {str(path): file_sha256(path) for path in report_paths}, | |
| } | |
| result["disclosure"] += " Version 2 also records every input digest and uses the vons.metrics/v2 definitions present in each saved report." | |
| return result | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--data", type=Path, required=True) | |
| parser.add_argument("--report", type=Path, action="append", required=True) | |
| parser.add_argument("--output", type=Path, required=True) | |
| parser.add_argument("--version", choices=("v1", "v2"), default="v1") | |
| args = parser.parse_args() | |
| result = build_quality_summary(args.data, args.report, version=args.version) | |
| args.output.parent.mkdir(parents=True, exist_ok=True) | |
| args.output.write_text(json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8") | |
| print(json.dumps(result, indent=2, sort_keys=True)) | |
| if __name__ == "__main__": | |
| main() | |