From ebd78cf88ea042a648e30e7231bba5c7a1713f4a Mon Sep 17 00:00:00 2001 From: egregore Date: Mon, 2 Feb 2026 12:58:33 +0000 Subject: [PATCH] Add rate limiting to /process endpoint - Uses slowapi for rate limiting - Default: 10 requests/minute per IP - Configurable via RATE_LIMIT env var - Returns HTTP 429 when limit exceeded --- main.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index bda4fb8..57ac118 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Egregore Brain Service - AI reasoning API +Egregore Reason Service - AI reasoning API Provides HTTP API for conversation processing with Claude. Runs on port 8081. @@ -9,9 +9,12 @@ Runs on port 8081. import os from typing import Optional -from fastapi import FastAPI, HTTPException +from fastapi import FastAPI, HTTPException, Request from pydantic import BaseModel from dotenv import load_dotenv +from slowapi import Limiter, _rate_limit_exceeded_handler +from slowapi.util import get_remote_address +from slowapi.errors import RateLimitExceeded import anthropic from tools import TOOLS, execute_tool @@ -25,7 +28,15 @@ ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY") if not ANTHROPIC_API_KEY: raise ValueError("ANTHROPIC_API_KEY not set") -app = FastAPI(title="Egregore Brain Service", docs_url="/docs") +# Rate limiting configuration +RATE_LIMIT = os.getenv("RATE_LIMIT", "10/minute") + +# Initialize rate limiter +limiter = Limiter(key_func=get_remote_address) + +app = FastAPI(title="Egregore Reason Service", docs_url="/docs") +app.state.limiter = limiter +app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) # Initialize Anthropic client client = anthropic.AsyncAnthropic(api_key=ANTHROPIC_API_KEY) @@ -45,7 +56,8 @@ class ToolRequest(BaseModel): # Endpoints @app.post("/process") -async def api_process(req: ProcessRequest): +@limiter.limit(RATE_LIMIT) +async def api_process(request: Request, req: ProcessRequest): """Process a conversation with tool use loop""" try: response_blocks = await process_conversation( @@ -82,7 +94,7 @@ async def api_get_prompt(): @app.get("/health") async def health(): """Health check endpoint""" - return {"status": "ok", "service": "brain"} + return {"status": "ok", "service": "reason"} if __name__ == "__main__":