"""Content-addressed file cache for LLM products. Keys are sha256 hashes of (prompt version, model, inputs...), so a cache entry is invalidated exactly when one of those changes — bump PROMPT_VERSION in the calling service to retire stale generations. Values are small JSON documents. On the Space the directory is ephemeral (rebuilt on restart), which is fine: the cache is a quota saver, not a store of record (that's M3 / Supabase). """ import hashlib import json from pathlib import Path class FileCache: def __init__(self, root: Path | str) -> None: self.root = Path(root) self.root.mkdir(parents=True, exist_ok=True) @staticmethod def key(*parts: str) -> str: return hashlib.sha256("\x1f".join(parts).encode("utf-8")).hexdigest() def get(self, key: str) -> dict | None: path = self.root / f"{key}.json" if not path.exists(): return None try: return json.loads(path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError): return None # a corrupt entry behaves like a miss def set(self, key: str, value: dict) -> None: (self.root / f"{key}.json").write_text( json.dumps(value, ensure_ascii=False), encoding="utf-8" )