import os import sys from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, RedirectResponse from fastapi.staticfiles import StaticFiles from dotenv import load_dotenv, find_dotenv # Ensure project root on sys.path for package imports ROOT_DIR = Path(__file__).resolve().parent if str(ROOT_DIR) not in sys.path: sys.path.append(str(ROOT_DIR)) # Load environment (HF Spaces will inject secrets via env vars) load_dotenv(find_dotenv(), override=True) app = FastAPI(title="Flagship AI", version="0.1.0") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # --- Static assets (shared Suite styles/header) --- SUITE_STATIC_DIR = (ROOT_DIR / "Suite" / "app" / "static").resolve() app.mount("/static", StaticFiles(directory=str(SUITE_STATIC_DIR)), name="static") # --- Include routers from agents that already use prefixed routes --- # UX Prototyper uses router prefix '/ux' try: from AIDesign.app.main import router as ux_router # type: ignore app.include_router(ux_router) except Exception: # Optional: keep the app booting even if module missing pass # Slack Knowledge Bot uses router prefix '/slack' try: from AIIntegration.app.main import router as slack_router # type: ignore app.include_router(slack_router) except Exception: pass # AI Readiness Audit uses router prefix '/audit' try: print("Attempting to import AIReadiness...") from AIReadiness.app.main import router as audit_router # type: ignore print(f"Successfully imported AIReadiness router: {audit_router}") print(f"Router prefix: {audit_router.prefix}") app.include_router(audit_router) print("Successfully included AIReadiness router") except Exception as e: print(f"Failed to import AIReadiness router: {e}") import traceback traceback.print_exc() pass # Additional prefixed routers for mod_path, name in ( ("AIAccessibility.app.main", "access_router"), ("AIAutomation.app.main", "automation_router"), ("AIStrategy.app.main", "strategy_router"), ("AIFit.app.main", "fit_router"), ("AIDual.app.main", "dual_router"), ("AIQuiz.app.main", "quiz_router"), ("AIRoute.app.main", "route_router"), ("AISaas.app.main", "saas_router"), ): try: print(f"Importing {mod_path}...") module = __import__(mod_path, fromlist=["router"]) # type: ignore router = getattr(module, "router") print(f"Successfully imported {name}: {router}") print(f"Router routes: {[route.path for route in router.routes]}") app.include_router(router) # type: ignore print(f"Successfully included {name}") except Exception as e: print(f"Failed to import {mod_path}: {e}") import traceback traceback.print_exc() pass # --- Mount full apps where clean scoping is needed --- # Opportunity Planner (AISolutions) has unprefixed routes; mount under '/solutions' try: from AISolutions.app.main import app as solutions_app # type: ignore app.mount("/solutions", solutions_app) except Exception: pass # Include routers with proper prefixes try: from AIArchitecture.app.main import router as arch_router # type: ignore app.include_router(arch_router) except Exception: pass try: from AICommerce.app.main import router as commerce_router # type: ignore app.include_router(commerce_router) except Exception: pass # AIAutomation is now included in the loop above try: from AIHealthcare.app.main import router as healthcare_router # type: ignore app.include_router(healthcare_router) except Exception: pass # These routers are already included by the loop above # --- Index routes for top-level and agent landings --- @app.get("/") def suite_index() -> FileResponse: """Serve the Flagship Suite landing page.""" return FileResponse((SUITE_STATIC_DIR / "index.html").as_posix()) @app.get("/ux") def ux_index() -> FileResponse: base = ROOT_DIR / "AIDesign" / "app" / "static" / "index.html" return FileResponse(base.as_posix()) @app.get("/slack") def slack_index() -> FileResponse: base = ROOT_DIR / "AIIntegration" / "app" / "static" / "index.html" return FileResponse(base.as_posix()) # Audit route is now handled by the AIReadiness router # Static index shortcuts for mounted apps and prefixed routers @app.get("/solutions/") def solutions_index_redirect() -> RedirectResponse: # Mounted app handles this; ensure trailing slash variant resolves return RedirectResponse(url="/solutions/") # These routes are now handled by their respective routers @app.get("/triage") def triage_index() -> FileResponse: base = ROOT_DIR / "AIHealthcare" / "app" / "static" / "index.html" return FileResponse(base.as_posix()) # Invoice route is now handled by the AIAutomation router # These routes are now handled by their respective routers # Optional health endpoint for the aggregator @app.get("/healthz") def healthz() -> dict: return { "status": "ok", "openai_key": bool(os.getenv("OPENAI_API_KEY")), "version": "0.1.0", }