| """Caching layer for HuggingFace Crypto Data Engine""" |
| from __future__ import annotations |
| from typing import Optional, Any |
| from datetime import datetime, timedelta |
| import time |
| import json |
| from dataclasses import dataclass |
|
|
|
|
| @dataclass |
| class CacheEntry: |
| """Cache entry with TTL""" |
| value: Any |
| expires_at: float |
|
|
|
|
| class MemoryCache: |
| """In-memory cache with TTL support""" |
|
|
| def __init__(self): |
| self._cache: dict[str, CacheEntry] = {} |
| self._hits = 0 |
| self._misses = 0 |
|
|
| def get(self, key: str) -> Optional[Any]: |
| """Get value from cache""" |
| if key not in self._cache: |
| self._misses += 1 |
| return None |
|
|
| entry = self._cache[key] |
|
|
| |
| if time.time() > entry.expires_at: |
| del self._cache[key] |
| self._misses += 1 |
| return None |
|
|
| self._hits += 1 |
| return entry.value |
|
|
| def set(self, key: str, value: Any, ttl: int): |
| """Set value in cache with TTL in seconds""" |
| expires_at = time.time() + ttl |
| self._cache[key] = CacheEntry(value=value, expires_at=expires_at) |
|
|
| def delete(self, key: str): |
| """Delete key from cache""" |
| if key in self._cache: |
| del self._cache[key] |
|
|
| def clear(self): |
| """Clear all cache entries""" |
| self._cache.clear() |
| self._hits = 0 |
| self._misses = 0 |
|
|
| def get_stats(self) -> dict: |
| """Get cache statistics""" |
| total = self._hits + self._misses |
| hit_rate = (self._hits / total) if total > 0 else 0 |
|
|
| return { |
| "size": len(self._cache), |
| "hits": self._hits, |
| "misses": self._misses, |
| "hitRate": round(hit_rate, 2) |
| } |
|
|
| def cleanup_expired(self): |
| """Remove expired entries""" |
| current_time = time.time() |
| expired_keys = [ |
| key for key, entry in self._cache.items() |
| if current_time > entry.expires_at |
| ] |
|
|
| for key in expired_keys: |
| del self._cache[key] |
|
|
|
|
| |
| cache = MemoryCache() |
|
|
|
|
| def cache_key(prefix: str, **kwargs) -> str: |
| """Generate cache key from prefix and parameters""" |
| params = ":".join(f"{k}={v}" for k, v in sorted(kwargs.items())) |
| return f"{prefix}:{params}" if params else prefix |
|
|
|
|
| async def get_or_set( |
| key: str, |
| ttl: int, |
| factory: callable |
| ) -> Any: |
| """Get from cache or compute and store""" |
| |
| cached = cache.get(key) |
| if cached is not None: |
| return cached |
|
|
| |
| value = await factory() |
|
|
| |
| cache.set(key, value, ttl) |
|
|
| return value |
|
|