Files
pablan/backend/app/main.py
T
ProfessorNovaandClaude Opus 5 784b76baf7 Pablan, as it stands
Self-hosted knowledge management for SMEs: a split-screen Markdown editor
whose sections an LLM refines while you write, and RAG question answering
over the documents that result. FastAPI + Postgres/pgvector on the back,
SvelteKit on the front, everything OpenAI-compatible and self-hostable.

Squashed into a single commit; the development history stays local.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CA43ZJda8Rbp2hKXNy8f6b
2026-09-04 09:21:37 +02:00

60 lines
1.9 KiB
Python

import asyncio
import uuid
from collections.abc import AsyncIterator, Awaitable, Callable
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, Response
from app.api import api_router
from app.db import async_session_factory
from app.errors import register_exception_handlers
from app.help_import import import_help_documents
from app.ingestion.handlers import ensure_retention_scheduled
from app.ingestion.queue import run_queue
from app.llm.overrides import bootstrap_llm_settings
from app.llm.overrides import load_config as load_llm_config
from app.log import correlation_id, setup_logging
from app.prompts.overrides import load_config as load_prompt_config
from app.template_catalog import seed_starter_templates
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
setup_logging()
async with async_session_factory() as db:
await ensure_retention_scheduled(db)
await seed_starter_templates(db)
await import_help_documents(db)
await bootstrap_llm_settings(db)
await load_llm_config(db)
await load_prompt_config(db)
stop_event = asyncio.Event()
queue_task = asyncio.create_task(run_queue(stop_event))
yield
stop_event.set()
try:
await asyncio.wait_for(queue_task, timeout=10)
except TimeoutError: # pragma: no cover — a handler refused to finish
queue_task.cancel()
app = FastAPI(title="Pablan", version="0.1.0", lifespan=lifespan)
register_exception_handlers(app)
@app.middleware("http")
async def add_correlation_id(
request: Request, call_next: Callable[[Request], Awaitable[Response]]
) -> Response:
cid = request.headers.get("x-request-id") or uuid.uuid4().hex[:16]
token = correlation_id.set(cid)
try:
response = await call_next(request)
finally:
correlation_id.reset(token)
response.headers["x-request-id"] = cid
return response
app.include_router(api_router)