Next-level improvements: 15 items across backend, frontend, and testing
Backend: - SQLiteStateBackend: persistent task/trace storage with SQLite - InMemoryStateBackend: in-memory impl of StateBackend interface - Redis cache backend (CacheBackend ABC + MemoryCacheBackend + RedisCacheBackend) - OpenAI adapter: async acomplete() with retry logic - Per-tenant + per-IP rate limiting in middleware Frontend: - State management: useStore + useAppState (zero-dep, context + reducer) - React Router integration: URL-based navigation (usePageNavigation) - WebSocket streaming: sendPrompt + StreamCallbacks for token-by-token updates - File preview: inline image/text/binary preview with expand/collapse - Sparkline charts + MetricCard + BarChart for dashboard visualization - Push notifications hook (useNotifications) with browser Notification API - i18n system: 6 locales (en, es, fr, de, ja, zh) with interpolation - 6 new Storybook stories (ChatMessage, Skeleton, Markdown, SearchFilter, Toast, FilePreview) Testing: - Playwright E2E config + 6 browser specs (desktop + mobile) - 18 new Python tests (SQLiteStateBackend, InMemoryStateBackend, cache backends) 570 Python tests + 45 frontend tests = 615 total, 0 ruff errors. Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
This commit is contained in:
@@ -93,23 +93,39 @@ def create_app(
|
||||
_buckets: dict[str, list[float]] = defaultdict(list)
|
||||
|
||||
class RateLimitMiddleware(BaseHTTPMiddleware):
|
||||
"""Per-IP sliding window rate limiter (advisory mode).
|
||||
"""Per-tenant + per-IP sliding window rate limiter (advisory mode).
|
||||
|
||||
Logs rate limit exceedances but allows the request through.
|
||||
Consistent with the advisory governance philosophy.
|
||||
Tracks both IP-level and tenant-level request rates. Logs exceedances
|
||||
but allows requests through (advisory governance).
|
||||
"""
|
||||
|
||||
async def dispatch(self, request: Request, call_next: Any) -> Response:
|
||||
client_ip = request.client.host if request.client else "unknown"
|
||||
tenant_id = request.headers.get("x-tenant-id", "default")
|
||||
now = time.monotonic()
|
||||
cutoff = now - rate_window
|
||||
_buckets[client_ip] = [t for t in _buckets[client_ip] if t > cutoff]
|
||||
if len(_buckets[client_ip]) >= rate_limit:
|
||||
|
||||
# Per-IP tracking
|
||||
ip_key = f"ip:{client_ip}"
|
||||
_buckets[ip_key] = [t for t in _buckets[ip_key] if t > cutoff]
|
||||
if len(_buckets[ip_key]) >= rate_limit:
|
||||
logger.info(
|
||||
"API rate limit advisory: limit exceeded (proceeding)",
|
||||
extra={"client_ip": client_ip, "count": len(_buckets[client_ip]), "limit": rate_limit},
|
||||
"API rate limit advisory: IP limit exceeded (proceeding)",
|
||||
extra={"client_ip": client_ip, "count": len(_buckets[ip_key]), "limit": rate_limit},
|
||||
)
|
||||
_buckets[client_ip].append(now)
|
||||
|
||||
# Per-tenant tracking (separate quota)
|
||||
tenant_key = f"tenant:{tenant_id}"
|
||||
tenant_limit = rate_limit * 5 # tenants get 5x the per-IP limit
|
||||
_buckets[tenant_key] = [t for t in _buckets[tenant_key] if t > cutoff]
|
||||
if len(_buckets[tenant_key]) >= tenant_limit:
|
||||
logger.info(
|
||||
"API rate limit advisory: tenant limit exceeded (proceeding)",
|
||||
extra={"tenant_id": tenant_id, "count": len(_buckets[tenant_key]), "limit": tenant_limit},
|
||||
)
|
||||
|
||||
_buckets[ip_key].append(now)
|
||||
_buckets[tenant_key].append(now)
|
||||
return await call_next(request) # type: ignore[no-any-return]
|
||||
|
||||
app.add_middleware(RateLimitMiddleware)
|
||||
|
||||
Reference in New Issue
Block a user