- Add web_search tool with Jina Search/Reader/Rerank - Flash mode: top 3, 8s timeout, no Reader/Rerank - Pro mode: top 10, 20s timeout, concurrent Reader + Rerank top 5 - Add Redis async cache (TTL=300s) for search results - Register "search" in ALL_TOOLS mapping Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
74 lines
2.0 KiB
Python
74 lines
2.0 KiB
Python
"""Redis cache client for search result caching.
|
|
|
|
Uses Azure Redis (TLS on port 6380).
|
|
Cache key pattern: search:{query_hash}:{model}
|
|
TTL: 300 seconds (5 minutes).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import logging
|
|
|
|
import redis.asyncio as redis
|
|
|
|
from app.config import settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_pool: redis.Redis | None = None
|
|
|
|
CACHE_TTL = 300 # seconds
|
|
|
|
|
|
async def get_redis() -> redis.Redis:
|
|
"""Return a singleton async Redis client."""
|
|
global _pool
|
|
if _pool is None:
|
|
_pool = redis.from_url(
|
|
settings.redis_url,
|
|
decode_responses=True,
|
|
socket_connect_timeout=5,
|
|
socket_timeout=5,
|
|
)
|
|
return _pool
|
|
|
|
|
|
async def close_redis() -> None:
|
|
"""Close the Redis connection pool (for clean shutdown)."""
|
|
global _pool
|
|
if _pool is not None:
|
|
await _pool.aclose()
|
|
_pool = None
|
|
|
|
|
|
def _cache_key(query: str, model: str) -> str:
|
|
"""Build a cache key from query hash and model."""
|
|
query_hash = hashlib.sha256(query.strip().lower().encode("utf-8")).hexdigest()[:16]
|
|
return f"search:{query_hash}:{model}"
|
|
|
|
|
|
async def get_cached_search(query: str, model: str) -> str | None:
|
|
"""Look up a cached search result. Returns None on miss or error."""
|
|
try:
|
|
r = await get_redis()
|
|
key = _cache_key(query, model)
|
|
value = await r.get(key)
|
|
if value is not None:
|
|
logger.info("Cache HIT for key=%s", key)
|
|
return value
|
|
except Exception:
|
|
logger.warning("Redis GET failed, treating as cache miss", exc_info=True)
|
|
return None
|
|
|
|
|
|
async def set_cached_search(query: str, model: str, result: str) -> None:
|
|
"""Store a search result in cache with TTL."""
|
|
try:
|
|
r = await get_redis()
|
|
key = _cache_key(query, model)
|
|
await r.set(key, result, ex=CACHE_TTL)
|
|
logger.info("Cache SET key=%s ttl=%ds", key, CACHE_TTL)
|
|
except Exception:
|
|
logger.warning("Redis SET failed, result not cached", exc_info=True)
|