"""Freeze the exact code, data, graph and parameters after complete DEV evaluation.""" from __future__ import annotations import argparse import json from datetime import datetime, timezone from pathlib import Path from .reproducibility import content_fingerprints, sha256_file def make_freeze( dev: dict, questions: list[dict], fingerprints: dict, audit: dict, *, purpose: str = "independent_holdout", ) -> dict: if purpose not in {"independent_holdout", "historical_regression"}: raise ValueError("Unknown evaluation purpose.") expected_ids = {q["id"] for q in questions if q["split"] == "dev"} actual_ids = [row["id"] for row in dev["questions"]] if ( dev["configuration"]["split"] != "dev" or set(actual_ids) != expected_ids or len(actual_ids) != len(expected_ids) ): raise RuntimeError("Freeze requires the complete DEV split, without duplicates.") if dev["summary"]["error_count"] or any(row.get("error") for row in dev["questions"]): raise RuntimeError("Freeze requires DEV without runtime errors.") if dev["run_metadata"]["fingerprints"] != fingerprints: raise RuntimeError("Code or data changed after DEV; rerun DEV before freezing.") if fingerprints.get("retrieval_policy") and any( row.get("source_checked") is not True or row.get("all_facts_supported") is not True for row in dev["questions"] ): raise RuntimeError( "Every DEV result must pass independent validation of all returned facts." ) if ( audit["dataset_sha256"] != fingerprints["dataset_sha256"] or audit["inputs"] != fingerprints["inputs"] ): raise RuntimeError("Source audit does not match the benchmark and inputs.") if len(audit["questions"]) != len(questions) or {r["id"] for r in audit["questions"]} != { q["id"] for q in questions }: raise RuntimeError("Source audit must cover every question exactly once.") schema3 = any(question.get("graph", {}).get("schema_version") == 3 for question in questions) partition = None if schema3: if audit.get("source_corpus") != fingerprints.get("source_corpus"): raise RuntimeError("Independent audit does not match original Markdown sources.") if any(not row.get("source_consistent") for row in audit["questions"]): raise RuntimeError("Resolve source-inconsistent annotations before freezing v2.") profiles = { split: { profile for question in questions if question["split"] == split for profile in question.get("graph", {}).get("source_profiles", []) } for split in ("dev", "test") } policy = audit.get("partition_policy", {}) if purpose == "historical_regression": partition = { "method": "Previously exposed questions; regression, not independent TEST", "dev_profile_count": len(profiles["dev"]), "test_profile_count": len(profiles["test"]), "source_profile_overlap": len(profiles["dev"] & profiles["test"]), } elif policy.get("method") == "disjoint logical query groups": # This mode explicitly reports known profiles. It must never be # presented as the stronger, historic unseen-profile experiment. from .benchmark_v4 import logical_group from .source_oracle import norm groups = { split: {logical_group(q) for q in questions if q["split"] == split} for split in ("dev", "test") } if any(q["graph"].get("partition_group") != logical_group(q) for q in questions): raise RuntimeError( "Declared logical group does not match the question constraints." ) if not groups["dev"] or not groups["test"] or groups["dev"] & groups["test"]: raise RuntimeError("DEV/TEST logical groups overlap or are missing.") exposed = set(policy.get("previously_exposed_groups", [])) if not exposed or groups["test"] & exposed: raise RuntimeError("TEST contains previously exposed logical groups.") texts = { split: {norm(q["question"]) for q in questions if q["split"] == split} for split in ("dev", "test") } if texts["dev"] & texts["test"]: raise RuntimeError("DEV/TEST question texts overlap.") partition = { "method": policy["method"], "logical_group_overlap": 0, "previously_exposed_test_groups": 0, "dev_group_count": len(groups["dev"]), "test_group_count": len(groups["test"]), "dev_profile_count": len(profiles["dev"]), "test_profile_count": len(profiles["test"]), "source_profile_overlap": len(profiles["dev"] & profiles["test"]), "limitations": policy["limitations"], } elif profiles["dev"] & profiles["test"]: raise RuntimeError("DEV/TEST source profiles overlap; independent holdout refused.") if not profiles["dev"] or not profiles["test"]: raise RuntimeError("Primary v2 freeze requires declared DEV and TEST source profiles.") if partition is None: partition = { "method": "disjoint source profiles", "dev_profile_count": len(profiles["dev"]), "test_profile_count": len(profiles["test"]), "overlap": 0, } return { "schema_version": 2 if schema3 else 1, "evaluation_kind": purpose, "partition": partition, "frozen_at_utc": datetime.now(timezone.utc).isoformat(), "fingerprints": fingerprints, "graph": dev["run_metadata"]["graph"], "parameters": {key: dev["configuration"][key] for key in ["limit", "max_entities"]}, "dev_summary": dev["summary"], "source_audit_summary": audit["by_split"], "policy": "Report all questions including negatives. No exclusions or holdout tuning.", } def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--dev-results", type=Path, required=True) parser.add_argument("--questions", type=Path, required=True) parser.add_argument("--audit", type=Path, required=True) parser.add_argument("--output", type=Path, required=True) parser.add_argument( "--purpose", choices=["independent_holdout", "historical_regression"], default="independent_holdout", ) args = parser.parse_args() if args.output.exists(): parser.error("Freeze manifest already exists; choose a new version/output path.") manifest = make_freeze( json.loads(args.dev_results.read_text()), json.loads(args.questions.read_text()), content_fingerprints(args.questions), json.loads(args.audit.read_text()), purpose=args.purpose, ) manifest.update( { "dev_results_sha256": sha256_file(args.dev_results), "audit_sha256": sha256_file(args.audit), } ) args.output.parent.mkdir(parents=True, exist_ok=True) with args.output.open("x", encoding="utf-8") as output: output.write(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n") print(f"Frozen GraphRAG: {args.output}") if __name__ == "__main__": main()