"""Persist a run's results so the memo can be re-rendered without re-running inference. Detoxify on CPU is the slow part; with a cache, every later tweak to report.py is instant via scripts/render_memo.py. The results dict carries numpy scalars (scores, metrics); they are coerced to native Python types so the cache is plain JSON. """ from __future__ import annotations import json import numpy as np def _default(o): if isinstance(o, np.integer): return int(o) if isinstance(o, np.floating): return float(o) if isinstance(o, np.bool_): return bool(o) if isinstance(o, np.ndarray): return o.tolist() return str(o) def save(path: str, results: dict, meta: dict) -> None: with open(path, "w", encoding="utf-8") as f: json.dump({"meta": meta, "results": results}, f, default=_default, indent=2) def load(path: str): """Return (results, meta) from a cache file.""" with open(path, "r", encoding="utf-8") as f: d = json.load(f) return d["results"], d["meta"]