FastAPI & Caddy Integration
In diesem Projekt demonstriere ich die Integration eines Python-basierten FastAPI-Backends mit dem Caddy Webserver. Ziel war es, eine leichtgewichtige API bereitzustellen, die über einen Reverse Proxy sicher und effizient erreichbar ist.
Das Backend: FastAPI (Text-Analyse & KI)
Als Backend-Framework kommt FastAPI zum Einsatz. In diesem erweiterten Beispiel haben wir zwei Endpunkte:
/generate: Eine klassische Text-Analyse (CPU-bound)./ask-ai: Eine Schnittstelle zu einem LLM über OpenRouter (I/O-bound).
Setup: Installieren Sie openai (pip install openai) und setzen Sie Ihren OpenRouter API Key als Umgebungsvariable.
Das vollständige Skript (main.py):
import os
import requests # Neu: Für die Modell-Liste
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from openai import OpenAI
app = FastAPI()
# --- Teil 1: Text-Analyse ---
class TextRequest(BaseModel):
text: str = Field(..., max_length=500, description="Der zu analysierende Text")
@app.post("/generate")
async def analyze_text(data: TextRequest):
text = data.text
stats = {
"uppercase": text.upper(),
"reversed": text[::-1],
"char_count": len(text),
"word_count": len(text.split()),
"is_palindrome": text.replace(" ", "").lower() == text.replace(" ", "").lower()[::-1]
}
return {"result": stats}
# --- Teil 2: KI-Integration (OpenRouter) ---
# API-Key sicher aus Umgebungsvariablen laden
# Setzen Sie in Linux/Bash: export OPENROUTER_API_KEY="sk-or-..."
API_KEY = os.getenv("OPENROUTER_API_KEY")
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=API_KEY)
class AIRequestData(BaseModel):
prompt: str = Field(..., max_length=2000, description="Code oder Frage")
model: str = Field("google/gemini-2.0-flash-exp:free", description="Das gewählte Modell") # Neu
@app.get("/models") # Neu: Endpunkt für Modelle
def get_free_models():
try:
# Wir holen die Liste direkt von OpenRouter
response = requests.get("https://openrouter.ai/api/v1/models")
if response.status_code == 200:
data = response.json()["data"]
# Filter: Nur Modelle, die auf ":free" enden
free_models = [m["id"] for m in data if m["id"].endswith(":free")]
return {"models": sorted(free_models)}
except:
pass
# Fallback, falls API nicht erreichbar
return {"models": ["google/gemini-2.0-flash-exp:free", "meta-llama/llama-3-8b-instruct:free"]}
@app.post("/ask-ai")
async def ask_ai(data: AIRequestData):
if not API_KEY:
raise HTTPException(status_code=500, detail="Server-Konfiguration fehlt (API Key)")
try:
completion = client.chat.completions.create(
model=data.model, # Hier nutzen wir das vom User gewählte Modell
messages=[
{"role": "system", "content": "Du bist ein Code-Erklärer. Erkläre den Code kurz, prägnant und auf Deutsch."},
{"role": "user", "content": data.prompt}
]
)
return {"result": completion.choices[0].message.content}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Der Webserver: Caddy
Um die API unter der gleichen Domain wie das Frontend verfügbar zu machen, verwende ich Caddy. Wir nutzen handle_path, um das /api Präfix zu entfernen, bevor die Anfrage an FastAPI weitergeleitet wird.
Auszug aus der Caddy-Konfiguration:
handle_path /api/* {
reverse_proxy 127.0.0.1:8000
}
Live-Demo: Text-Analyzer
Geben Sie einen Text ein, um ihn vom Python-Backend analysieren zu lassen. (Stellen Sie sicher, dass das aktualisierte Skript lokal läuft).
Text Analyse API
Erweiterung: KI Code-Erklärer
Dieses zweite Beispiel nutzt den neuen /ask-ai Endpunkt. Geben Sie einen Code-Schnipsel ein, und das Backend leitet ihn sicher an ein LLM (via OpenRouter) weiter.
Der System-Prompt: Ein großer Vorteil dieser Architektur ist, dass wir dem Modell eine feste Rolle zuweisen können, die der Nutzer nicht ändern kann. Im Python-Backend wird jeder Anfrage dieser System-Prompt vorangestellt:
“Du bist ein Code-Erklärer. Erkläre den Code kurz, prägnant und auf Deutsch.”
Das garantiert, dass die KI immer hilfreich und fokussiert antwortet, egal was der Nutzer eingibt. Ihr API-Key bleibt dabei sicher auf dem Server.