FinDataPilot / app /main.py
Fin-DataPilot Deploy Bot
ci: 102c90e
e051bfe
Raw
History Blame Contribute Delete
3.02 kB
"""Fin-DataPilot FastAPI application entrypoint.
Triggered by the user adding HF_SSH_PRIVATE_KEY to GitHub Secrets.
"""
from __future__ import annotations
import logging
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import ORJSONResponse
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware
from slowapi.util import get_remote_address
from app import __version__
from app.api import agent, auth, health, sessions, skills
from app.config import get_settings
from app.db_init import init_db
from app.skills import registry as _skills_registry # noqa: F401 — trigger registration
from app.skills.user_uploads import load_uploaded_skills_at_startup
from app.utils.trace import setup_logging
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
settings = get_settings()
setup_logging(settings.log_level)
await init_db()
# After the 4 built-in skills have registered, re-import any
# previously uploaded skills so they survive container restarts.
n_loaded = load_uploaded_skills_at_startup()
if n_loaded:
logger.info(
"Loaded %d uploaded skill(s) from %s",
n_loaded,
settings.user_skills_dir,
)
yield
def create_app() -> FastAPI:
settings = get_settings()
app = FastAPI(
title="Fin-DataPilot",
version=__version__,
description="Natural-language financial data agent platform",
default_response_class=ORJSONResponse,
lifespan=lifespan,
)
# Fail loudly before serving any request with an unsigned identity in
# production. Accessing the property performs the environment check.
_ = settings.effective_auth_secret
# CORS: credentials are not used (the API uses bearer headers), so don't
# opt browsers into cross-origin cookie semantics.
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins_list,
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["X-Trace-Id"],
)
# Rate limiting (per remote IP)
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) # type: ignore[arg-type]
app.add_middleware(SlowAPIMiddleware)
# Routers
app.include_router(health.router, prefix="/api", tags=["health"])
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
app.include_router(skills.router, prefix="/api", tags=["skills"])
app.include_router(sessions.router, prefix="/api", tags=["sessions"])
app.include_router(agent.router, prefix="/api/agent", tags=["agent"])
return app
app = create_app()