88a1dab810
---ci--- phase: 7 milestone: v0.2 status: complete requirements: covered: [REQ-2-001, REQ-2-002, REQ-2-003, REQ-2-004, REQ-2-005, REQ-2-006, REQ-2-007, REQ-2-008, REQ-2-009, REQ-2-010, REQ-2-011, REQ-2-012] partial: [] ---/ci--- Milestone v0.2 (ai-tutor-architecture) merged to main. Escalation record (audit remediation, durable): P1 executor delegation failed twice (empty subagent results, zero files created); auto-resolved at full autonomy to inline execution with identical plan fidelity (commit 3271373, reflog-only after phase branch squash-delete).
66 lines
2.0 KiB
Python
66 lines
2.0 KiB
Python
"""FastAPI app factory — lifespan, CORS, health, routers."""
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
import httpx
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
from .agents.registry import AgentRegistry, register_builtin_agents
|
|
from .agents.session import InMemorySessionStore
|
|
from .api import (
|
|
assessment_router,
|
|
chat_router,
|
|
lab_router,
|
|
mentor_router,
|
|
proctor_router,
|
|
)
|
|
from .config import Settings
|
|
from .llm import create_provider
|
|
|
|
|
|
def create_app(settings: Settings | None = None) -> FastAPI:
|
|
settings = settings or Settings()
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
# Shared HTTP client pool (D-017): 10s connect / 300s read for cloud TTFT
|
|
timeout = httpx.Timeout(connect=10.0, read=300.0, write=30.0, pool=10.0)
|
|
app.state.http_client = httpx.AsyncClient(timeout=timeout)
|
|
app.state.settings = settings
|
|
app.state.provider = create_provider(settings, app.state.http_client)
|
|
app.state.session_store = InMemorySessionStore()
|
|
app.state.agent_registry = AgentRegistry()
|
|
register_builtin_agents(app.state.agent_registry)
|
|
yield
|
|
await app.state.http_client.aclose()
|
|
|
|
app = FastAPI(title="Nextcraft AI Service", version="0.2.0", lifespan=lifespan)
|
|
|
|
# A-008: localhost-only CORS, no credentials
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["http://localhost:3000", "http://127.0.0.1:3000"],
|
|
allow_methods=["GET", "POST", "OPTIONS"],
|
|
allow_headers=["Content-Type"],
|
|
allow_credentials=False,
|
|
)
|
|
|
|
@app.get("/health")
|
|
async def health() -> dict:
|
|
return {
|
|
"status": "ok",
|
|
"provider": settings.provider,
|
|
"model": settings.model,
|
|
}
|
|
|
|
app.include_router(chat_router)
|
|
app.include_router(lab_router)
|
|
app.include_router(assessment_router)
|
|
app.include_router(mentor_router)
|
|
app.include_router(proctor_router)
|
|
return app
|
|
|
|
|
|
app = create_app()
|