import json, hashlib, os from pathlib import Path import httpx class LLMClient: def __init__(self, model="gpt-4o-mini", timeout=15, cache_dir=".cache/llm"): self.model = model self.timeout = timeout self.dir = Path(cache_dir) self.dir.mkdir(parents=True, exist_ok=True) def _key(self, msgs): return hashlib.sha256(json.dumps(msgs, sort_keys=True).encode()).hexdigest() def _get(self, k): p = self.dir / f"{k}.json" return json.loads(p.read_text())["response"] if p.exists() else None def _set(self, k, v): (self.dir / f"{k}.json").write_text(json.dumps({"response": v})) def call(self, messages, retries=1): k = self._key(messages) c = self._get(k) if c: return c key = os.environ.get("LLM_API_KEY", os.environ.get("OPENAI_API_KEY", "")) base = os.environ.get("LLM_API_BASE", "https://api.openai.com/v1") for a in range(retries + 1): try: r = httpx.post(f"{base}/chat/completions", json={"model": self.model, "messages": messages}, headers={"Authorization": f"Bearer {key}"}, timeout=self.timeout) r.raise_for_status() v = r.json()["choices"][0]["message"]["content"] self._set(k, v) return v except Exception: if a == retries: raise return ""