#!/usr/bin/env python3 """ Egregore Reason Service - AI reasoning API Provides HTTP API for conversation processing with Claude. Runs on port 8081. """ import os from typing import Optional 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 from prompts import get_system_prompt from conversation import process_conversation # Load environment load_dotenv("/home/admin/.env") ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY") if not ANTHROPIC_API_KEY: raise ValueError("ANTHROPIC_API_KEY not set") # 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) # Request models class ProcessRequest(BaseModel): model: str = "claude-sonnet-4-20250514" history: list # Conversation history in Claude API format max_iterations: int = 10 class ToolRequest(BaseModel): name: str input: dict # Endpoints @app.post("/process") @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( client=client, model=req.model, history=req.history, max_iterations=req.max_iterations ) return {"blocks": response_blocks} except anthropic.APIError as e: raise HTTPException(status_code=500, detail=str(e)) @app.post("/tool") async def api_execute_tool(req: ToolRequest): """Execute a single tool directly""" result = await execute_tool(req.name, req.input) return {"result": result} @app.get("/tools") async def api_get_tools(): """Get available tool definitions""" return {"tools": TOOLS} @app.get("/prompt") async def api_get_prompt(): """Get current system prompt with context""" prompt = await get_system_prompt() return {"prompt": prompt} @app.get("/health") async def health(): """Health check endpoint""" return {"status": "ok", "service": "reason"} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="127.0.0.1", port=8081)