# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Private-LLM Service - FastAPI Web App Provides AI model endpoints for OCR and Vision processing via Ollama. Models exposed: - poweron-ocr-general (deepseek) - poweron-vision-general (qwen2.5) - poweron-vision-deep (granite3.2) """ import logging from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from config import CONFIG logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) logger = logging.getLogger(__name__) # ============================================================================ # Application Lifecycle # ============================================================================ @asynccontextmanager async def lifespan(app: FastAPI): """Application lifespan handler.""" logger.info("Private-LLM Service starting up...") logger.info(f"Ollama URL: {CONFIG['ollamaUrl']}") logger.info(f"API Key configured: {'Yes' if CONFIG['apiKey'] else 'No (development mode)'}") from config import PDF_SUPPORT logger.info(f"PDF Support: {'Enabled' if PDF_SUPPORT else 'Disabled'}") yield logger.info("Private-LLM Service shutting down...") # ============================================================================ # FastAPI Application # ============================================================================ app = FastAPI( title="PowerOn Private-LLM Service", description="AI model endpoints for OCR and Vision processing", version="1.0.0", lifespan=lifespan, ) # CORS Configuration ALLOWED_ORIGINS = [ "http://localhost:8000", "http://localhost:8080", "http://localhost:5000", "http://127.0.0.1:8000", "http://127.0.0.1:8080", "http://127.0.0.1:5000", ] PRODUCTION_PATTERNS = [ "poweron.swiss", "poweron-center.net", ] for pattern in PRODUCTION_PATTERNS: ALLOWED_ORIGINS.extend([ f"https://{pattern}", f"https://www.{pattern}", f"https://api.{pattern}", f"https://gateway.{pattern}", f"https://app.{pattern}", f"https://nyla.{pattern}", f"https://playground.{pattern}", ]) app.add_middleware( CORSMiddleware, allow_origins=ALLOWED_ORIGINS, allow_origin_regex=r"https://.*\.(poweron\.swiss|poweron-center\.net)", allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], allow_headers=["*"], expose_headers=["*"], max_age=86400, ) # Static files (for web UI) app.mount("/static", StaticFiles(directory="static"), name="static") # ============================================================================ # Route Registration # ============================================================================ from routeApi import router as apiRouter app.include_router(apiRouter) from routeOpenAi import router as openAiRouter app.include_router(openAiRouter) from routeWeb import router as webRouter app.include_router(webRouter) # ============================================================================ # Main # ============================================================================ if __name__ == "__main__": import uvicorn print("\n" + "=" * 60) print(" Private-LLM Service - KI-Dokumentenanalyse") print(" Powered by PowerOn") print("=" * 60) print(f"\n Server läuft auf: http://localhost:5000") print(f" API Docs: http://localhost:5000/docs") print(f" Ollama URL: {CONFIG['ollamaUrl']}") print("\n Drücke Ctrl+C zum Beenden") print("=" * 60 + "\n") uvicorn.run(app, host="0.0.0.0", port=5000)