Spaces:
Sleeping
Sleeping
File size: 5,409 Bytes
ed33951 159f5a5 16b7df8 159f5a5 9dfccd9 159f5a5 16b7df8 6a4e71a 159f5a5 dc7da82 7eb57e9 dc7da82 9dfccd9 451d52a 9dfccd9 e8b3591 9dfccd9 159f5a5 16b7df8 159f5a5 16b7df8 159f5a5 9dfccd9 451d52a 9dfccd9 e8b3591 9dfccd9 159f5a5 16b7df8 6a4e71a dc7da82 7eb57e9 9dfccd9 68af3c5 9dfccd9 e646563 9dfccd9 e646563 9dfccd9 68af3c5 | 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 | from dotenv import load_dotenv
load_dotenv()
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from agent.api import router as agent_router
from graph_store.api import router as graph_router
from graph_store.stream import router as graph_stream_router
from ingestion.api import router as ingestion_router
from src.confluence_agent.router import router as confluence_router
from toolsforgitnotionslack.router import router as tools_router
from src.file_agent.router import router as file_router
from src.jira_agent.router import router as jira_router
from src.utils.middleware import RequestLoggingMiddleware
from src.auth.router import router as auth_router
from src.analytics.router import router as analytics_router
from src.anomaly.router import router as anomaly_router
from src.admin.router import router as admin_router
from src.admin.users_api import router as admin_users_router
from src.admin.users_api import audit_router as admin_audit_router
from src.workspace.router import router as workspace_router
from src.ws.router import router as ws_router
# src.utils.logger configures the root JSON handler on import
@asynccontextmanager
async def lifespan(app: FastAPI):
yield
from graph_store.writer import close_driver
await close_driver()
app = FastAPI(
title="Enterprise Knowledge Copilot",
version="0.1.0",
lifespan=lifespan,
)
# ---------------------------------------------------------------------------
# CORS β allow the Vite dev server and any configured origins
# ---------------------------------------------------------------------------
from src.config import settings as _settings
_cors_origins = [o.strip() for o in _settings.cors_origins.split(",") if o.strip()]
app.add_middleware(
CORSMiddleware,
allow_origins=_cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.add_middleware(RequestLoggingMiddleware)
# ---------------------------------------------------------------------------
# Routers
# ---------------------------------------------------------------------------
app.include_router(auth_router)
app.include_router(analytics_router)
app.include_router(anomaly_router)
app.include_router(admin_router)
app.include_router(admin_users_router)
app.include_router(admin_audit_router)
app.include_router(workspace_router)
app.include_router(ws_router)
app.include_router(agent_router)
app.include_router(ingestion_router)
app.include_router(graph_router)
app.include_router(graph_stream_router)
app.include_router(jira_router)
app.include_router(confluence_router)
app.include_router(file_router)
app.include_router(tools_router)
# ---------------------------------------------------------------------------
# Health endpoint β must be registered BEFORE the SPA catch-all
# ---------------------------------------------------------------------------
@app.get("/health", tags=["infra"])
async def health() -> dict:
import asyncio
results: dict = {"status": "ok", "neo4j": "unknown", "redis": "unknown", "qdrant": "unknown"}
# Neo4j β reuse module-level singleton; do not close it here
try:
from graph_store.writer import get_driver
driver = get_driver()
await asyncio.wait_for(driver.verify_connectivity(), timeout=3)
results["neo4j"] = "ok"
except Exception as exc:
results["neo4j"] = f"error: {exc}"
results["status"] = "degraded"
# Redis
try:
import redis.asyncio as aioredis
r = aioredis.from_url(_settings.redis_url, socket_connect_timeout=3)
await r.ping()
await r.aclose()
results["redis"] = "ok"
except Exception as exc:
results["redis"] = f"error: {exc}"
results["status"] = "degraded"
# Qdrant β supports local (host+port) and hosted (url+api_key)
try:
from qdrant_client import AsyncQdrantClient
if _settings.qdrant_url:
qc = AsyncQdrantClient(
url=_settings.qdrant_url,
api_key=_settings.qdrant_api_key or None,
timeout=3,
)
else:
qc = AsyncQdrantClient(
host=_settings.qdrant_host,
port=_settings.qdrant_port,
timeout=3,
)
await qc.get_collections()
await qc.close()
results["qdrant"] = "ok"
except Exception as exc:
results["qdrant"] = f"error: {exc}"
results["status"] = "degraded"
return results
# ---------------------------------------------------------------------------
# Serve React build β SPA catch-all MUST be last (catches everything else)
# ---------------------------------------------------------------------------
import os as _os
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse as _FileResponse
_dist = _os.path.join(_os.path.dirname(__file__), "frontend", "dist")
if _os.path.exists(_dist):
app.mount("/assets", StaticFiles(directory=_os.path.join(_dist, "assets")), name="assets")
@app.get("/{full_path:path}", include_in_schema=False)
async def serve_spa(full_path: str):
file = _os.path.join(_dist, full_path)
if _os.path.isfile(file):
return _FileResponse(file)
return _FileResponse(_os.path.join(_dist, "index.html"))
|