service-llm-private/app.py
ValueOn AG b15d283941
All checks were successful
Deploy LLM Service / deploy (push) Successful in 23s
cp adapted to 2026 poweron
2026-06-09 09:54:11 +02:00

129 lines
3.7 KiB
Python

# Copyright (c) 2026 PowerOn AG
# 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)