Spaces:
Running
Running
File size: 7,490 Bytes
c2ea5ed 1670330 c2ea5ed 188f8a3 c2ea5ed c8243d5 188f8a3 c8243d5 7da14b7 97068b2 c8243d5 c2ea5ed c8243d5 795b72e c2ea5ed bcbd2ec c89d79e c8243d5 c89d79e bcbd2ec c89d79e bcbd2ec c8243d5 59e68c5 1c1facd fd80df3 59e68c5 1c1facd 59e68c5 66e834a 1c1facd 59e68c5 1c1facd c2ea5ed 59e68c5 d1a53e8 188f8a3 c2ea5ed 3b65325 c2ea5ed c8243d5 c2ea5ed 795b72e c2ea5ed 675e977 c8243d5 675e977 c8243d5 675e977 c2ea5ed eb7193f c2ea5ed eb7193f c2ea5ed |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
"""
FastAPI Application Definition
This module defines the FastAPI application and routes for the agent monitoring system.
"""
import logging
import os
import secrets
from pathlib import Path
import sys
from fastapi import FastAPI, Request, status, Depends, HTTPException
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.sessions import SessionMiddleware
from fastapi.responses import RedirectResponse, HTMLResponse, JSONResponse
from fastapi.exception_handlers import http_exception_handler
from backend.middleware.auth import ConditionalAuthMiddleware
from backend.middleware.usage_tracker import UsageTrackingMiddleware
from backend.dependencies import require_auth_in_hf_spaces
from utils.environment import should_enable_auth, debug_environment
# Add server module to path if not already there
server_dir = os.path.dirname(os.path.abspath(__file__))
if server_dir not in sys.path:
sys.path.append(server_dir)
# Import from backend modules
from backend.server_config import ensure_directories
from backend.routers import (
knowledge_graphs,
traces,
tasks,
temporal_graphs,
graph_comparison,
agentgraph,
example_traces,
methods,
observability,
auth,
testing,
)
# Setup logging
logger = logging.getLogger("agent_monitoring_server")
# Create FastAPI app
app = FastAPI(title="Agent Monitoring System", version="1.0.0")
# Define allowed CORS origins (security fix: restrict from wildcard)
ALLOWED_ORIGINS = [
"http://localhost:3001",
"http://localhost:5280",
"http://localhost:7860",
"https://holistic-ai-agentgraph.hf.space",
"https://huggingface.co",
]
# Add CORS middleware (first, so it's outermost)
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["Content-Type", "Authorization"],
)
# IMPORTANT: Middleware runs in REVERSE order of add_middleware() calls!
# We need Session to run BEFORE Auth so that request.session is available.
# Therefore: Add Auth FIRST, then Session SECOND.
# Detect if running in HF Spaces and adjust session config accordingly
from utils.environment import is_huggingface_space
# Debug environment detection
logger.info(f"π Environment detection: is_huggingface_space() = {is_huggingface_space()}")
logger.info(f"π SPACE_ID env var: {os.getenv('SPACE_ID')}")
# Add auth middleware FIRST (will run AFTER session middleware)
app.add_middleware(ConditionalAuthMiddleware)
# Add session middleware SECOND (will run BEFORE auth middleware, setting up request.session)
# HF Spaces specific session configuration
session_secret = os.getenv("SESSION_SECRET_KEY") or secrets.token_urlsafe(32)
if is_huggingface_space():
# HF Spaces optimized session configuration
logger.info("ποΈ Configuring session middleware for HF Spaces environment")
app.add_middleware(
SessionMiddleware,
secret_key=session_secret,
max_age=3600, # Shorter expiry for HF Spaces (1 hour)
same_site="none", # CRITICAL: Required for iframe cookies in HF Spaces
https_only=True, # HF Spaces uses HTTPS
)
else:
# Local development session configuration
logger.info("π Configuring session middleware for local development")
app.add_middleware(
SessionMiddleware,
secret_key=session_secret,
max_age=86400, # 24 hours for local dev
same_site="lax", # Better for OAuth redirects
https_only=False, # HTTP for local dev
)
# Add usage tracking middleware (last added = runs first, outermost)
app.add_middleware(UsageTrackingMiddleware)
# Custom exception handler for authentication redirects
@app.exception_handler(HTTPException)
async def custom_http_exception_handler(request: Request, exc: HTTPException):
"""
Custom exception handler that redirects web requests to login page
when they receive 401 errors with redirect_to field.
"""
# Check if this is a 401 with redirect_to field
if exc.status_code == 401 and isinstance(exc.detail, dict) and "redirect_to" in exc.detail:
# Check if this is not an API request
is_api_request = (
request.url.path.startswith("/api/") or
request.headers.get("accept", "").startswith("application/json") or
request.headers.get("content-type", "").startswith("application/json")
)
if not is_api_request:
# Redirect web requests to login page
logger.info(f"π Redirecting unauthenticated user from {request.url.path} to login page")
return RedirectResponse(url=exc.detail["redirect_to"], status_code=302)
# For all other cases, use default exception handler
return await http_exception_handler(request, exc)
# Mount datasets directory for accessing json files
app.mount("/data", StaticFiles(directory="datasets"), name="data")
# Mount static directory for papers and other static assets
app.mount("/static", StaticFiles(directory="backend/static"), name="static")
# Include routers
app.include_router(auth.router) # Add auth router first
app.include_router(traces.router)
app.include_router(knowledge_graphs.router)
app.include_router(agentgraph.router)
app.include_router(tasks.router)
app.include_router(temporal_graphs.router)
app.include_router(graph_comparison.router)
app.include_router(example_traces.router)
app.include_router(methods.router)
app.include_router(observability.router)
app.include_router(testing.router)
# Start background scheduler for automated tasks
# scheduler_service.start()
@app.on_event("startup")
async def startup_event():
"""Start background services on app startup"""
logger.info("β
Backend server starting...")
# π Debug environment information
debug_environment()
# π§ Create necessary directories
ensure_directories()
logger.info("π Directory structure created")
# ποΈ Initialize database on startup
try:
from backend.database.init_db import init_database
init_database(reset=False, force=False)
logger.info("ποΈ Database initialized successfully")
except Exception as e:
logger.error(f"β Database initialization failed: {e}")
# Don't fail startup - continue with empty database
# π Log authentication status
if should_enable_auth():
logger.info("π Authentication ENABLED (HF Spaces environment)")
else:
logger.info("π Authentication DISABLED (Local development)")
logger.info("π Backend API available at: http://0.0.0.0:7860")
# scheduler_service.start() # This line is now commented out
@app.on_event("shutdown")
async def shutdown_event():
"""Stop background services on app shutdown"""
logger.info("Stopping background services...")
# scheduler_service.stop() # This line is now commented out
# Root route - serve React app directly (authentication handled by frontend)
@app.get("/")
async def root(request: Request):
"""Serve the React app directly - authentication is now handled by frontend"""
return RedirectResponse(url="/agentgraph")
# Serve React app for any unmatched routes
@app.get("/app/{path:path}")
async def serve_react_app(path: str):
"""Serve the React app for client-side routing"""
return RedirectResponse(url="/agentgraph")
|