Ace-Step-v1.5 / acestep /local_cache.py
ChuxiJ's picture
feat: update api
2d5e35a
"""Local cache module to replace Redis
Uses diskcache as backend, provides Redis-compatible API.
Supports persistent storage and TTL expiration.
"""
import json
import os
from typing import Any, Optional
from threading import Lock
try:
from diskcache import Cache
HAS_DISKCACHE = True
except ImportError:
HAS_DISKCACHE = False
class LocalCache:
"""
Local cache implementation with Redis-compatible API.
Uses diskcache as backend, supports persistence and TTL.
"""
_instance = None
_lock = Lock()
def __new__(cls, cache_dir: Optional[str] = None):
"""Singleton pattern"""
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self, cache_dir: Optional[str] = None):
if getattr(self, '_initialized', False):
return
if not HAS_DISKCACHE:
raise ImportError(
"diskcache not installed. Run: pip install diskcache"
)
if cache_dir is None:
cache_dir = os.path.join(
os.path.dirname(os.path.dirname(__file__)),
".cache",
"local_redis"
)
os.makedirs(cache_dir, exist_ok=True)
self._cache = Cache(cache_dir)
self._initialized = True
def set(self, name: str, value: Any, ex: Optional[int] = None) -> bool:
"""
Set key-value pair
Args:
name: Key name
value: Value (auto-serialize dict/list)
ex: Expiration time (seconds)
Returns:
bool: Success status
"""
if isinstance(value, (dict, list)):
value = json.dumps(value, ensure_ascii=False)
self._cache.set(name, value, expire=ex)
return True
def get(self, name: str) -> Optional[str]:
"""Get value"""
return self._cache.get(name)
def delete(self, name: str) -> int:
"""Delete key, returns number of deleted items"""
return 1 if self._cache.delete(name) else 0
def exists(self, name: str) -> bool:
"""Check if key exists"""
return name in self._cache
def keys(self, pattern: str = "*") -> list:
"""
Get list of matching keys
Note: Simplified implementation, only supports prefix and full matching
"""
if pattern == "*":
return list(self._cache.iterkeys())
prefix = pattern.rstrip("*")
return [k for k in self._cache.iterkeys() if k.startswith(prefix)]
def expire(self, name: str, seconds: int) -> bool:
"""Set key expiration time"""
value = self._cache.get(name)
if value is not None:
self._cache.set(name, value, expire=seconds)
return True
return False
def ttl(self, name: str) -> int:
"""
Get remaining time to live (seconds)
Note: diskcache does not directly support TTL queries
"""
if name in self._cache:
return -1 # Exists but TTL unknown
return -2 # Key does not exist
def close(self):
"""Close cache connection"""
if hasattr(self, '_cache'):
self._cache.close()
# Lazily initialized global instance
_local_cache: Optional[LocalCache] = None
def get_local_cache(cache_dir: Optional[str] = None) -> LocalCache:
"""Get local cache instance"""
global _local_cache
if _local_cache is None:
_local_cache = LocalCache(cache_dir)
return _local_cache