Terminal / api /semantic_cache.py
Baida-A
Initial clean deploy (Reverse Proxy removed)
28a08e7
Raw
History Blame
2.79 kB
"""
backend/api/semantic_cache.py — S-CACHE-1: Cross-Node Semantic Cache
Ottimizzazione ZeroGPU: evita ricalcoli identici tra nodi A-E.
"""
import hashlib
import json
import logging
import time
from typing import Optional
from fastapi import APIRouter, Depends
from .auth_guard import require_role, AuthRole
from redis import redis_get, redis_set
_logger = logging.getLogger("api.semantic_cache")
router = APIRouter(prefix="/api/cache", tags=["cache"], dependencies=[Depends(require_role(AuthRole.MACHINE))])
# TTL: 24 ore per risposte semantiche
_CACHE_TTL = 86400
def _gen_key(messages: list) -> str:
"""Genera una chiave hash basata sull'ultimo messaggio user (semplificato)."""
try:
# Prendiamo solo l'ultimo messaggio user per la cache semantica "veloce"
user_msgs = [m for m in messages if m.get("role") == "user"]
if not user_msgs:
return ""
last_content = user_msgs[-1].get("content", "").strip().lower()
h = hashlib.sha256(last_content.encode()).hexdigest()
return f"semcache:v1:{h[:16]}"
except Exception:
return ""
@router.post("/lookup")
async def cache_lookup(payload: dict):
"""Verifica se esiste una risposta cachata per il prompt corrente."""
messages = payload.get("messages", [])
key = _gen_key(messages)
if not key:
return {"hit": False}
cached = await redis_get(key)
if cached:
_logger.info(f"[cache] Hit semantica per chiave {key}")
try:
return {"hit": True, "content": json.loads(cached)}
except:
return {"hit": True, "content": cached}
return {"hit": False}
@router.post("/store")
async def cache_store(payload: dict):
"""Salva una risposta nella cache semantica condivisa."""
messages = payload.get("messages", [])
content = payload.get("content", "")
if not content or len(content) < 50: # Non cachiamo risposte troppo brevi
return {"ok": False}
key = _gen_key(messages)
if not key:
return {"ok": False}
# Salviamo come JSON per gestire metadati futuri
ok = await redis_set(key, json.dumps(content), ttl=_CACHE_TTL)
return {"ok": ok}
async def get_cached_response(messages: list) -> Optional[str]:
"""Helper interno per AIClient."""
key = _gen_key(messages)
if not key:
return None
cached = await redis_get(key)
if cached:
try:
return json.loads(cached)
except:
return cached
return None
async def set_cached_response(messages: list, content: str):
"""Helper interno per AIClient."""
key = _gen_key(messages)
if key and content and len(content) >= 50:
await redis_set(key, json.dumps(content), ttl=_CACHE_TTL)