diff --git a/CHANGELOG.md b/CHANGELOG.md index ef3966b..7071118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); ver ### Added +- Intern-Decision input verification and offline replay with the official evaluator + and explicit coverage for incomplete runs. + - Public231 decision-model benchmark with raw HTTP evidence, pinned upstream scoring, complete-denominator quality metrics and per-round service latency. diff --git a/benchmarks/INTERN_DECISION.md b/benchmarks/INTERN_DECISION.md new file mode 100644 index 0000000..8bcc0c6 --- /dev/null +++ b/benchmarks/INTERN_DECISION.md @@ -0,0 +1,81 @@ +# Intern-Decision accuracy-v1 + +Verify pinned benchmark inputs and replay saved responses with the official +Intern evaluator. Run from the repository root with the public231 runner's shared +`benchmarks.client`, `benchmarks.evidence` and `benchmarks.legs.leg1_public231` modules. +`verify` uses only the standard library; replay needs the upstream evaluation +packages, including Torch, in a prepared CPU environment. No datasets or weights +are downloaded by this entry. + +## Sources + +| Repository | Required clean revision | +|---|---| +| [Intern-Decision](https://github.com/InternLM/Intern-Decision) | `3572c8a68b5df5dafe02d0e093989ba8ec0183bc` | +| [JevBench](https://github.com/fstandhartinger/jevbench) | `7ce310c7262ed49cc85853339a8a42459298e3f3` | + +The upstream bundle verifier checks hashes and the complete plan: + +| Suite | Requests | Decisions | +|---|---:|---:| +| JevBench easy / original / hard | 48 / 72 / 111 | 48 / 72 / 111 | +| AG News | 7,600 | 7,600 | +| ToolACE | 310 | 310 | +| Typed Decisions | 400 | 2,000 | +| WildJailBreak | 2,210 | 2,210 | +| Total | 10,751 | 12,351 | + +Public JevBench is a development diagnostic, not the full leaderboard or an +independent holdout. The manifest lists AG News licensing as `unknown`; this +entry preserves that metadata and does not redistribute the inputs. + +## Verify and replay + +```bash +python -m benchmarks.intern_decision verify \ + --intern-root /path/to/Intern-Decision --scorer-root /path/to/jevbench +python -m benchmarks.intern_decision replay \ + --intern-root /path/to/Intern-Decision --scorer-root /path/to/jevbench \ + --round /path/to/saved-run/round-1 --out /path/to/fresh-replay +``` + +Keep outputs outside the reference checkouts and use a new output directory for +each invocation. `JEVBENCH_ROOT`, if set, must match `--scorer-root`. + +Replay accepts public231 `round-1` through `round-3`, or saved seven-suite +accuracy-v1 evidence. It verifies `environment.json` against its recorded hash and +every original environment field in `run.meta.json`, plus the pinned data hashes. +Every `results.jsonl` attempt must identify `(suite, physical source_line, task_id)` +and match its raw file hash. Duplicate or unknown identities, missing raw files +and raw files without matching ledger entries are rejected. +The entire saved request must match the frozen task, original field/option order, +`thinking.enabled=false` and the configured model; extra fields are rejected. +`response_body` is authoritative. Decoded answers remain unchanged; HTTP, client, +parse and refusal failures remain failed attempts. Scored ledger columns are ignored. + +`input.json` records metadata, ledger and raw paths/hashes; `report.json` binds it +by hash. Retain the original run because raw files are referenced, not copied. +Recorded configuration is provenance, not proof of the loaded weights. + +## Scoring and completeness + +The unmodified upstream `src.eval.jev.evaluate` and table renderer perform scoring. +Answers need per-field `probabilities`; scalar-only responses cannot complete this +replay. No replacement distributions or temperature calibration are applied. +HF T=1 and XTuner calibrated results describe different inference settings. + +`report.json` always keeps all seven planned denominators. Missing responses or +an official evaluation failure leave that suite incomplete with an error and +unknown scores. Numeric overflow is a suite failure, never a clipped probability. +Only seven successful suite evaluations produce `complete.json`, `accuracy.md` +and `average_accuracy`, the unweighted mean of seven accuracies. Replaying only +the three public suites cannot produce a complete seven-suite result. +`replay` exits 0 after writing an incomplete report, so callers must check +`report.json.complete` and each suite's error; rejected evidence exits nonzero. + +The pinned Intern Hard input has no `gold_probs`: official `tvd_n=0`, `tvd=null` +means TVD was not evaluated. Original JevBench Hard has 10 references, but is a +different input. This entry calls official `evaluate` directly because upstream's +main/merge CLI requires those 10 references even for the bundled input. The table's +Brier/ECE columns refer to JevBench hard. A complete replay establishes scoring +coverage, not reproduction of published model quality or inference latency. diff --git a/benchmarks/intern_decision.py b/benchmarks/intern_decision.py new file mode 100644 index 0000000..fc6bae1 --- /dev/null +++ b/benchmarks/intern_decision.py @@ -0,0 +1,396 @@ +"""Verify and replay pinned Intern-Decision suites with the official evaluator.""" + +from __future__ import annotations + +import argparse +import hashlib +import importlib +import importlib.util +import json +import math +import os +from pathlib import Path +import subprocess +import sys + +INTERN_REVISION = "3572c8a68b5df5dafe02d0e093989ba8ec0183bc" +SCORER_REVISION = "7ce310c7262ed49cc85853339a8a42459298e3f3" +SUITES = ( + "jevbench-easy", + "jevbench-original", + "jevbench-hard", + "agnews-test", + "toolace-test", + "typed_decisions-test", + "wildjailbreak-test", +) + + +def _sha256(path): + return hashlib.sha256(Path(path).read_bytes()).hexdigest() + + +def _verify_checkout(root, revision): + def git(*args): + return subprocess.run( + ["git", "-C", str(root), *args], check=True, capture_output=True, text=True + ).stdout.strip() + + if git("rev-parse", "HEAD") != revision: + raise ValueError(f"Source revision mismatch: {root}; expected {revision}") + if git("status", "--porcelain", "--untracked-files=all"): + raise ValueError(f"Source checkout is not clean: {root}") + + +def verify_sources(intern_root, scorer_root): + """Verify code pins, then call the unmodified upstream CPU bundle verifier.""" + intern_root, scorer_root = Path(intern_root).resolve(), Path(scorer_root).resolve() + _verify_checkout(intern_root, INTERN_REVISION) + _verify_checkout(scorer_root, SCORER_REVISION) + spec = importlib.util.spec_from_file_location("_intern_bundle_verifier", intern_root / "src/eval/verify_bundle.py") + verifier = importlib.util.module_from_spec(spec) + spec.loader.exec_module(verifier) + bundle = intern_root / "benchmarks/accuracy-v1" + verifier.verify(bundle) + manifest = json.loads((bundle / "manifest.json").read_text(encoding="utf-8")) + if set(manifest["datasets"]) != set(SUITES) or (manifest["rows"], manifest["decisions"]) != (10751, 12351): + raise ValueError("Expected all seven accuracy-v1 suites") + return { + **manifest, + "intern_root": str(intern_root), + "scorer_root": str(scorer_root), + "source_revisions": {"intern_decision": INTERN_REVISION, "jevbench": SCORER_REVISION}, + "manifest_sha256": _sha256(bundle / "manifest.json"), + } + + +def _rows(path): + with Path(path).open(encoding="utf-8") as stream: + for number, line in enumerate(stream, 1): + if not line.strip(): + raise ValueError(f"Blank input record: {path}:{number}") + yield number, json.loads(line) + + +def _body(row): + questions = row["questions"] if "questions" in row else {"decision": row["question"]} + return { + "state": row["state"], + "questions": { + field: {key: value for key, value in question.items() if key in ("type", "instructions", "criteria")} + for field, question in questions.items() + }, + "thinking": {"enabled": False}, + } + + +def _raw_path(directory, key, extended): + name = f"{key[0]}/{key[1]}.json" if extended else hashlib.sha256(key[2].encode("utf-8")).hexdigest() + ".json" + return directory / "raw" / name + + +def load_round(intern_root, scorer_root, round_dir): + """Read A public231 or B accuracy-v1 evidence without trusting scored columns.""" + from benchmarks.client import decode + from benchmarks.evidence import digest, dump + from benchmarks.legs.leg1_public231 import validate_environment + + verified = verify_sources(intern_root, scorer_root) + directory = Path(round_dir).resolve() + if directory.name not in ("round-1", "round-2", "round-3"): + raise ValueError("Expected a measured round-1, round-2 or round-3 directory") + meta_path, results_path = directory.parent / "run.meta.json", directory / "results.jsonl" + metadata = decode(meta_path.read_text(encoding="utf-8")) + validate_environment(metadata) + environment = (directory.parent / "environment.json").read_bytes() + if digest(environment) != metadata.get("env_sha256"): + raise ValueError("Environment source hash mismatch") + if any(key not in metadata or metadata[key] != value for key, value in decode(environment).items()): + raise ValueError("Run metadata differs from supplied environment") + try: + checkpoint, backend, dataset = metadata["checkpoint"], metadata["backend"], metadata["dataset"] + temperature = metadata["sampling"]["temperature"] + if not dataset["dataset_hash"]: + raise ValueError("Run metadata requires data hash") + if dataset["jevbench_pin"] != SCORER_REVISION: + raise ValueError("Run metadata scorer pin mismatch") + extended = "intern_revision" in dataset + if extended and (dataset["intern_revision"], dataset["dataset_hash"]) != ( + INTERN_REVISION, + verified["manifest_sha256"], + ): + raise ValueError("Run metadata Intern bundle pin mismatch") + hashes = ( + verified["files"] + if extended + else { + name: _sha256(Path(verified["scorer_root"]) / name) + for name in (f"datasets/public/{tier}.jsonl" for tier in ("easy", "original", "hard")) + } + ) + if any(dataset["hashes"].get(path) != expected for path, expected in hashes.items()): + raise ValueError("Run metadata data hash mismatch") + except (KeyError, TypeError) as exc: + raise ValueError(f"Missing or malformed run metadata: {exc}") from exc + bundle = Path(verified["intern_root"]) / "benchmarks/accuracy-v1" + inputs = { + (suite, line, row["id"]): row + for suite in (SUITES if extended else SUITES[:3]) + for line, row in _rows(bundle / verified["datasets"][suite]["path"]) + } + responses, evidence = {}, [] + for number, record in _rows(results_path) if results_path.exists() else (): + suite, line, identity = record.get("suite"), record.get("source_line"), record.get("task_id") + if not isinstance(suite, str) or type(line) is not int or not isinstance(identity, str): + raise ValueError(f"Malformed response identity at results line {number}") + key = (suite, line, identity) + source = ( + verified["datasets"].get(suite, {}).get("path") if extended else suite.removeprefix("jevbench-") + ".jsonl" + ) + if key not in inputs or record.get("source_file") != source: + raise ValueError(f"Saved response identity is outside the pinned public input plan: {key}") + if key in responses: + raise ValueError(f"Duplicate saved response identity: {key}") + if ( + record.get("kind") != "measured" + or record.get("attempted") is not True + or type(record.get("round")) is not int + or record["round"] != int(directory.name[-1]) + ): + raise ValueError(f"Not a measured attempt from this round: {key}") + raw_path = _raw_path(directory, key, extended) + raw_bytes = raw_path.read_bytes() + raw_hash = hashlib.sha256(raw_bytes).hexdigest() + if raw_hash != record.get("raw_sha256"): + raise ValueError(f"Raw response hash mismatch: {key}") + raw = json.loads(raw_bytes) + expected = _body(inputs[key]) + if metadata["model_configured"] is not None: + expected["model"] = metadata["model_configured"] + if dump(raw.get("request")) != dump(expected): + raise ValueError(f"Raw request differs from frozen task and configuration: {key}") + status, body = raw["http_status"], raw["response_body"] + if status != record.get("status_code") or (status is not None and type(status) is not int): + raise ValueError(f"HTTP status mismatch: {key}") + if record.get("status") not in ("ok", "http_error", "client_error", "parse_error", "refused"): + raise ValueError(f"Unknown attempt status: {key}") + if body is not None and not isinstance(body, str): + raise ValueError(f"Raw response body must be text or null: {key}") + try: + decoded = json.loads(body) if body is not None else None + except ValueError: + decoded = None + if raw.get("response") is not None and json.dumps(raw["response"], sort_keys=True) != json.dumps( + decoded, sort_keys=True + ): + raise ValueError(f"Raw decoded response differs from body: {key}") + usable = ( + status is not None + and 200 <= status < 300 + and raw["client_error"] is None + and record.get("status") == "ok" + and record.get("ok") is True + ) + responses[key] = decoded if usable else None + evidence.append( + { + "suite": suite, + "source_line": line, + "task_id": identity, + "path": str(raw_path), + "sha256": raw_hash, + "status": record["status"], + "http_status": status, + "error": record.get("error"), + "client_error": raw["client_error"], + } + ) + expected_raw = {item["path"] for item in evidence} + actual_raw = {str(path) for path in (directory / "raw").rglob("*") if path.is_file()} + if expected_raw != actual_raw: + raise ValueError( + f"Raw/record mismatch in {directory}: missing={sorted(expected_raw - actual_raw)}, " + f"orphan={sorted(actual_raw - expected_raw)}" + ) + provenance = { + "round_directory": str(directory), + "run_meta": metadata, + "run_meta_sha256": _sha256(meta_path), + "results_sha256": _sha256(results_path) if results_path.exists() else None, + "raw": evidence, + } + config = { + "checkpoint": f"{checkpoint['repo']}@{checkpoint['revision']}", + "backend": backend["name"], + "temperature": temperature, + } + return responses, provenance, config + + +class SavedResponseEngine: + """Sequential, unsharded evaluate() adapter; never normalizes saved answers.""" + + def __init__(self, suite, responses, *, checkpoint, backend, temperature): + self.suite, self.responses = suite, responses + self.checkpoint, self.backend_name, self.temperature = checkpoint, backend, temperature + self.source_line = 0 + + def predict(self, row): + self.source_line += 1 + key = (self.suite, self.source_line, row["id"]) + if key not in self.responses: + raise ValueError(f"Missing saved response: {key}") + return self.responses[key] + + +def _official(intern_root, scorer_root): + """Refuse an already-imported foreign package instead of silently mixing sources.""" + roots = {"src": Path(intern_root), "jevbench": Path(scorer_root)} + for name, module in tuple(sys.modules.items()): + prefix = name.split(".")[0] + if prefix in roots and module is not None: + source = getattr(module, "__file__", None) + locations = [source] if source else list(getattr(module, "__path__", [])) + if not locations or any(not Path(path).resolve().is_relative_to(roots[prefix]) for path in locations): + raise ValueError(f"Foreign reference module already imported: {name}") + configured = os.environ.get("JEVBENCH_ROOT") + if configured and Path(configured).resolve() != roots["jevbench"]: + raise ValueError("JEVBENCH_ROOT differs from the verified scorer checkout") + previous_path = sys.path.copy() + sys.path[:0] = [str(roots["src"]), str(roots["jevbench"])] + try: + evaluator = importlib.import_module("src.eval.jev") + tables = importlib.import_module("src.eval.tables") + finally: + sys.path[:] = previous_path + return evaluator, tables + + +def summarize(plan, metrics, errors, attempted): + """Only complete official suites have scores; every planned denominator remains.""" + if set(plan) != set(SUITES): + raise ValueError("Expected seven planned suites") + if (set(metrics) | set(errors) | set(attempted)) - set(plan): + raise ValueError("Unknown evaluation suite") + datasets = {} + for name, expected in plan.items(): + result = metrics.get(name) + if result and (result["rows"], result["total"]) != (expected["rows"], expected["decisions"]): + raise ValueError(f"{name}: official count mismatch") + complete = result is not None and name not in errors + datasets[name] = { + **(result or {}), + "planned": expected["rows"], + "planned_decisions": expected["decisions"], + "attempted": attempted.get(name, expected["rows"] if complete else 0), + "valid": expected["decisions"] if complete else None, + "correct": result["correct"] if complete else None, + "accuracy": result["correct"] / expected["decisions"] if complete else None, + "complete": complete, + "error": errors.get(name, None if complete else "Suite not evaluated"), + } + complete = all(value["complete"] for value in datasets.values()) + return { + "complete": complete, + "planned": sum(value["rows"] for value in plan.values()), + "planned_decisions": sum(value["decisions"] for value in plan.values()), + "attempted": sum(value["attempted"] for value in datasets.values()), + "valid": sum(value["valid"] for value in datasets.values()) if complete else None, + "correct": sum(value["correct"] for value in datasets.values()) if complete else None, + "average_accuracy": sum(value["accuracy"] for value in datasets.values()) / 7 if complete else None, + "datasets": datasets, + } + + +def replay_bundle(intern_root, scorer_root, responses, *, checkpoint, backend, temperature, output, provenance=None): + """Replay A's attempted responses keyed by (suite, physical line, id). + + The caller retains raw HTTP evidence and rejects duplicate keys before building + the mapping. Missing keys are unattempted; error/invalid bodies stay unchanged. + This function makes no model requests and does not measure inference latency. + """ + if not checkpoint or not backend or not math.isfinite(temperature) or temperature <= 0: + raise ValueError("Checkpoint, backend and finite positive temperature are required") + verified = verify_sources(intern_root, scorer_root) + bundle = Path(verified["intern_root"]) / "benchmarks/accuracy-v1" + plan = verified["datasets"] + identities = { + name: [(name, line, row["id"]) for line, row in _rows(bundle / entry["path"])] for name, entry in plan.items() + } + expected_keys = {key for keys in identities.values() for key in keys} + if set(responses) - expected_keys: + raise ValueError("Saved response identity is outside the pinned input plan") + output = Path(output) + output.mkdir(parents=True, exist_ok=False) + if provenance is not None: + (output / "input.json").write_text(json.dumps(provenance, indent=2, allow_nan=False) + "\n", encoding="utf-8") + metrics, errors, attempted = {}, {}, {} + for name, entry in plan.items(): + missing = [key for key in identities[name] if key not in responses] + attempted[name] = entry["rows"] - len(missing) + if missing: + errors[name] = f"Missing {len(missing)} responses; first: {missing[0]}" + continue + engine = None + try: + evaluator, _ = _official(verified["intern_root"], verified["scorer_root"]) + paths = {name: Path(path).resolve() for name, path, _ in evaluator.suite_datasets(test_root=bundle)} + if paths != {suite: (bundle / spec["path"]).resolve() for suite, spec in plan.items()}: + raise ValueError("Official suite paths differ from the verified bundle") + engine = SavedResponseEngine( + name, responses, checkpoint=checkpoint, backend=backend, temperature=temperature + ) + destination = output / f"{name}.predictions.jsonl" + result = evaluator.evaluate(engine, paths[name], destination) + actual = [(name, row["source_line"], row["id"]) for _, row in _rows(destination)] + if actual != identities[name] or (result["rows"], result["total"]) != (entry["rows"], entry["decisions"]): + raise ValueError("Official output identity/count mismatch") + metrics[name] = result + except (ValueError, KeyError, TypeError, AttributeError, ImportError, OverflowError) as exc: + location = f" at source_line={engine.source_line}" if engine is not None else "" + errors[name] = f"{type(exc).__name__}{location}: {exc}" + report = { + **summarize(plan, metrics, errors, attempted), + "source_revisions": verified["source_revisions"], + "manifest_sha256": verified["manifest_sha256"], + "data_sha256": verified["files"], + "checkpoint": checkpoint, + "inference_backend": backend, + "temperature": temperature, + "scope": "accuracy-v1; seven-suite mean, not the full JevBench leaderboard", + "scoring_policy": "Incomplete suites have no official score; planned denominators are retained.", + } + if provenance is not None: + report["input_evidence"] = {"path": "input.json", "sha256": _sha256(output / "input.json")} + (output / "report.json").write_text(json.dumps(report, indent=2, allow_nan=False) + "\n", encoding="utf-8") + if report["complete"]: + _, tables = _official(verified["intern_root"], verified["scorer_root"]) + table = tables.accuracy_table([(checkpoint, report)]) + (output / "accuracy.md").write_text(table, encoding="utf-8") + (output / "complete.json").write_text(json.dumps(report, indent=2, allow_nan=False) + "\n", encoding="utf-8") + return report + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="command", required=True) + for command in ("verify", "replay"): + child = commands.add_parser(command) + child.add_argument("--intern-root", type=Path, required=True) + child.add_argument("--scorer-root", type=Path, required=True) + if command == "replay": + child.add_argument("--round", type=Path, required=True, help="A's saved round-N directory") + child.add_argument("--out", type=Path, required=True, help="New replay output directory") + args = parser.parse_args() + if args.command == "verify": + report = verify_sources(args.intern_root, args.scorer_root) + else: + responses, provenance, config = load_round(args.intern_root, args.scorer_root, args.round) + report = replay_bundle( + args.intern_root, args.scorer_root, responses, **config, output=args.out, provenance=provenance + ) + print(json.dumps(report, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/benchmarks/test_intern_decision.py b/tests/benchmarks/test_intern_decision.py new file mode 100644 index 0000000..09ad5e9 --- /dev/null +++ b/tests/benchmarks/test_intern_decision.py @@ -0,0 +1,755 @@ +"""Protocol tests; optional reference tests use real pinned upstream modules.""" + +import copy +import hashlib +import importlib.util +import json +import os +from pathlib import Path +from types import SimpleNamespace + +import pytest + +SPEC = importlib.util.spec_from_file_location( + "intern_decision", Path(__file__).resolve().parents[2] / "benchmarks/intern_decision.py" +) +MODULE = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(MODULE) +SavedResponseEngine = MODULE.SavedResponseEngine +replay_bundle = MODULE.replay_bundle +summarize = MODULE.summarize +verify_sources = MODULE.verify_sources +SUITES = MODULE.SUITES + + +def fixture_answer(body): + answers = {} + for field, question in body["questions"].items(): + if question["type"] == "noul": + labels = ["no", "yes"] + elif question["type"] == "score": + labels = [str(i) for i in range(len(question["criteria"]))] + else: + labels = list(question["criteria"]) + answers[field] = {"type": question["type"], "probabilities": dict.fromkeys(labels, 1 / len(labels))} + return {"answers": answers, "model": "protocol-fixture-no-model"} + + +def test_replay_catches_real_pinned_scorer_overflow_without_torch(tmp_path, monkeypatch): + """A small evaluator adapter calls the real pinned scorer; it is not full official evaluate.""" + from benchmarks.evidence import load_reference + + intern, scorer = references() + verified = verify_sources(intern, scorer) + upstream, _, _ = load_reference() + bundle = intern / "benchmarks/accuracy-v1" + suite = SUITES[0] + rows = list(MODULE._rows(bundle / verified["datasets"][suite]["path"])) + saved = {(suite, line, row["id"]): fixture_answer(MODULE._body(row)) for line, row in rows} + key = next(iter(saved)) + probs = saved[key]["answers"]["decision"]["probabilities"] + probs[next(iter(probs))] = 10**400 + + def evaluate(engine, data_path, output_path): + row = rows[0][1] + answer = engine.predict(row) + return upstream.scoring.score_task( + answer["answers"]["decision"]["probabilities"], upstream.tasks.Task.from_dict(row) + ) + + evaluator = SimpleNamespace( + evaluate=evaluate, + suite_datasets=lambda test_root: [ + (name, bundle / spec["path"], spec["rows"]) for name, spec in verified["datasets"].items() + ], + ) + monkeypatch.setattr(MODULE, "_official", lambda *_: (evaluator, None)) + target = tmp_path / "overflow-report" + report = MODULE.replay_bundle( + intern, scorer, saved, checkpoint="fixture", backend="fixture", temperature=1, output=target + ) + assert report["attempted"] == 48 and report["planned_decisions"] == 12351 + assert report["average_accuracy"] is None and not report["complete"] + assert "OverflowError at source_line=1" in report["datasets"][suite]["error"] + assert (target / "report.json").exists() and not (target / "complete.json").exists() + assert max(probs.values()) == 10**400 + + +def test_actual_a_loopback_output_replays_without_layout_assumptions(tmp_path, monkeypatch): + from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + import threading + + from benchmarks.legs.leg1_public231 import predict + + intern, scorer = references() + monkeypatch.setenv("JEVBENCH_ROOT", str(scorer)) + calls = [] + + class Handler(BaseHTTPRequestHandler): + def log_message(self, *_): + pass + + def do_POST(self): + body = json.loads(self.rfile.read(int(self.headers["Content-Length"]))) + calls.append(body) + content = b"not JSON" if len(calls) == 3 else json.dumps(fixture_answer(body)).encode() + self.send_response(503 if len(calls) == 4 else 200) + self.send_header("Content-Length", str(len(content))) + self.end_headers() + self.wfile.write(content) + + environment = tmp_path / "actual-a-environment.json" + environment.write_text( + json.dumps( + { + "checkpoint": {"repo": "protocol-fixture-no-model", "revision": "a" * 40}, + "backend": {"name": "loopback", "version": "fixture"}, + "sampling": {"temperature": 1}, + "hardware": "CPU loopback protocol fixture", + } + ) + ) + server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + thread = threading.Thread(target=server.serve_forever) + thread.start() + try: + output = predict( + SimpleNamespace( + endpoint=f"http://127.0.0.1:{server.server_port}", + path="/v1/decisions", + model="fixture", + timeout=2, + rounds=1, + warmup=1, + limit=3, + run_meta=environment, + out=tmp_path / "actual-a-output", + ) + ) + finally: + server.shutdown() + server.server_close() + thread.join() + saved, evidence, config = MODULE.load_round(intern, scorer, output / "round-1") + assert len(calls) == 4 and len(saved) == 3 + assert list(saved.values()) == [fixture_answer(calls[1]), None, None] + assert [row["status"] for row in evidence["raw"]] == ["ok", "parse_error", "http_error"] + report = MODULE.replay_bundle( + intern, scorer, saved, **config, output=tmp_path / "actual-a-replay", provenance=evidence + ) + assert report["planned_decisions"] == 12351 and report["attempted"] == 3 + assert not report["complete"] and report["average_accuracy"] is None + + +@pytest.fixture +def disk_round(tmp_path, monkeypatch): + """A's frozen disk contract; protocol fixture, never model evidence.""" + intern, scorer = tmp_path / "intern", tmp_path / "scorer" + row = {"id": "item", "state": {"text": "original"}, "question": {"type": "choice", "criteria": {"A": "a"}}} + plan, hashes = {}, {} + for suite in SUITES: + relative = f"{suite}/test.jsonl" + source = intern / "benchmarks/accuracy-v1" / relative + source.parent.mkdir(parents=True) + source.write_text(json.dumps(row) + "\n") + plan[suite] = {"path": relative, "rows": 1, "decisions": 1} + for tier in ("easy", "original", "hard"): + source = scorer / f"datasets/public/{tier}.jsonl" + source.parent.mkdir(parents=True, exist_ok=True) + source.write_text(json.dumps(row) + "\n") + hashes[f"datasets/public/{tier}.jsonl"] = MODULE._sha256(source) + verified = { + "intern_root": str(intern), + "scorer_root": str(scorer), + "datasets": plan, + "source_revisions": {}, + "manifest_sha256": "fixture", + "files": {}, + } + monkeypatch.setattr(MODULE, "verify_sources", lambda *_: verified) + round_dir = tmp_path / "run/round-1" + (round_dir / "raw").mkdir(parents=True) + meta = { + "checkpoint": {"repo": "example/model", "revision": "a" * 40}, + "backend": {"name": "hf", "version": "fixture"}, + "sampling": {"temperature": 1.0}, + "hardware": "CPU protocol fixture", + "dtype": "bf16", + "context_limits": {"max_tokens": 16384}, + "cache_config": {"enabled": False}, + "dataset": {"jevbench_pin": MODULE.SCORER_REVISION, "hashes": hashes, "dataset_hash": "fixture"}, + } + environment = {key: value for key, value in meta.items() if key != "dataset"} + (round_dir.parent / "environment.json").write_text(json.dumps(environment)) + meta.update(model_configured=None, env_sha256=MODULE._sha256(round_dir.parent / "environment.json")) + raw = { + "request": {"state": row["state"], "questions": {"decision": row["question"]}, "thinking": {"enabled": False}}, + "response_body": json.dumps(response(0.8)), + "response": response(0.8), + "http_status": 200, + "client_error": None, + } + record = { + "suite": SUITES[0], + "source_line": 1, + "task_id": "item", + "source_file": "easy.jsonl", + "round": 1, + "kind": "measured", + "attempted": True, + "ok": True, + "status": "ok", + "status_code": 200, + "error": None, + } + raw_path = round_dir / "raw" / (hashlib.sha256(b"item").hexdigest() + ".json") + + def save(*, records=None): + (round_dir.parent / "run.meta.json").write_text(json.dumps(meta)) + raw_path.write_text(json.dumps(raw)) + record["raw_sha256"] = MODULE._sha256(raw_path) + (round_dir / "results.jsonl").write_text("".join(json.dumps(r) + "\n" for r in (records or [record]))) + + save() + return intern, scorer, round_dir, meta, raw, record, raw_path, save + + +@pytest.fixture(params=[False, True], ids=["public231", "accuracy-v1"]) +def raw_inventory_round(disk_round, request): + intern, scorer, directory, meta, _, record, original_raw, _ = disk_round + verified = MODULE.verify_sources(intern, scorer) + extended = request.param + if extended: + meta["dataset"].update(intern_revision=MODULE.INTERN_REVISION, dataset_hash=verified["manifest_sha256"]) + meta["dataset"]["hashes"] = verified["files"] + second_source = intern / "benchmarks/accuracy-v1" / verified["datasets"][SUITES[1]]["path"] + second = json.loads(second_source.read_text()) + second["id"] = "second" + second_source.write_text(json.dumps(second) + "\n") + records = [dict(record), dict(record, suite=SUITES[1], task_id="second")] + raw_bytes, raw_paths = original_raw.read_bytes(), [] + for row in records: + row["source_file"] = ( + verified["datasets"][row["suite"]]["path"] + if extended + else row["suite"].removeprefix("jevbench-") + ".jsonl" + ) + relative = ( + f"{row['suite']}/{row['source_line']}.json" + if extended + else hashlib.sha256(row["task_id"].encode()).hexdigest() + ".json" + ) + path = directory / "raw" / relative + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(raw_bytes) + row["raw_sha256"] = hashlib.sha256(raw_bytes).hexdigest() + raw_paths.append(path) + if original_raw not in raw_paths: + original_raw.unlink() + (directory.parent / "run.meta.json").write_text(json.dumps(meta)) + (directory / "results.jsonl").write_text("".join(json.dumps(row) + "\n" for row in records)) + return intern, scorer, directory, records, raw_paths + + +@pytest.mark.parametrize( + "failure", ["drop_row", "drop_ledger", "empty_ledger", "unknown_raw", "nested_raw", "missing_raw", "blank_line"] +) +def test_round_rejects_raw_inventory_mismatch(raw_inventory_round, failure): + intern, scorer, directory, records, raw_paths = raw_inventory_round + ledger = directory / "results.jsonl" + if failure == "drop_row": + ledger.write_text(json.dumps(records[0]) + "\n") + elif failure == "drop_ledger": + ledger.unlink() + elif failure == "empty_ledger": + ledger.write_text("") + elif failure in ("unknown_raw", "nested_raw"): + extra = directory / "raw" / ("unexpected.txt" if failure == "unknown_raw" else "unknown/nested.json") + extra.parent.mkdir(parents=True, exist_ok=True) + extra.write_bytes(raw_paths[0].read_bytes()) + elif failure == "missing_raw": + raw_paths[0].unlink() + else: + ledger.write_text("\n") + with pytest.raises((ValueError, FileNotFoundError)) as error: + MODULE.load_round(intern, scorer, directory) + if failure not in ("missing_raw", "blank_line"): + assert "Raw/record mismatch" in str(error.value) + + +@pytest.mark.parametrize("state", ["partial", "empty_ledger", "absent_ledger", "absent_round"]) +def test_round_matching_raw_inventory_keeps_partial_plan(raw_inventory_round, state): + import shutil + + intern, scorer, directory, records, raw_paths = raw_inventory_round + ledger = directory / "results.jsonl" + if state == "partial": + ledger.write_text(json.dumps(records[0]) + "\n") + raw_paths[1].unlink() + elif state == "absent_round": + shutil.rmtree(directory) + else: + for path in raw_paths: + path.unlink() + if state == "empty_ledger": + ledger.write_text("") + else: + ledger.unlink() + saved, provenance, config = MODULE.load_round(intern, scorer, directory) + attempted = 1 if state == "partial" else 0 + assert len(saved) == len(provenance["raw"]) == attempted + report = MODULE.replay_bundle(intern, scorer, saved, **config, output=directory.parent / "partial-replay") + assert report["planned"] == report["planned_decisions"] == 7 + assert report["attempted"] == attempted and set(report["datasets"]) == set(SUITES) + assert not report["complete"] and report["average_accuracy"] is None + + +@pytest.mark.parametrize( + "field", ["checkpoint", "backend", "sampling", "hardware", "dtype", "context_limits", "cache_config"] +) +def test_replay_rejects_every_environment_field_mismatch(disk_round, field): + intern, scorer, directory, meta, _, _, _, save = disk_round + if field == "checkpoint": + meta[field]["revision"] = "relabeled-revision" + elif field == "backend": + meta[field]["version"] = "other" + elif field == "sampling": + meta[field]["temperature"] = 2.0 + elif field == "context_limits": + meta[field]["max_tokens"] = 4096 + elif field == "cache_config": + meta[field]["enabled"] = True + else: + meta[field] = "other" + save() + with pytest.raises(ValueError, match="[Ee]nvironment"): + MODULE.load_round(intern, scorer, directory) + + +@pytest.mark.parametrize("failure", ["missing", "hash", "corrupt", "deleted_field"]) +def test_replay_rejects_missing_or_corrupt_environment(disk_round, failure): + intern, scorer, directory, meta, _, _, _, save = disk_round + environment = directory.parent / "environment.json" + if failure == "missing": + environment.unlink() + elif failure == "hash": + meta["env_sha256"] = "wrong" + elif failure == "corrupt": + environment.write_text("not JSON") + meta["env_sha256"] = MODULE._sha256(environment) + else: + del meta["dtype"] + save() + with pytest.raises((ValueError, FileNotFoundError)): + MODULE.load_round(intern, scorer, directory) + + +@pytest.mark.parametrize( + "change", + [ + {"targets": {"decision": "A"}}, + {"labels": ["A"]}, + {"thinking": {"enabled": True}}, + {"thinking": {"enabled": 0}}, + {"model": "different-model"}, + ], +) +def test_replay_rejects_changed_complete_request_envelope(disk_round, change): + intern, scorer, directory, _, raw, _, _, save = disk_round + raw["request"].update(change) + save() # Recompute the correct raw hash: request semantics still must be rejected. + with pytest.raises(ValueError, match="request"): + MODULE.load_round(intern, scorer, directory) + + +def test_disk_round_keeps_original_response_and_evidence(disk_round): + intern, scorer, directory, meta, raw, _, raw_path, _ = disk_round + saved, provenance, config = MODULE.load_round(intern, scorer, directory) + assert saved == {(SUITES[0], 1, "item"): raw["response"]} + assert config == {"checkpoint": "example/model@" + "a" * 40, "backend": "hf", "temperature": 1.0} + assert provenance["run_meta"] == meta + assert provenance["raw"][0]["path"] == str(raw_path) + assert provenance["raw"][0]["sha256"] == MODULE._sha256(raw_path) + + +@pytest.mark.parametrize( + "change", + [ + {"suite": "unknown"}, + {"source_line": 2}, + {"source_line": True}, + {"task_id": "forged"}, + {"source_file": "hard.jsonl"}, + {"kind": "warmup"}, + {"round": 2}, + {"round": True}, + {"attempted": False}, + {"attempted": 1}, + ], +) +def test_disk_round_rejects_forged_record_identity(disk_round, change): + intern, scorer, directory, _, _, record, _, save = disk_round + record.update(change) + save() + with pytest.raises(ValueError): + MODULE.load_round(intern, scorer, directory) + + +def test_disk_round_rejects_duplicates_and_raw_hash_mismatch(disk_round): + intern, scorer, directory, _, _, record, raw_path, save = disk_round + save(records=[record, record]) + with pytest.raises(ValueError, match="Duplicate"): + MODULE.load_round(intern, scorer, directory) + save() + raw_path.write_text(raw_path.read_text() + " ") + with pytest.raises(ValueError, match="hash"): + MODULE.load_round(intern, scorer, directory) + + +@pytest.mark.parametrize("part", ["state", "questions"]) +def test_disk_round_rejects_raw_request_from_another_input(disk_round, part): + intern, scorer, directory, _, raw, _, _, save = disk_round + raw["request"][part] = {} + save() + with pytest.raises(ValueError, match="request"): + MODULE.load_round(intern, scorer, directory) + + +@pytest.mark.parametrize( + "status,code,error", + [ + ("http_error", 503, None), + ("client_error", None, "timeout"), + ("parse_error", 200, None), + ("refused", 200, None), + ], +) +def test_disk_round_does_not_score_failed_attempts(disk_round, status, code, error): + intern, scorer, directory, _, raw, record, _, save = disk_round + record.update(status=status, status_code=code, ok=False, error=status) + raw.update(http_status=code, client_error=error) + if status == "parse_error": + raw.update(response_body="not JSON", response=None) + elif status == "client_error": + raw.update(response_body=None, response=None) + save() + saved, provenance, _ = MODULE.load_round(intern, scorer, directory) + assert saved == {(SUITES[0], 1, "item"): None} + assert provenance["raw"][0]["status"] == status + + +def test_disk_round_body_is_authoritative_and_decoded_is_optional(disk_round): + intern, scorer, directory, _, raw, _, _, save = disk_round + raw["response"] = response(1) + save() + with pytest.raises(ValueError, match="decoded"): + MODULE.load_round(intern, scorer, directory) + del raw["response"] + save() + saved, _, _ = MODULE.load_round(intern, scorer, directory) + assert saved[(SUITES[0], 1, "item")] == response(0.8) + + +def test_disk_round_preserves_integer_decode_limit_failure(disk_round): + import sys + + intern, scorer, directory, _, raw, record, raw_path, save = disk_round + body = '{"answers":{"decision":{"type":"choice","probabilities":{"A":' + "9" * 5001 + "}}}}" + previous_limit = sys.get_int_max_str_digits() + try: + sys.set_int_max_str_digits(4300) + with pytest.raises(ValueError) as exc: + json.loads(body) + assert not isinstance(exc.value, json.JSONDecodeError) + raw.update(response_body=body, response=None) + record.update(status="parse_error", ok=False, error="parse_error") + save() + saved, provenance, _ = MODULE.load_round(intern, scorer, directory) + finally: + sys.set_int_max_str_digits(previous_limit) + assert saved == {(SUITES[0], 1, "item"): None} + assert provenance["raw"][0]["status"] == "parse_error" + assert provenance["raw"][0]["sha256"] == MODULE._sha256(raw_path) + assert json.loads(raw_path.read_text())["response_body"] == body + + +@pytest.mark.parametrize("failure", ["http", "client", "parse"]) +def test_disk_round_does_not_trust_success_columns(disk_round, failure): + intern, scorer, directory, _, raw, record, _, save = disk_round + record.update(valid=True, correct=True, probs={"A": 1}) + if failure == "http": + raw["http_status"] = record["status_code"] = 503 + elif failure == "client": + raw["client_error"] = "failed read" + else: + raw.update(response_body="failure", response=None) + save() + saved, _, _ = MODULE.load_round(intern, scorer, directory) + assert saved[(SUITES[0], 1, "item")] is None + + +def test_disk_round_preserves_invalid_probabilities_for_official_scorer(disk_round): + intern, scorer, directory, _, raw, _, _, save = disk_round + raw["response"] = response(float("nan")) + raw["response_body"] = json.dumps(raw["response"]) + save() + saved, _, _ = MODULE.load_round(intern, scorer, directory) + assert json.dumps(saved[(SUITES[0], 1, "item")]) == raw["response_body"] + + +def test_a_scorer_error_does_not_replace_original_decoded_response(disk_round): + intern, scorer, directory, _, raw, record, _, save = disk_round + raw["response"] = {"answers": {"decision": {"type": "choice", "probabilities": {"unknown": 1}}}} + raw["response_body"] = json.dumps(raw["response"]) + record.update(valid=False, error="upstream diagnostic: unexpected probability labels") + save() + saved, _, _ = MODULE.load_round(intern, scorer, directory) + assert saved[(SUITES[0], 1, "item")] == raw["response"] + + +def test_disk_round_rejects_option_reordering(disk_round): + intern, scorer, directory, _, raw, _, _, save = disk_round + source = intern / "benchmarks/accuracy-v1/jevbench-easy/test.jsonl" + row = json.loads(source.read_text()) + row["question"]["criteria"] = {"A": "a", "B": "b"} + source.write_text(json.dumps(row) + "\n") + raw["request"]["questions"]["decision"]["criteria"] = {"B": "b", "A": "a"} + save() + with pytest.raises(ValueError, match="request"): + MODULE.load_round(intern, scorer, directory) + + +@pytest.mark.parametrize("change", ["hash", "pin", "checkpoint", "backend", "temperature"]) +def test_disk_round_rejects_unbound_metadata(disk_round, change): + intern, scorer, directory, meta, _, _, _, save = disk_round + if change == "hash": + meta["dataset"]["hashes"]["datasets/public/easy.jsonl"] = "bad" + elif change == "pin": + meta["dataset"]["jevbench_pin"] = "main" + elif change == "temperature": + meta["sampling"]["temperature"] = 0 + else: + del meta[change] + save() + with pytest.raises(ValueError): + MODULE.load_round(intern, scorer, directory) + + +def test_replay_cli_retains_seven_suite_plan(disk_round, monkeypatch, capsys): + intern, scorer, directory, _, _, _, _, _ = disk_round + out = directory.parent.parent / "replay" + # Import failure cannot turn the one attempted fixture row into a score. + monkeypatch.setattr(MODULE, "_official", lambda *_: (_ for _ in ()).throw(ImportError("CPU fixture"))) + monkeypatch.setattr( + "sys.argv", + [ + "intern_decision.py", + "replay", + "--intern-root", + str(intern), + "--scorer-root", + str(scorer), + "--round", + str(directory), + "--out", + str(out), + ], + ) + MODULE.main() + report = json.loads(capsys.readouterr().out) + assert report["planned"] == 7 and report["attempted"] == 1 + assert not report["complete"] and report["average_accuracy"] is None + assert len(report["datasets"]) == 7 + assert not (out / "complete.json").exists() + assert report["input_evidence"]["sha256"] == MODULE._sha256(out / "input.json") + assert json.loads((out / "input.json").read_text())["raw"][0]["task_id"] == "item" + + +def references(): + intern = os.environ.get("INTERN_DECISION_ROOT") + scorer = os.environ.get("JEVBENCH_ROOT") + if not intern or not scorer: + pytest.skip("set INTERN_DECISION_ROOT and JEVBENCH_ROOT for pinned reference checks") + return Path(intern), Path(scorer) + + +def response(value): + return {"answers": {"decision": {"probabilities": {"A": value, "B": 1 - value}}}, "timing": {"inference_ms": 7}} + + +def test_saved_responses_keep_suite_line_and_original_values(): + saved = { + ("one", 1, "same"): response(0.8), + ("one", 2, "same"): response(0.2), + ("two", 1, "same"): response(0.6), + } + original = copy.deepcopy(saved) + engine = SavedResponseEngine("one", saved, checkpoint="model@revision", backend="hf", temperature=1.0) + assert engine.predict({"id": "same"}) == saved[("one", 1, "same")] + assert engine.predict({"id": "same"}) == saved[("one", 2, "same")] + other = SavedResponseEngine("two", saved, checkpoint="model@revision", backend="hf", temperature=1.0) + assert other.predict({"id": "same"}) == saved[("two", 1, "same")] + assert saved == original + assert (engine.checkpoint, engine.backend_name, engine.temperature) == ("model@revision", "hf", 1.0) + + +def test_saved_response_never_falls_back_to_id_only(): + engine = SavedResponseEngine("one", {("one", 2, "same"): response(1)}, checkpoint="m", backend="hf", temperature=1) + with pytest.raises(ValueError, match="one.*1.*same"): + engine.predict({"id": "same"}) + + +def test_engine_does_not_repair_or_drop_invalid_answers(): + raw = {"answers": {"decision": {"probabilities": {"A": float("nan")}}}, "usage": {"input_tokens": 4}} + engine = SavedResponseEngine("s", {("s", 1, "x"): raw}, checkpoint="m", backend="hf", temperature=1) + assert engine.predict({"id": "x"}) is raw + + +def test_summary_uses_all_seven_planned_denominators(): + plan = {name: {"rows": 1, "decisions": 2} for name in SUITES} + plan[SUITES[-1]] = {"rows": 10, "decisions": 100} + metrics = {name: {"rows": spec["rows"], "total": spec["decisions"], "correct": 1} for name, spec in plan.items()} + metrics[SUITES[-1]]["correct"] = 90 + report = summarize(plan, metrics, {}, {}) + assert report["complete"] is True + assert report["average_accuracy"] == pytest.approx((6 * 0.5 + 0.9) / 7) + assert report["planned_decisions"] == 112 + assert report["correct"] == 96 + + +def test_summary_missing_suite_or_short_result_cannot_be_complete(): + plan = {name: {"rows": 2, "decisions": 2} for name in SUITES} + metrics = {name: {"rows": 2, "total": 2, "correct": 1} for name in plan} + missing = SUITES[-1] + del metrics[missing] + report = summarize(plan, metrics, {missing: "missing response"}, {missing: 1}) + assert report["complete"] is False + assert report["planned_decisions"] == 14 + assert report["datasets"][missing]["planned_decisions"] == 2 + assert report["datasets"][missing]["attempted"] == 1 + assert report["datasets"][missing]["correct"] is None + assert report["average_accuracy"] is None + metrics[missing] = {"rows": 1, "total": 1, "correct": 1} + with pytest.raises(ValueError, match="count"): + summarize(plan, metrics, {}, {}) + + +def test_summary_requires_exactly_seven_suites(): + with pytest.raises(ValueError, match="seven"): + summarize({"one": {"rows": 1, "decisions": 1}}, {}, {}, {}) + with pytest.raises(ValueError, match="seven"): + summarize({f"unknown{i}": {"rows": 1, "decisions": 1} for i in range(7)}, {}, {}, {}) + + +def test_real_bundle_is_pinned_and_cpu_only(): + intern, scorer = references() + result = verify_sources(intern, scorer) + assert result["rows"] == 10751 + assert result["decisions"] == 12351 + assert len(result["datasets"]) == 7 + assert result["datasets"]["typed_decisions-test"]["decisions"] == 2000 + assert result["datasets"]["agnews-test"]["upstream_license_metadata"] == ["unknown"] + + +def test_wrong_source_pin_is_rejected(): + intern, scorer = references() + with pytest.raises(ValueError, match="revision"): + verify_sources(scorer, intern) + + +def test_hard_bundle_has_no_tvd_reference_distributions(): + intern, scorer = references() + counts = [] + for path in ( + intern / "benchmarks/accuracy-v1/jevbench/hard.jsonl", + scorer / "datasets/public/hard.jsonl", + ): + with path.open(encoding="utf-8") as stream: + rows = [json.loads(line) for line in stream] + counts.append(sum(bool(row.get("gold_probs") or row.get("provenance", {}).get("gold_probs")) for row in rows)) + assert counts == [0, 10] + + +def test_empty_replay_keeps_full_plan_without_torch(tmp_path): + intern, scorer = references() + out = tmp_path / "replay" + report = replay_bundle(intern, scorer, {}, checkpoint="model@revision", backend="hf", temperature=1, output=out) + assert report["complete"] is False + assert report["planned_decisions"] == 12351 + assert report["attempted"] == 0 + assert report["average_accuracy"] is None + assert not (out / "complete.json").exists() + assert json.loads((out / "report.json").read_text())["complete"] is False + assert not (out / "accuracy.md").exists() + with pytest.raises(FileExistsError): + replay_bundle(intern, scorer, {}, checkpoint="m", backend="hf", temperature=1, output=out) + + +def test_replay_rejects_orphan_identity_before_output(tmp_path): + intern, scorer = references() + out = tmp_path / "replay" + with pytest.raises(ValueError, match="identity"): + replay_bundle( + intern, scorer, {("unknown", 1, "x"): response(1)}, checkpoint="m", backend="hf", temperature=1, output=out + ) + assert not out.exists() + + +def test_official_replay_of_protocol_responses(tmp_path): + """Exercise real scoring, never model quality: probabilities are uniform fixtures.""" + intern, scorer = references() + if importlib.util.find_spec("torch") is None: + pytest.skip("official evaluate imports Torch; run this check in the prepared CPU environment") + verified = verify_sources(intern, scorer) + evaluator, _ = MODULE._official(intern.resolve(), scorer.resolve()) + saved = {} + for suite, entry in verified["datasets"].items(): + source = intern / "benchmarks/accuracy-v1" / entry["path"] + with source.open(encoding="utf-8") as stream: + for line, text in enumerate(stream, 1): + row = json.loads(text) + questions = row.get("questions") or {"decision": row["question"]} + answers = {} + for field, question in questions.items(): + labels = [value for value, _ in evaluator._options(question)] + answers[field] = {"type": question["type"], "probabilities": dict.fromkeys(labels, 1 / len(labels))} + saved[(suite, line, row["id"])] = {"answers": answers} + out = tmp_path / "uniform-protocol-fixture" + report = replay_bundle( + intern, scorer, saved, checkpoint="protocol-fixture-no-model", backend="hf", temperature=1, output=out + ) + assert report["complete"] is True, json.dumps(report, indent=2) + assert report["valid"] == report["planned_decisions"] == 12351 + assert report["datasets"]["jevbench-hard"]["tvd_n"] == 0 + assert report["datasets"]["jevbench-hard"]["tvd"] is None + assert (out / "complete.json").is_file() + assert (out / "accuracy.md").is_file() + assert report["average_accuracy"] == pytest.approx(sum(v["accuracy"] for v in report["datasets"].values()) / 7) + direct = evaluator.evaluate( + SavedResponseEngine( + "typed_decisions-test", saved, checkpoint="protocol-fixture-no-model", backend="hf", temperature=1 + ), + intern / "benchmarks/accuracy-v1/typed_decisions/test.jsonl", + tmp_path / "direct.predictions.jsonl", + ) + for key in ("correct", "total", "brier", "ece"): + assert direct[key] == report["datasets"]["typed_decisions-test"][key] + + # A complete response ledger with an invalid probability must still fail closed. + easy = {key: copy.deepcopy(value) for key, value in saved.items() if key[0] == "jevbench-easy"} + first = next(iter(easy.values()))["answers"]["decision"]["probabilities"] + first[next(iter(first))] = float("nan") + failed = replay_bundle( + intern, + scorer, + easy, + checkpoint="protocol-fixture-no-model", + backend="hf", + temperature=1, + output=tmp_path / "invalid", + ) + assert failed["complete"] is False + assert failed["datasets"]["jevbench-easy"]["attempted"] == 48 + assert failed["datasets"]["jevbench-easy"]["correct"] is None + assert "not finite" in failed["datasets"]["jevbench-easy"]["error"] + assert not (tmp_path / "invalid/complete.json").exists()