Files
reservierungssystem/app/utils/ollama_client.py
T

192 lines
5.4 KiB
Python

#!/usr/bin/env python3
"""Ollama Integration für KI-gestütztes E-Mail-Parsing"""
import json
import requests
import re
from datetime import datetime
OLLAMA_URL = "http://192.168.0.150:11434/api/generate"
DEFAULT_MODEL = "gemma4:latest"
def query_ollama(prompt, model=DEFAULT_MODEL, temperature=0.1):
"""Ollama API aufrufen"""
try:
response = requests.post(
OLLAMA_URL,
json={
"model": model,
"prompt": prompt,
"stream": False,
"options": {
"temperature": temperature,
"num_predict": 500
}
},
timeout=30
)
response.raise_for_status()
return response.json()
except Exception as e:
return {"error": str(e)}
def extract_json_from_response(text):
"""JSON aus Ollama-Antwort extrahieren"""
# Versuche direkt als JSON zu parsen
try:
return json.loads(text)
except:
pass
# Suche nach JSON-Block
json_match = re.search(r'\{[\s\S]*\}', text)
if json_match:
try:
return json.loads(json_match.group())
except:
pass
return {"error": "Kein gültiges JSON gefunden", "raw": text}
def parse_email_with_ollama(email_subject, email_body, sender_name):
"""E-Mail mit Ollama parsen"""
prompt = f"""Analysiere diese Restaurant-Reservierungs-E-Mail.
WICHTIG: Suche nach einer Buchungsnummer im Format RES-YYYY-MM-DD-XXX.
Wenn eine Buchungsnummer erwähnt wird, ist es eine Änderung oder Stornierung.
Wenn keine Buchungsnummer vorhanden ist, ist es eine neue Reservierung.
Absender: {sender_name}
Betreff: {email_subject}
E-Mail-Inhalt:
---
{email_body}
---
Extrahiere folgende Informationen und antworte NUR mit JSON:
{{
"booking_number": "RES-2025-05-12-001" oder null,
"intent": "new" | "modification" | "cancellation",
"name": "Name des Gastes",
"phone": "Telefonnummer",
"email": "E-Mail-Adresse",
"date": "YYYY-MM-DD",
"time": "HH:MM",
"guests": 4,
"occasion": "Geburtstag/Dinner/etc. oder null",
"notes": "Weitere Informationen",
"confidence": 0.95,
"needs_review": false,
"review_reason": null
}}
Beispiele für Änderungen:
- "Ich möchte meine Reservierung RES-2025-05-12-001 verschieben"
- "Bitte stornieren Sie meinen Tisch"
- "Wir sind jetzt 6 statt 4 Personen"
Antworte nur mit dem JSON-Objekt, keine Erklärungen."""
result = query_ollama(prompt)
if "error" in result:
return {
"error": result["error"],
"needs_review": True,
"review_reason": "Ollama-Fehler"
}
response_text = result.get("response", "")
parsed = extract_json_from_response(response_text)
# Validiere und ergänze
if "confidence" not in parsed:
parsed["confidence"] = 0.5
if "needs_review" not in parsed:
parsed["needs_review"] = parsed.get("confidence", 0) < 0.7
return parsed
def generate_confirmation_email(reservation, is_new=True):
"""Bestätigungsmail-Text generieren"""
if is_new:
template = f"""Hallo {reservation.get('name', 'Gast')},
vielen Dank für Ihre Reservierung bei uns!
━━━━━━━━━━━━━━━━━━━━━━━
📋 BUCHUNGSNUMMER: {reservation['booking_number']}
━━━━━━━━━━━━━━━━━━━━━━━
📅 Datum: {reservation.get('date', 'unbekannt')}
⏰ Uhrzeit: {reservation.get('time_from', 'unbekannt')}
👥 Personen: {reservation.get('guests', 'unbekannt')}
🍽️ Tisch: {reservation.get('table_name', 'wird zugewiesen')}
Bei Änderungen antworten Sie einfach auf diese E-Mail
und nennen Sie Ihre Buchungsnummer: {reservation['booking_number']}
Wir freuen uns auf Ihren Besuch!
Mit freundlichen Grüßen
Ihr Restaurant-Team"""
else:
template = f"""Hallo {reservation.get('name', 'Gast')},
Ihre Reservierung wurde aktualisiert.
━━━━━━━━━━━━━━━━━━━━━━━
📋 BUCHUNGSNUMMER: {reservation['booking_number']}
━━━━━━━━━━━━━━━━━━━━━━━
📅 Neuer Termin: {reservation.get('date', 'unbekannt')} um {reservation.get('time_from', 'unbekannt')}
👥 Personen: {reservation.get('guests', 'unbekannt')}
Alle anderen Details bleiben bestehen.
Bei weiteren Änderungen nennen Sie bitte immer Ihre Buchungsnummer: {reservation['booking_number']}
Mit freundlichen Grüßen
Ihr Restaurant-Team"""
return template
def suggest_table_for_group(available_tables, guests, date, time, ollama_model=DEFAULT_MODEL):
"""Ollama schlägt optimalen Tisch vor"""
tables_info = "\n".join([
f"- {t['name']}: {t['seats']} Plätze, Bereich: {t.get('area_name', 'unbekannt')}"
for t in available_tables
])
prompt = f"""Schlage den besten Tisch für diese Reservierung vor:
Gruppe: {guests} Personen
Datum: {date}
Uhrzeit: {time}
Verfügbare Tische:
{tables_info}
Berücksichtige:
- Passende Größe für {guests} Personen
- Atmosphäre (Fenster bevorzugt)
- Nicht zu groß oder zu klein
Antworte mit JSON:
{{
"suggested_table_id": 5,
"reasoning": "Tisch 5 hat 6 Plätze, ist am Fenster und ideal für {guests} Personen",
"alternatives": [3, 7]
}}"""
result = query_ollama(prompt, model=ollama_model)
if "error" in result:
return None
return extract_json_from_response(result.get("response", ""))