madDegen's picture
download
raw
4.98 kB
"""
Agent-Q3 — Redis-backed shared memory layer.
Used by all services (multimodal, coder, research, monitor) to:
- Cache MCP tool call results (TTL'd)
- Store conversation history per session
- Persist monitor metrics across restarts
- Track skill activation log
- Coordinate cross-service rate limits
Falls back to a no-op in-memory store if REDIS_URL is unset.
"""
import json
import time
from typing import Any
import structlog
from .config import settings
log = structlog.get_logger(__name__)
class _InMemoryFallback:
"""Drop-in async stub used when REDIS_URL is unset."""
def __init__(self):
self._store: dict[str, tuple[float, str]] = {}
self._lists: dict[str, list] = {}
async def get(self, key: str) -> str | None:
item = self._store.get(key)
if not item:
return None
expires_at, value = item
if expires_at and time.time() > expires_at:
self._store.pop(key, None)
return None
return value
async def set(self, key: str, value: str, ex: int | None = None) -> None:
exp = (time.time() + ex) if ex else 0
self._store[key] = (exp, value)
async def delete(self, *keys: str) -> None:
for k in keys:
self._store.pop(k, None)
async def lpush(self, key: str, *values: str) -> int:
self._lists.setdefault(key, [])
for v in values:
self._lists[key].insert(0, v)
return len(self._lists[key])
async def lrange(self, key: str, start: int, stop: int) -> list:
lst = self._lists.get(key, [])
if stop == -1:
return lst[start:]
return lst[start : stop + 1]
async def ltrim(self, key: str, start: int, stop: int) -> None:
lst = self._lists.get(key, [])
if stop == -1:
self._lists[key] = lst[start:]
else:
self._lists[key] = lst[start : stop + 1]
async def ping(self) -> bool:
return True
async def close(self) -> None:
pass
class Memory:
"""Async memory facade. Uses redis.asyncio when REDIS_URL set, else in-memory."""
def __init__(self):
self._client = None
self._backend: str = "uninitialized"
async def connect(self) -> None:
if settings.redis_url:
try:
from redis.asyncio import from_url
self._client = from_url(settings.redis_url, decode_responses=True)
await self._client.ping()
self._backend = "redis"
log.info("memory connected", backend="redis", url=settings.redis_url.split("@")[-1])
return
except Exception as e:
log.warning("redis unavailable, falling back to in-memory", error=str(e))
self._client = _InMemoryFallback()
self._backend = "in-memory"
log.info("memory connected", backend="in-memory")
@property
def backend(self) -> str:
return self._backend
@property
def client(self):
return self._client
# ── Typed helpers ────────────────────────────────────────────────────────
async def cache_get(self, key: str) -> Any | None:
raw = await self._client.get(f"cache:{key}")
return json.loads(raw) if raw else None
async def cache_set(self, key: str, value: Any, ttl_secs: int = 300) -> None:
await self._client.set(f"cache:{key}", json.dumps(value), ex=ttl_secs)
async def log_event(self, channel: str, event: dict, max_history: int = 500) -> None:
key = f"events:{channel}"
await self._client.lpush(key, json.dumps({"ts": time.time(), **event}))
await self._client.ltrim(key, 0, max_history - 1)
async def get_events(self, channel: str, limit: int = 50) -> list[dict]:
raw_items = await self._client.lrange(f"events:{channel}", 0, limit - 1)
return [json.loads(x) for x in raw_items]
async def session_append(self, session_id: str, message: dict, max_turns: int = 50) -> None:
key = f"session:{session_id}"
await self._client.lpush(key, json.dumps(message))
await self._client.ltrim(key, 0, max_turns - 1)
async def session_history(self, session_id: str, limit: int = 50) -> list[dict]:
raw_items = await self._client.lrange(f"session:{session_id}", 0, limit - 1)
return [json.loads(x) for x in reversed(raw_items)]
async def ping(self) -> bool:
try:
return await self._client.ping()
except Exception:
return False
async def close(self) -> None:
try:
await self._client.close()
except Exception:
pass
memory = Memory()

Xet Storage Details

Size:
4.98 kB
·
Xet hash:
599436c4cc6611d16a613a18888a3a1ec374b7d0270e2aab9a3b5eb88f0027d2

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.