Pix-Agent / app.py
ManTea's picture
QA version persionality
c8b8c9b
from fastapi import FastAPI, Depends, Request, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import uvicorn
import os
import sys
import logging
from dotenv import load_dotenv
from fastapi.responses import JSONResponse, PlainTextResponse
from fastapi.staticfiles import StaticFiles
import time
import uuid
import traceback
# Cấu hình logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout),
]
)
logger = logging.getLogger(__name__)
# Load environment variables
load_dotenv()
DEBUG = os.getenv("DEBUG", "False").lower() in ("true", "1", "t")
# Kiểm tra các biến môi trường bắt buộc
required_env_vars = [
"AIVEN_DB_URL",
"MONGODB_URL",
"PINECONE_API_KEY",
"PINECONE_INDEX_NAME",
"GOOGLE_API_KEY"
]
missing_vars = [var for var in required_env_vars if not os.getenv(var)]
if missing_vars:
logger.error(f"Missing required environment variables: {', '.join(missing_vars)}")
if not DEBUG: # Chỉ thoát nếu không ở chế độ debug
sys.exit(1)
# Database health checks
def check_database_connections():
"""Kiểm tra kết nối các database khi khởi động"""
from app.database.postgresql import check_db_connection as check_postgresql
from app.database.mongodb import check_db_connection as check_mongodb
from app.database.pinecone import check_db_connection as check_pinecone
db_status = {
"postgresql": check_postgresql(),
"mongodb": check_mongodb(),
"pinecone": check_pinecone()
}
all_ok = all(db_status.values())
if not all_ok:
failed_dbs = [name for name, status in db_status.items() if not status]
logger.error(f"Failed to connect to databases: {', '.join(failed_dbs)}")
if not DEBUG: # Chỉ thoát nếu không ở chế độ debug
sys.exit(1)
return db_status
# Khởi tạo lifespan để kiểm tra kết nối database khi khởi động
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup: kiểm tra kết nối các database
logger.info("Starting application...")
db_status = check_database_connections()
# Khởi tạo bảng trong cơ sở dữ liệu (nếu chưa tồn tại)
if DEBUG and all(db_status.values()): # Chỉ khởi tạo bảng trong chế độ debug và khi tất cả kết nối DB thành công
from app.database.postgresql import create_tables
if create_tables():
logger.info("Database tables created or already exist")
yield
# Shutdown
logger.info("Shutting down application...")
# Import routers
try:
from app.api.mongodb_routes import router as mongodb_router
from app.api.postgresql_routes import router as postgresql_router
from app.api.rag_routes import router as rag_router
from app.api.websocket_routes import router as websocket_router
from app.api.pdf_routes import router as pdf_router
from app.api.pdf_websocket import router as pdf_websocket_router
# Import middlewares
from app.utils.middleware import RequestLoggingMiddleware, ErrorHandlingMiddleware, DatabaseCheckMiddleware
# Import debug utilities
from app.utils.debug_utils import debug_view, DebugInfo, error_tracker, performance_monitor
# Import cache
from app.utils.cache import get_cache
logger.info("Successfully imported all routers and modules")
except ImportError as e:
logger.error(f"Error importing routes or middlewares: {e}")
raise
# Create FastAPI app
app = FastAPI(
title="PIX Project Backend API",
description="Backend API for PIX Project with MongoDB, PostgreSQL and RAG integration",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc",
debug=DEBUG,
lifespan=lifespan,
)
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Thêm middlewares
app.add_middleware(ErrorHandlingMiddleware)
app.add_middleware(RequestLoggingMiddleware)
if not DEBUG: # Chỉ thêm middleware kiểm tra database trong production
app.add_middleware(DatabaseCheckMiddleware)
# Include routers
app.include_router(mongodb_router)
app.include_router(postgresql_router)
app.include_router(rag_router)
app.include_router(websocket_router)
app.include_router(pdf_router)
app.include_router(pdf_websocket_router)
# Log all registered routes
logger.info("Registered API routes:")
for route in app.routes:
if hasattr(route, "path") and hasattr(route, "methods"):
methods = ",".join(route.methods)
logger.info(f" {methods:<10} {route.path}")
# Root endpoint
@app.get("/")
def read_root():
return {
"message": "Welcome to PIX Project Backend API",
"documentation": "/docs",
}
# Health check endpoint
@app.get("/health")
def health_check():
# Kiểm tra kết nối database
db_status = check_database_connections()
all_db_ok = all(db_status.values())
return {
"status": "healthy" if all_db_ok else "degraded",
"version": "1.0.0",
"environment": os.environ.get("ENVIRONMENT", "production"),
"databases": db_status
}
@app.get("/api/ping")
async def ping():
return {"status": "pong"}
# Cache stats endpoint
@app.get("/cache/stats")
def cache_stats():
"""Trả về thống kê về cache"""
cache = get_cache()
return cache.stats()
# Cache clear endpoint
@app.delete("/cache/clear")
def cache_clear():
"""Xóa tất cả dữ liệu trong cache"""
cache = get_cache()
cache.clear()
return {"message": "Cache cleared successfully"}
# Debug endpoints (chỉ có trong chế độ debug)
if DEBUG:
@app.get("/debug/config")
def debug_config():
"""Hiển thị thông tin cấu hình (chỉ trong chế độ debug)"""
config = {
"environment": os.environ.get("ENVIRONMENT", "production"),
"debug": DEBUG,
"db_connection_mode": os.environ.get("DB_CONNECTION_MODE", "aiven"),
"databases": {
"postgresql": os.environ.get("AIVEN_DB_URL", "").split("@")[1].split("/")[0] if "@" in os.environ.get("AIVEN_DB_URL", "") else "N/A",
"mongodb": os.environ.get("MONGODB_URL", "").split("@")[1].split("/?")[0] if "@" in os.environ.get("MONGODB_URL", "") else "N/A",
"pinecone": os.environ.get("PINECONE_INDEX_NAME", "N/A"),
}
}
return config
@app.get("/debug/system")
def debug_system():
"""Hiển thị thông tin hệ thống (chỉ trong chế độ debug)"""
return DebugInfo.get_system_info()
@app.get("/debug/database")
def debug_database():
"""Hiển thị trạng thái database (chỉ trong chế độ debug)"""
return DebugInfo.get_database_status()
@app.get("/debug/errors")
def debug_errors(limit: int = 10):
"""Hiển thị các lỗi gần đây (chỉ trong chế độ debug)"""
return error_tracker.get_errors(limit=limit)
@app.get("/debug/performance")
def debug_performance():
"""Hiển thị thông tin hiệu suất (chỉ trong chế độ debug)"""
return performance_monitor.get_report()
@app.get("/debug/full")
def debug_full_report(request: Request):
"""Hiển thị báo cáo debug đầy đủ (chỉ trong chế độ debug)"""
return debug_view(request)
@app.get("/debug/cache")
def debug_cache():
"""Hiển thị thông tin chi tiết về cache (chỉ trong chế độ debug)"""
cache = get_cache()
cache_stats = cache.stats()
# Thêm thông tin chi tiết về các key trong cache
cache_keys = list(cache.cache.keys())
history_users = list(cache.user_history_queues.keys())
return {
"stats": cache_stats,
"keys": cache_keys,
"history_users": history_users,
"config": {
"ttl": cache.ttl,
"cleanup_interval": cache.cleanup_interval,
"max_size": cache.max_size,
"history_queue_size": os.getenv("HISTORY_QUEUE_SIZE", "10"),
"history_cache_ttl": os.getenv("HISTORY_CACHE_TTL", "3600"),
}
}
@app.get("/debug/websocket-routes")
def debug_websocket_routes():
"""Hiển thị thông tin về các WebSocket route (chỉ trong chế độ debug)"""
ws_routes = []
for route in app.routes:
if "websocket" in str(route.__class__).lower():
ws_routes.append({
"path": route.path,
"name": route.name,
"endpoint": str(route.endpoint)
})
return {
"websocket_routes": ws_routes,
"total_count": len(ws_routes)
}
@app.get("/debug/mock-status")
def debug_mock_status():
"""Display current mock mode settings"""
# Import was: from app.api.pdf_routes import USE_MOCK_MODE
# We've disabled mock mode
return {
"mock_mode": False, # Disabled - using real database
"mock_env_variable": os.getenv("USE_MOCK_MODE", "false"),
"debug_mode": DEBUG
}
# Run the app with uvicorn when executed directly
if __name__ == "__main__":
port = int(os.environ.get("PORT", 7860))
uvicorn.run("app:app", host="0.0.0.0", port=port, reload=DEBUG)