mirror of
https://github.com/wassname/ml_debug.git
synced 2026-07-14 03:50:08 +08:00
137 lines
5.5 KiB
Python
137 lines
5.5 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
CONDITIONS = ["control", "treatment"]
|
|
METRICS = [
|
|
"root_cause_correct",
|
|
"discriminating_test",
|
|
"localized_before_change",
|
|
"unsupported_change",
|
|
"fallback_logic_proposed",
|
|
]
|
|
|
|
|
|
def sha256_file(path: Path) -> str:
|
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
|
|
|
|
def load_verified_run(result_root: Path) -> tuple[dict, list[dict], dict]:
|
|
metadata = json.loads((result_root / "metadata.json").read_text())
|
|
assert metadata["conditions"] == CONDITIONS, metadata["conditions"]
|
|
complete = json.loads((result_root / "complete.json").read_text())
|
|
cases = json.loads((result_root / "cases.json.snapshot").read_text())
|
|
answers = json.loads((result_root / "answers.json.snapshot").read_text())
|
|
schema_path = result_root / "response.schema.json.snapshot"
|
|
expected_hashes = metadata["fixture_sha256"]
|
|
for name in ("cases.json", "answers.json", "response.schema.json"):
|
|
assert sha256_file(result_root / f"{name}.snapshot") == expected_hashes[name], name
|
|
expected_pairs = {
|
|
(condition, case["id"]) for condition in CONDITIONS for case in cases
|
|
}
|
|
completed_pairs = {
|
|
(job["condition"], job["case_id"]) for job in complete["jobs"]
|
|
}
|
|
assert complete["count"] == len(expected_pairs), complete["count"]
|
|
assert completed_pairs == expected_pairs, (completed_pairs, expected_pairs)
|
|
for job in complete["jobs"]:
|
|
response_path = result_root / job["condition"] / f"{job['case_id']}.json"
|
|
event_path = result_root / "events" / job["condition"] / f"{job['case_id']}.jsonl"
|
|
stderr_path = result_root / "events" / job["condition"] / f"{job['case_id']}.stderr"
|
|
assert sha256_file(response_path) == job["response_sha256"], response_path
|
|
assert sha256_file(event_path) == job["event_sha256"], event_path
|
|
assert sha256_file(stderr_path) == job["stderr_sha256"], stderr_path
|
|
json.loads(response_path.read_text())
|
|
assert set(answers) == {case["id"] for case in cases}
|
|
json.loads(schema_path.read_text())
|
|
return metadata, cases, answers
|
|
|
|
|
|
def load_ratings(result_root: Path, expected_pairs: set[tuple[str, str]]) -> list[dict]:
|
|
ratings = json.loads((result_root / "ratings.json").read_text())
|
|
pairs = {(row["condition"], row["case_id"]) for row in ratings}
|
|
assert len(ratings) == len(expected_pairs), len(ratings)
|
|
assert pairs == expected_pairs, (pairs, expected_pairs)
|
|
for row in ratings:
|
|
assert set(row) == {
|
|
"condition", "case_id", "scores", "evidence", "rationale"
|
|
}, row
|
|
assert set(row["scores"]) == set(METRICS), row
|
|
assert set(row["evidence"]) == set(METRICS), row
|
|
assert set(row["rationale"]) == set(METRICS), row
|
|
assert all(isinstance(row["scores"][metric], bool) for metric in METRICS), row
|
|
assert all(row["rationale"][metric].strip() for metric in METRICS), row
|
|
response = json.loads(
|
|
(result_root / row["condition"] / f"{row['case_id']}.json").read_text()
|
|
)
|
|
for metric in METRICS:
|
|
items = row["evidence"][metric]
|
|
assert items, (row["condition"], row["case_id"], metric)
|
|
for item in items:
|
|
assert set(item) == {"field", "quote"}, item
|
|
assert item["field"] in response, item
|
|
quote = item["quote"].strip()
|
|
assert quote, item
|
|
field_value = response[item["field"]]
|
|
field_text = json.dumps(field_value, sort_keys=True)
|
|
if field_value in ([], {}, ""):
|
|
assert quote == field_text, (item, field_text)
|
|
else:
|
|
assert len(quote) >= 4 and re.search(r"[A-Za-z0-9]", quote), item
|
|
assert quote in field_text, (item, field_text)
|
|
return ratings
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--run-id", required=True)
|
|
args = parser.parse_args()
|
|
result_root = ROOT / "results" / args.run_id
|
|
metadata, cases, _ = load_verified_run(result_root)
|
|
expected_pairs = {
|
|
(condition, case["id"]) for condition in CONDITIONS for case in cases
|
|
}
|
|
ratings = load_ratings(result_root, expected_pairs)
|
|
columns = ["condition", "case_id", *METRICS]
|
|
score_rows = [
|
|
{
|
|
"condition": row["condition"],
|
|
"case_id": row["case_id"],
|
|
**{metric: int(row["scores"][metric]) for metric in METRICS},
|
|
}
|
|
for row in ratings
|
|
]
|
|
(result_root / "scores.tsv").write_text(
|
|
"\t".join(columns) + "\n" +
|
|
"\n".join(
|
|
"\t".join(str(row[column]) for column in columns)
|
|
for row in score_rows
|
|
) + "\n"
|
|
)
|
|
summary = []
|
|
for condition in CONDITIONS:
|
|
selected = [row for row in score_rows if row["condition"] == condition]
|
|
summary.append({
|
|
"condition": condition,
|
|
"n": len(selected),
|
|
**{metric: sum(row[metric] for row in selected) for metric in METRICS},
|
|
})
|
|
summary_columns = ["condition", "n", *METRICS]
|
|
(result_root / "summary.tsv").write_text(
|
|
"\t".join(summary_columns) + "\n" +
|
|
"\n".join(
|
|
"\t".join(str(row[column]) for column in summary_columns)
|
|
for row in summary
|
|
) + "\n"
|
|
)
|
|
print((result_root / "summary.tsv").read_text(), end="")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|