""" Grimlock - AI-Native Company Operating System Main FastAPI Application """ from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from contextlib import asynccontextmanager import logging from dotenv import load_dotenv import os import socketio # Remove circular import - comment out chat router # from api.chat import router as chat_router from api.auth import router as auth_router from api.channels import router as channels_router from api.messages import router as messages_router from api.direct_messages import router as dm_router from api.files import router as files_router from core.context_manager import ContextManager from core.ai_client import AIClient from core.database import engine from core.models import Base from core.websocket import sio, broadcast_new_message # Load environment variables load_dotenv() # Configure logging logging.basicConfig( level=os.getenv("LOG_LEVEL", "INFO"), format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) # Global state context_manager = None ai_client = None @asynccontextmanager async def lifespan(app: FastAPI): """Startup and shutdown events""" global context_manager, ai_client logger.info("Starting Grimlock backend...") # Create database tables Base.metadata.create_all(bind=engine) logger.info("Database tables created/verified") # Initialize context manager context_path = os.getenv("CONTEXT_PATH", "./context") context_manager = ContextManager(context_path) context_manager.load_all_context() logger.info(f"Loaded context from {context_path}") # Initialize AI client api_key = os.getenv("ANTHROPIC_API_KEY") if not api_key: logger.error("ANTHROPIC_API_KEY not set!") raise ValueError("ANTHROPIC_API_KEY environment variable is required") ai_client = AIClient(api_key=api_key) logger.info("AI client initialized") logger.info("WebSocket server ready") yield # Cleanup logger.info("Shutting down Grimlock backend...") # Create FastAPI app app = FastAPI( title="Grimlock", description="AI-Native Company Operating System", version="0.3.0", lifespan=lifespan ) # Mount Socket.IO socket_app = socketio.ASGIApp(sio, app) # CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], # Configure appropriately for production allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Include routers (chat router removed to fix circular import) app.include_router(auth_router, prefix="/api/auth", tags=["auth"]) app.include_router(channels_router, prefix="/api/channels", tags=["channels"]) app.include_router(messages_router, prefix="/api/channels", tags=["messages"]) app.include_router(dm_router, prefix="/api/dms", tags=["direct-messages"]) app.include_router(files_router, prefix="/api/files", tags=["files"]) # app.include_router(chat_router, prefix="/api/chat", tags=["chat"]) # Commented out - circular import @app.get("/") async def root(): """Health check endpoint""" return { "status": "online", "service": "Grimlock", "version": "0.3.0", "features": ["auth", "channels", "messages", "dms", "files", "websocket", "ai"] } @app.get("/api/health") async def health(): """Detailed health check""" return { "status": "healthy", "context_loaded": context_manager is not None and context_manager.is_loaded(), "ai_client_ready": ai_client is not None, "database": "connected", "websocket": "ready" } def get_context_manager() -> ContextManager: """Dependency to get context manager""" if context_manager is None: raise HTTPException(status_code=500, detail="Context manager not initialized") return context_manager def get_ai_client() -> AIClient: """Dependency to get AI client""" if ai_client is None: raise HTTPException(status_code=500, detail="AI client not initialized") return ai_client