rmi-backend/app/caching_shield/router.py
opencode c762564d40 style(rmi-backend): complete lint cleanup — 1175→0 ruff errors
- Fix 71 invalid-syntax files (class-body newline-broken assignments)
- Add from/None chain to 307 B904 raise-without-from sites
- Add B008 ignore to ruff.toml (already in pyproject.toml)
- Noqa F401 on __init__.py re-exports (137 sites)
- Noqa E402 on deferred imports (63 sites)
- Bulk-add stdlib/FastAPI/project imports for F821 (127 sites)
- Replace ×→x, –→-, …→... in docstrings (4093 chars)
- Manual refactor of 5 SIM103/SIM116 patterns

Tests: 791 passed (66 deselected due to pre-existing Redis issues in test_rag.py)
Co-authored-by: opencode <opencode@rugmunch.io>
2026-07-06 15:43:20 +02:00

134 lines
4 KiB
Python

"""
Aggressive Caching Shield - FastAPI Router
Monitor and control the caching shield via API endpoints.
Mount in main.py:
from app.caching_shield.router import router
app.include_router(router)
Endpoints:
GET /api/v1/cache/health - All shield components health + stats
GET /api/v1/cache/stats - Detailed cache statistics
POST /api/v1/cache/clear - Clear L1 cache (admin)
GET /api/v1/cache/rate-limits - Current rate limit bucket states
"""
import os
import time
from fastapi import APIRouter, HTTPException, Request
from app.caching_shield.history_depth import get_history_controller
from app.caching_shield.rate_limiter import get_rate_limiter
from app.caching_shield.rpc_cache import get_rpc_cache
from app.caching_shield.ws_broadcaster import get_ws_manager
router = APIRouter(prefix="/api/v1/cache", tags=["caching-shield"])
def _verify_admin(request: Request) -> bool:
"""Verify admin key for destructive operations."""
admin_key = os.getenv("ADMIN_API_KEY", "")
if not admin_key:
return True # No key configured, allow from localhost
provided = request.headers.get("X-Admin-Key", "")
return provided == admin_key
@router.get("/health")
async def cache_health():
"""Aggregate health check for all caching shield components."""
cache = get_rpc_cache()
limiter = get_rate_limiter()
ws = get_ws_manager()
hdc = get_history_controller()
cache_h = await cache.health()
ws_s = await ws.stats()
hdc_s = hdc.stats()
return {
"status": "ok",
"timestamp": time.time(),
"components": {
"rpc_cache": cache_h,
"rate_limiter": {"configured_providers": list(limiter.get_all_limits().keys())},
"ws_broadcaster": ws_s,
"history_depth": hdc_s,
},
"summary": {
"cache_hit_rate_pct": cache_h.get("hit_rate_pct", 0),
"redis_available": cache_h.get("redis_available", False),
"total_ws_clients": ws_s.get("total_clients", 0),
"broadcasts_sent": ws_s.get("broadcasts_sent", 0),
},
}
@router.get("/stats")
async def cache_stats():
"""Detailed cache statistics."""
cache = get_rpc_cache()
return await cache.health()
@router.post("/clear")
async def cache_clear(request: Request):
"""Clear L1 in-memory cache. Requires admin key."""
if not _verify_admin(request):
raise HTTPException(status_code=401, detail="Invalid admin key")
cache = get_rpc_cache()
await cache.clear_l1()
return {
"status": "cleared",
"timestamp": time.time(),
"message": "L1 in-memory cache cleared. L2 (Redis) cache intact.",
}
@router.get("/rate-limits")
async def rate_limits_status():
"""Get current token bucket states for all providers."""
limiter = get_rate_limiter()
limits = limiter.get_all_limits()
result = {}
for provider, limit in limits.items():
# Get current bucket state
tokens, _last_refill, burst_used = await limiter.get_bucket_state(provider)
result[provider] = {
"tokens_per_sec": limit.tokens_per_sec,
"burst_size": limit.burst_size,
"current_tokens": round(tokens, 1),
"available_pct": round(tokens / limit.burst_size * 100, 1) if limit.burst_size else 0,
"burst_used": burst_used,
}
return {
"timestamp": time.time(),
"providers": result,
}
@router.get("/solana-tracker")
async def solana_tracker_stats():
"""Get Solana Tracker multi-key load balancing stats."""
from app.caching_shield.solana_tracker import get_solana_tracker
st = get_solana_tracker()
return {
"timestamp": time.time(),
**st.stats(),
}
@router.get("/capacity")
async def capacity_report():
"""Full API capacity analysis - all providers, keys, free APIs, and backend sources."""
from app.caching_shield.api_registry import list_all_sources
return {
"timestamp": time.time(),
**list_all_sources(),
}