"""FastAPI application factory for Spore Agent Commons. This mounts our custom routers onto the koi-net node's FastAPI app. If running standalone (without koi-net), creates its own app. """ import logging from pathlib import Path from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from spore_node.config import SporeConfig from spore_node.db.connection import init_pool, close_pool from spore_node.api.routers import health, holons, governance, claims, intents, commitments, federation log = logging.getLogger(__name__) def mount_routers(app: FastAPI) -> None: """Mount Spore-specific routers onto a FastAPI app.""" app.include_router(health.router) app.include_router(holons.router) app.include_router(governance.router) app.include_router(claims.router) app.include_router(intents.router) app.include_router(commitments.router) app.include_router(federation.router) # Graph visualization static_dir = Path(__file__).parent.parent / "static" if static_dir.exists(): @app.get("/graph") async def graph_page(): return FileResponse(static_dir / "graph.html") app.mount("/static", StaticFiles(directory=str(static_dir)), name="static") def create_standalone_app(cfg: SporeConfig | None = None) -> FastAPI: """Create a standalone FastAPI app (without koi-net node). Used for development/testing or when koi-net is optional. """ if cfg is None: cfg = SporeConfig() @asynccontextmanager async def lifespan(app: FastAPI): log.info("Starting Spore Commons API...") await init_pool(cfg.database_url) yield await close_pool() log.info("Spore Commons API stopped.") app = FastAPI( title="Spore Agent Commons", version="0.1.0", description="Agent Commons — governance memory, knowledge graph, and federation layer", lifespan=lifespan, ) mount_routers(app) return app