Shoaib-33's picture
added
da32bf1
Raw
History Blame Contribute Delete
4.2 kB
from __future__ import annotations
import hashlib
import json
import os
from collections import OrderedDict
from dataclasses import dataclass
from typing import Any
import numpy as np
def stable_hash(payload: Any) -> str:
serialized = json.dumps(payload, sort_keys=True, default=str)
return hashlib.sha256(serialized.encode("utf-8")).hexdigest()
class LRUCache:
def __init__(self, max_size: int = 256) -> None:
self.max_size = max_size
self._items: OrderedDict[str, Any] = OrderedDict()
def get(self, key: str) -> Any | None:
if key not in self._items:
return None
self._items.move_to_end(key)
return self._items[key]
def set(self, key: str, value: Any) -> None:
self._items[key] = value
self._items.move_to_end(key)
if len(self._items) > self.max_size:
self._items.popitem(last=False)
@dataclass
class SemanticCacheEntry:
vector: list[float]
value: Any
class SemanticCache:
def __init__(self, threshold: float = 0.92, max_size: int = 128) -> None:
self.threshold = threshold
self.max_size = max_size
self._items: list[SemanticCacheEntry] = []
def get(self, vector: list[float]) -> Any | None:
if not self._items:
return None
query = np.array(vector, dtype=np.float32)
query_norm = np.linalg.norm(query)
if query_norm == 0:
return None
best_score = -1.0
best_value = None
for entry in self._items:
candidate = np.array(entry.vector, dtype=np.float32)
denom = query_norm * np.linalg.norm(candidate)
score = float(np.dot(query, candidate) / denom) if denom else 0.0
if score > best_score:
best_score = score
best_value = entry.value
return best_value if best_score >= self.threshold else None
def set(self, vector: list[float], value: Any) -> None:
self._items.append(SemanticCacheEntry(vector=vector, value=value))
if len(self._items) > self.max_size:
self._items.pop(0)
class RedisSemanticCache:
def __init__(self, namespace: str, threshold: float = 0.92, max_size: int = 256) -> None:
self.namespace = namespace
self.threshold = threshold
self.max_size = max_size
self.enabled = False
self._redis = None
redis_url = os.getenv("REDIS_URL")
if not redis_url:
return
try:
import redis
self._redis = redis.Redis.from_url(redis_url, decode_responses=True)
self._redis.ping()
self.enabled = True
except Exception:
self._redis = None
self.enabled = False
def get(self, vector: list[float]) -> Any | None:
if not self.enabled or self._redis is None:
return None
query = np.array(vector, dtype=np.float32)
query_norm = np.linalg.norm(query)
if query_norm == 0:
return None
best_score = -1.0
best_value = None
index_key = f"{self.namespace}:index"
for item_key in self._redis.lrange(index_key, 0, -1):
raw = self._redis.get(item_key)
if not raw:
continue
item = json.loads(raw)
candidate = np.array(item["vector"], dtype=np.float32)
denom = query_norm * np.linalg.norm(candidate)
score = float(np.dot(query, candidate) / denom) if denom else 0.0
if score > best_score:
best_score = score
best_value = item["value"]
return best_value if best_score >= self.threshold else None
def set(self, vector: list[float], value: Any) -> None:
if not self.enabled or self._redis is None:
return
payload = {"vector": vector, "value": value}
item_key = f"{self.namespace}:item:{stable_hash(payload)}"
index_key = f"{self.namespace}:index"
self._redis.set(item_key, json.dumps(payload, default=str))
self._redis.lpush(index_key, item_key)
self._redis.ltrim(index_key, 0, self.max_size - 1)