study-buddy / app /services /output_cache.py
GitHub Actions
deploy b3d187d69e5df10bb2ec396e89f539f853465a06
14184e3
Raw
History Blame Contribute Delete
2.16 kB
"""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