""" Disk-based cache for LLM API responses. Uses SHA256 of (prompt + model + system_prompt) as cache key. Cache files stored as JSON in ``.cache/`` directory. """ import hashlib import json from pathlib import Path class LLMCache: """SHA256-based disk cache for LLM API responses.""" def __init__(self, cache_dir: str | None = None): if cache_dir is None: import os cache_dir = os.path.join(os.path.dirname(__file__), ".cache") self.cache_dir = Path(cache_dir) self.cache_dir.mkdir(parents=True, exist_ok=True) def _make_key(self, prompt: str, model: str, system_prompt: str = "") -> str: """Create deterministic cache key from request parameters.""" data = json.dumps( {"prompt": prompt, "model": model, "system": system_prompt}, sort_keys=True, ) return hashlib.sha256(data.encode()).hexdigest() def get(self, prompt: str, model: str, system_prompt: str = "") -> dict | None: """Retrieve cached response if exists.""" key = self._make_key(prompt, model, system_prompt) path = self.cache_dir / f"{key}.json" if path.exists(): try: with open(path) as f: return json.load(f) except (json.JSONDecodeError, IOError): return None return None def set( self, prompt: str, model: str, system_prompt: str, response: dict ) -> None: """Store response in cache.""" key = self._make_key(prompt, model, system_prompt) path = self.cache_dir / f"{key}.json" with open(path, "w") as f: json.dump(response, f, ensure_ascii=False) def clear(self) -> None: """Clear all cached responses.""" for f in self.cache_dir.glob("*.json"): f.unlink()