Stage 0: Repository und Architekturgrundlage
- Pyproject.toml mit FastAPI, Pydantic v2, SQLAlchemy, httpx, asyncio, BeautifulSoup4, selectolax, trafilatura, uvicorn, pytest-asyncio - Multi-stage Dockerfile (Python 3.12-slim, Non-Root-User nsct) - docker-compose.yml (nsct-api + postgres + optional searxng) - .env.example mit allen Config-Parametern - Config-System: AppSettings mit LLMConfig, VisionConfig, AudioConfig, DatabaseConfig — komplett aus Environment, keine Hardcodes - Strukturiertes Logging mit research_id/llm_request_id Tracking - Pydantic v2 Schemas: SearchQuery, Source, Claim, EvidenceRelation, CitationEdge, ResearchReport - SQLAlchemy 2.0 Declarative Models + async Engine Factory - SSRF-Schutz: URL-Validation, IP-Blocklist (RFC1918, Cloud Metadata, file://, ftp://) - Provider-Interfaces: LLMProvider, VisionProvider, AudioProvider, SearchProvider, ContentFetcher als ABCs - Health-Endpoints: /health, /ready (LLM-Connect-Test), /providers - FastAPI App mit CORS, lifespan (LLM Pre-Flight) - CLI-Stub mit Entry-Points: nsct, nsct-core, nsct-api - 6 Test-Cases: /health, /ready, /providers + No-Secrets-Test - Vollständige Dokumentation: README, ARCHITECTURE, SECURITY, METHODOLOGY, API, DEPLOYMENT - .gitignore (Python, Docker, IDE, .env)
This commit is contained in:
81
src/nsct/api/main.py
Normal file
81
src/nsct/api/main.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""FastAPI application — entry point for the API server."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
import httpx
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from nsct.config import AppSettings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Lifespan
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Application lifespan — startup and shutdown hooks."""
|
||||
config: AppSettings = app.state.config
|
||||
|
||||
logger.info("NSCT API starting up — version %s", app.state.version)
|
||||
|
||||
# Pre-flight: validate LLM connectivity
|
||||
llm_base = config.llm.base_url if config and config.llm and config.llm.base_url else ""
|
||||
if llm_base:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=5.0) as client:
|
||||
resp = await client.get(f"{llm_base}/models")
|
||||
if resp.status_code == 200:
|
||||
logger.info("LLM provider reachable — model endpoint returned 200")
|
||||
else:
|
||||
logger.warning("LLM provider returned %s", resp.status_code)
|
||||
except Exception as exc:
|
||||
logger.warning("LLM connectivity check failed: %s", exc)
|
||||
|
||||
yield
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# App factory
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
"""Create the FastAPI application with all routes and middleware."""
|
||||
config = AppSettings.from_env()
|
||||
|
||||
app = FastAPI(
|
||||
title="NSCT API",
|
||||
description="Neutral Search Crawler Tool — API server",
|
||||
version="0.1.0",
|
||||
docs_url="/docs" if config.debug else None,
|
||||
redoc_url="/redoc" if config.debug else None,
|
||||
lifespan=lifespan,
|
||||
)
|
||||
app.state.config = config
|
||||
app.state.version = "0.1.0"
|
||||
|
||||
# CORS — allow local development
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["http://localhost:3000", "http://localhost:5173", "http://127.0.0.1:3000"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Mount health router
|
||||
from nsct.api.health import router as health_router
|
||||
app.include_router(health_router, tags=["system"])
|
||||
|
||||
return app
|
||||
|
||||
|
||||
app = create_app()
|
||||
Reference in New Issue
Block a user