Spaces:
Sleeping
Sleeping
| """Content-addressed output cache -> persist & reuse agent outputs. | |
| Key = sha256(SCHEMA_VERSION | event_name | familiarity | anchor_id | input_fingerprint) | |
| where input_fingerprint = sha256(exact chunk texts + selection_text). | |
| Any change to content, selection, familiarity or anchor naturally misses. | |
| SCHEMA_VERSION bump mass-invalidates all cached entries. | |
| """ | |
| import hashlib | |
| import json | |
| import os | |
| from typing import Any, Optional | |
| SCHEMA_VERSION = "1" | |
| _CACHE_DIR = os.path.expanduser("~/.studybuddy/cache") | |
| class OutputCache: | |
| def __init__(self) -> None: | |
| os.makedirs(_CACHE_DIR, exist_ok=True) | |
| self._mem: dict[str, Any] = {} | |
| def fingerprint(self, *parts: str) -> str: | |
| combined = "|".join(parts) | |
| return hashlib.sha256(combined.encode()).hexdigest() | |
| def make_key( | |
| self, | |
| event_name: str, | |
| familiarity: str, | |
| anchor_id: str, | |
| chunk_texts: list[str], | |
| selection_text: str = "", | |
| image_hash: str = "", | |
| ) -> str: | |
| fp = self.fingerprint(*chunk_texts, selection_text, image_hash) | |
| raw = f"{SCHEMA_VERSION}|{event_name}|{familiarity}|{anchor_id}|{fp}" | |
| return hashlib.sha256(raw.encode()).hexdigest() | |
| def _path(self, key: str) -> str: | |
| return os.path.join(_CACHE_DIR, f"{key}.json") | |
| def get(self, key: str) -> Optional[Any]: | |
| if key in self._mem: | |
| return self._mem[key] | |
| p = self._path(key) | |
| if os.path.exists(p): | |
| with open(p, encoding="utf-8") as f: | |
| val = json.load(f) | |
| self._mem[key] = val | |
| return val | |
| return None | |
| def put(self, key: str, payload: Any) -> None: | |
| self._mem[key] = payload | |
| with open(self._path(key), "w", encoding="utf-8") as f: | |
| json.dump(payload, f) | |
| def clear(self) -> int: | |
| """Dev helper -> wipe all cached entries. Returns count deleted.""" | |
| count = 0 | |
| for fname in os.listdir(_CACHE_DIR): | |
| if fname.endswith(".json"): | |
| os.remove(os.path.join(_CACHE_DIR, fname)) | |
| count += 1 | |
| self._mem.clear() | |
| return count | |