221 lines
7.0 KiB
Python

from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.responses import JSONResponse
import time
import logging
from contextlib import asynccontextmanager
from app.core.config import settings
from app.db.database import engine
from app.db.models import Base
# Import routers
from app.api.auth import router as auth_router
# from app.api.files import router as files_router
# from app.api.chat import router as chat_router
# from app.api.reminders import router as reminders_router
# from app.api.search import router as search_router
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Lifespan context manager for startup and shutdown events"""
# Startup
logger.info("Starting aPersona backend...")
# Create database tables
Base.metadata.create_all(bind=engine)
logger.info("Database tables created")
# Initialize AI components
try:
from ai_core.embeddings.embedding_service import embedding_service
from ai_core.rag.vector_store import vector_store
from ai_core.llm.ollama_client import ollama_client
# Test Ollama connection
is_healthy = await ollama_client.check_health()
if is_healthy:
logger.info("Ollama connection established")
else:
logger.warning("Ollama service not available - some features may be limited")
# Initialize vector store
stats = vector_store.get_collection_stats()
logger.info(f"Vector store initialized: {stats}")
# Test embedding service
embedding_info = embedding_service.get_model_info()
logger.info(f"Embedding service ready: {embedding_info}")
except Exception as e:
logger.error(f"Failed to initialize AI components: {e}")
yield
# Shutdown
logger.info("Shutting down aPersona backend...")
try:
await ollama_client.close()
except:
pass
# Create FastAPI app
app = FastAPI(
title=settings.APP_NAME,
version=settings.APP_VERSION,
description="AI-powered personal assistant that works completely offline",
lifespan=lifespan
)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=settings.BACKEND_CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Add trusted host middleware for security
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["localhost", "127.0.0.1", "*.localhost"]
)
# Request timing middleware
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
"""Add processing time to response headers"""
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
# Global exception handler
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""Global exception handler for unhandled errors"""
logger.error(f"Unhandled error for {request.url}: {exc}")
return JSONResponse(
status_code=500,
content={
"detail": "Internal server error",
"error": str(exc) if settings.DEBUG else "An unexpected error occurred"
}
)
# Health check endpoint
@app.get("/health")
async def health_check():
"""Health check endpoint"""
try:
from ai_core.llm.ollama_client import ollama_client
ollama_healthy = await ollama_client.check_health()
return {
"status": "healthy",
"app_name": settings.APP_NAME,
"version": settings.APP_VERSION,
"services": {
"database": "healthy",
"ollama": "healthy" if ollama_healthy else "unhealthy",
"embeddings": "healthy",
"vector_store": "healthy"
}
}
except Exception as e:
logger.error(f"Health check failed: {e}")
return JSONResponse(
status_code=503,
content={
"status": "unhealthy",
"error": str(e)
}
)
# Root endpoint
@app.get("/")
async def root():
"""Root endpoint"""
return {
"message": f"Welcome to {settings.APP_NAME}",
"version": settings.APP_VERSION,
"description": "AI-powered personal assistant - fully local and private",
"endpoints": {
"health": "/health",
"docs": "/docs",
"api": settings.API_V1_STR
}
}
# System info endpoint
@app.get(f"{settings.API_V1_STR}/system/info")
async def get_system_info():
"""Get system information and capabilities"""
try:
from ai_core.embeddings.embedding_service import embedding_service
from ai_core.rag.vector_store import vector_store
from ai_core.llm.ollama_client import ollama_client
# Get AI service information
embedding_info = embedding_service.get_model_info()
vector_stats = vector_store.get_collection_stats()
available_models = await ollama_client.list_models()
return {
"app_info": {
"name": settings.APP_NAME,
"version": settings.APP_VERSION,
"debug": settings.DEBUG
},
"ai_services": {
"embedding_model": embedding_info,
"vector_store": vector_stats,
"available_llm_models": available_models,
"current_llm_model": settings.DEFAULT_LLM_MODEL
},
"capabilities": {
"file_processing": [
"PDF", "DOCX", "TXT", "Images (OCR)",
"Markdown", "PNG", "JPEG", "GIF"
],
"ai_features": [
"Semantic search", "Auto-categorization",
"Smart reminders", "Personalized responses",
"Learning from interactions"
]
}
}
except Exception as e:
logger.error(f"Failed to get system info: {e}")
raise HTTPException(status_code=500, detail="Failed to retrieve system information")
# Include API routers
app.include_router(auth_router, prefix=f"{settings.API_V1_STR}/auth", tags=["authentication"])
# app.include_router(files_router, prefix=f"{settings.API_V1_STR}/files", tags=["files"])
# app.include_router(chat_router, prefix=f"{settings.API_V1_STR}/chat", tags=["chat"])
# app.include_router(reminders_router, prefix=f"{settings.API_V1_STR}/reminders", tags=["reminders"])
# app.include_router(search_router, prefix=f"{settings.API_V1_STR}/search", tags=["search"])
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app.main:app",
host="0.0.0.0",
port=8000,
reload=settings.DEBUG,
log_level="info" if not settings.DEBUG else "debug"
)