diff --git a/.env b/.env index 5c0217e..e83c73b 100644 --- a/.env +++ b/.env @@ -7,7 +7,7 @@ FLASK_ENV=development SECRET_KEY=your-secret-key-replace-in-production # OpenAI API - +OPENAI_API_KEY=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 # Datenbank # Bei Bedarf kann hier eine andere Datenbank-URL angegeben werden diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc index 4acc7dd..6b1dbbc 100644 Binary files a/__pycache__/app.cpython-313.pyc and b/__pycache__/app.cpython-313.pyc differ diff --git a/app.py b/app.py index cb16aca..c965712 100644 --- a/app.py +++ b/app.py @@ -15,11 +15,15 @@ from wtforms.validators import DataRequired, Email, Length, EqualTo, ValidationE from functools import wraps import secrets from sqlalchemy.sql import func +import openai from openai import OpenAI from dotenv import load_dotenv from flask_socketio import SocketIO, emit from flask_migrate import Migrate import sqlalchemy +import ssl +import certifi +import os # Modelle importieren from models import ( @@ -46,12 +50,32 @@ app.config['UPLOAD_FOLDER'] = os.getenv('UPLOAD_FOLDER', os.path.join(os.getcwd( app.config['WTF_CSRF_ENABLED'] = False # OpenAI API-Konfiguration -api_key = "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" -# api_key = os.environ.get("OPENAI_API_KEY") -# if not api_key: -# print("WARNUNG: Kein OPENAI_API_KEY in Umgebungsvariablen gefunden. KI-Funktionalität wird nicht verfügbar sein.") +api_key = os.environ.get("OPENAI_API_KEY", "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") -client = OpenAI(api_key=api_key) +# Variable zur Überprüfung, ob OpenAI verfügbar ist +openai_available = True + +# Client mit SSL-Verify-Deaktivierung für problematische Umgebungen +try: + # Versuche, den OpenAI-Client zu initialisieren und zu testen + client = OpenAI(api_key=api_key) + # Deaktiviere SSL-Verifizierung, falls in Windows-Umgebungen Probleme auftreten + client._client.proxies = {} + client._client.verify = False + + # Testanfrage, um zu prüfen, ob die API funktioniert + try: + # Einfache Testanfrage + resp = client.models.list() + openai_available = True + print("OpenAI API-Verbindung erfolgreich hergestellt.") + except Exception as e: + print(f"OpenAI API-Verbindungstest fehlgeschlagen: {e}") + openai_available = False +except Exception as e: + print(f"Fehler bei der Initialisierung des OpenAI-Clients: {e}") + openai_available = False + client = None # Dark Mode Einstellung in Session speichern @app.before_request @@ -1471,163 +1495,77 @@ def too_many_requests(e): # OpenAI-Integration für KI-Assistenz @app.route('/api/assistant', methods=['POST']) def chat_with_assistant(): - """Chatbot-API mit OpenAI Integration und Datenbankzugriff.""" + """Chat mit dem KI-Assistenten""" data = request.json + user_message = data.get('message', '') - # Prüfen, ob wir ein einzelnes Prompt oder ein messages-Array haben - if 'messages' in data: - messages = data.get('messages', []) - if not messages: - return jsonify({ - 'error': 'Keine Nachrichten vorhanden.' - }), 400 - - # Extrahiere Systemnachricht falls vorhanden, sonst Standard-Systemnachricht - system_message = next((msg['content'] for msg in messages if msg['role'] == 'system'), - "Du bist ein spezialisierter Assistent für Systades, eine innovative Wissensmanagement-Plattform. " - "Systades ist ein intelligentes System zur Verwaltung, Verknüpfung und Visualisierung von Wissen. " - "Die Plattform ermöglicht es Nutzern, Gedanken zu erfassen, in Kategorien zu organisieren und durch Mindmaps zu visualisieren. " - "Wichtige Funktionen sind:\n" - "- Gedankenverwaltung mit Titeln, Zusammenfassungen und Keywords\n" - "- Kategorisierung und thematische Organisation\n" - "- Interaktive Mindmaps zur Wissensvisualisierung\n" - "- KI-gestützte Analyse und Zusammenfassung von Inhalten\n" - "- Kollaborative Wissensarbeit und Teilen von Inhalten\n\n" - "Du antwortest AUSSCHLIESSLICH auf Fragen bezüglich der Systades-Wissensdatenbank und Website. " - "Du kannst Informationen zu Gedanken, Kategorien und Mindmaps liefern und durch Themen führen. " - "Antworte informativ, sachlich und gut strukturiert auf Deutsch.") - - # Formatiere Nachrichten für OpenAI API - api_messages = [{"role": "system", "content": system_message}] - - # Füge Benutzer- und Assistenten-Nachrichten hinzu - for msg in messages: - if msg['role'] in ['user', 'assistant']: - api_messages.append({"role": msg['role'], "content": msg['content']}) - else: - # Alte Implementierung für direktes Prompt - prompt = data.get('prompt', '') - context = data.get('context', '') - selected_items = data.get('selected_items', []) # Ausgewählte Elemente aus der Datenbank - - if not prompt: - return jsonify({ - 'error': 'Prompt darf nicht leer sein.' - }), 400 - - # Zusammenfassen mehrerer Gedanken oder Analyse anfordern - system_message = ( - "Du bist ein spezialisierter Assistent für Systades, eine innovative Wissensmanagement-Plattform. " - "Systades ist ein intelligentes System zur Verwaltung, Verknüpfung und Visualisierung von Wissen. " - "Die Plattform ermöglicht es Nutzern, Gedanken zu erfassen, in Kategorien zu organisieren und durch Mindmaps zu visualisieren. " - "Wichtige Funktionen sind:\n" - "- Gedankenverwaltung mit Titeln, Zusammenfassungen und Keywords\n" - "- Kategorisierung und thematische Organisation\n" - "- Interaktive Mindmaps zur Wissensvisualisierung\n" - "- KI-gestützte Analyse und Zusammenfassung von Inhalten\n" - "- Kollaborative Wissensarbeit und Teilen von Inhalten\n\n" - "Du antwortest AUSSCHLIESSLICH auf Fragen bezüglich der Systades-Wissensdatenbank und Website. " - "Du kannst Informationen zu Gedanken, Kategorien und Mindmaps liefern und durch Themen führen. " - "Antworte informativ, sachlich und gut strukturiert auf Deutsch." - ) - - if context: - system_message += f"\n\nKontext: {context}" - - if selected_items: - system_message += "\n\nAusgewählte Elemente aus der Datenbank:\n" - for item in selected_items: - if 'type' in item and 'data' in item: - if item['type'] == 'thought': - system_message += f"- Gedanke: {item['data'].get('title', 'Unbekannter Titel')}\n" - system_message += f" Zusammenfassung: {item['data'].get('abstract', 'Keine Zusammenfassung')}\n" - system_message += f" Keywords: {item['data'].get('keywords', 'Keine Keywords')}\n" - elif item['type'] == 'category': - system_message += f"- Kategorie: {item['data'].get('name', 'Unbekannte Kategorie')}\n" - system_message += f" Beschreibung: {item['data'].get('description', 'Keine Beschreibung')}\n" - system_message += f" Unterkategorien: {item['data'].get('subcategories', 'Keine Unterkategorien')}\n" - elif item['type'] == 'mindmap': - system_message += f"- Mindmap: {item['data'].get('name', 'Unbekannte Mindmap')}\n" - system_message += f" Beschreibung: {item['data'].get('description', 'Keine Beschreibung')}\n" - system_message += f" Knoten: {item['data'].get('nodes', 'Keine Knoten')}\n" - - api_messages = [ - {"role": "system", "content": system_message}, - {"role": "user", "content": prompt} - ] - - # Extrahiere die letzte Benutzernachricht für Datenbankabfragen - user_message = next((msg['content'] for msg in reversed(api_messages) if msg['role'] == 'user'), '') - - # Prüfen, ob die Anfrage nach Datenbankinformationen sucht - db_context = check_database_query(user_message) - - if db_context: - # Erweitere den Kontext mit Datenbankinformationen - api_messages.append({ - "role": "system", - "content": f"Hier sind relevante Informationen aus der Datenbank:\n\n{db_context}" - }) + # Überprüfe, ob die OpenAI-API verfügbar ist + if not openai_available or client is None: + # Fallback-Antwort, wenn OpenAI nicht verfügbar ist + fallback_message = { + "response": "Der KI-Assistent ist derzeit nicht verfügbar. Bitte versuchen Sie es später erneut oder kontaktieren Sie den Administrator.", + "thoughts": "Leider konnte keine Verbindung zur OpenAI-API hergestellt werden. Dies kann an SSL-Zertifikatsproblemen, Netzwerkproblemen oder API-Schlüsselproblemen liegen." + } + return jsonify(fallback_message) + # Versuche, eine Antwort von OpenAI zu erhalten try: - # OpenAI-Client mit dem API-Key initialisieren - client = OpenAI(api_key=api_key) - - # Überprüfen ob OpenAI API-Key konfiguriert ist - if not api_key or api_key.startswith("sk-dummy"): - print("Warnung: OpenAI API-Key ist nicht oder nur als Dummy konfiguriert!") + # Check, ob es eine Datenbankanfrage ist + is_db_query, db_query_result = check_database_query(user_message) + if is_db_query: return jsonify({ - 'error': 'Der OpenAI API-Key ist nicht korrekt konfiguriert. Bitte konfigurieren Sie die Umgebungsvariable OPENAI_API_KEY.' - }), 500 - - # API-Aufruf mit Timeout - import time - start_time = time.time() + "response": db_query_result, + "thoughts": "Ihre Anfrage wurde als Datenbankanfrage erkannt und direkt beantwortet." + }) + + system_message = """Du bist SysTades, ein intelligenter Assistent in einer Wissensmanagement-Anwendung. + Deine Aufgabe ist es, tiefe, reflektierte Antworten auf Fragen der Benutzer zu geben. + Beziehe dich auf vorhandene Konzepte und betrachte Fragen stets aus mehreren Perspektiven. + Vermeide pauschale Antworten und beziehe immer verschiedene Denkansätze ein. + Antworte stets auf Deutsch und in einem nachdenklichen, philosophischen Ton. + """ - # Erhöhtes Timeout für die API-Anfrage response = client.chat.completions.create( model="gpt-4o-mini", - messages=api_messages, - max_tokens=1000, + messages=[ + {"role": "system", "content": system_message}, + {"role": "user", "content": user_message} + ], temperature=0.7, timeout=60 # Erhöht auf 60 Sekunden für bessere Zuverlässigkeit ) - print(f"OpenAI API-Antwortzeit: {time.time() - start_time:.2f} Sekunden") + # Extrahiere die Antwort + assistant_response = response.choices[0].message.content - answer = response.choices[0].message.content + # Generiere zusätzliche "Gedanken" für verbesserte UX + thoughts_response = client.chat.completions.create( + model="gpt-4o-mini", + messages=[ + {"role": "system", "content": "Gib kurze Einblicke in deine Gedankenprozesse zu dieser Frage. Schreibe 1-3 Sätze zur Reflexion über die Frage und die wichtigsten Aspekte deiner Antwort. Bleibe dabei informell und persönlich. Schreibe auf Deutsch."}, + {"role": "user", "content": user_message}, + {"role": "assistant", "content": assistant_response} + ], + temperature=0.7, + max_tokens=150 + ) + thoughts = thoughts_response.choices[0].message.content - # Für das neue Format erwarten wir response statt answer return jsonify({ - 'response': answer + "response": assistant_response, + "thoughts": thoughts }) - except Exception as e: + print(f"Fehler bei der OpenAI-Anfrage: {e}") import traceback - error_message = str(e) - stack_trace = traceback.format_exc() + print(f"Stack Trace: {traceback.format_exc()}") - print(f"Fehler bei der OpenAI-Anfrage: {error_message}") - print(f"Stack Trace: {stack_trace}") - - # Überprüfen auf spezifische Fehlertypen - if "timeout" in error_message.lower(): - return jsonify({ - 'error': 'Die Anfrage hat zu lange gedauert. Bitte versuchen Sie es später erneut.' - }), 504 - elif "rate limit" in error_message.lower(): - return jsonify({ - 'error': 'API-Ratelimit erreicht. Bitte warten Sie einen Moment und versuchen Sie es erneut.' - }), 429 - elif "internal server error" in error_message.lower() or "500" in error_message: - return jsonify({ - 'error': 'Es ist ein Serverfehler aufgetreten. Unser Team wurde benachrichtigt.' - }), 500 - else: - # Allgemeine Fehlermeldung - return jsonify({ - 'error': 'Bei der Verarbeitung Ihrer Anfrage ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.' - }), 500 + # Fallback-Antwort bei Fehler + fallback_message = { + "response": "Es tut mir leid, aber ich konnte Ihre Anfrage nicht verarbeiten. Bitte versuchen Sie es später erneut.", + "thoughts": "Ein technisches Problem ist aufgetreten. Dies könnte an Netzwerkproblemen, API-Grenzen oder Serverauslastung liegen." + } + return jsonify(fallback_message) def check_database_query(user_message): """ diff --git a/database/systades.db b/database/systades.db index d7a6612..2b37256 100644 Binary files a/database/systades.db and b/database/systades.db differ