Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import google.generativeai as genai
|
3 |
+
from flask import Flask, request, jsonify
|
4 |
+
from flask_cors import CORS
|
5 |
+
|
6 |
+
app = Flask(__name__)
|
7 |
+
CORS(app) # Erlaubt Cross-Origin Anfragen von deiner Hoststar-Seite
|
8 |
+
|
9 |
+
# --- Konfiguration & Gemini Initialisierung ---
|
10 |
+
# Dein Gemini API-Schlüssel wird als Hugging Face Space Secret übergeben
|
11 |
+
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
|
12 |
+
if not GOOGLE_API_KEY:
|
13 |
+
raise ValueError("GOOGLE_API_KEY Umgebungsvariable nicht gesetzt!")
|
14 |
+
|
15 |
+
genai.configure(api_key=GOOGLE_API_KEY)
|
16 |
+
|
17 |
+
# Initialisiere das fortschrittlichste Gemini Pro Modell
|
18 |
+
# Nutze 'gemini-1.5-pro' für höchste Qualität und Multimodalität.
|
19 |
+
# 'gemini-1.5-flash' ist schneller und günstiger für rein textlastige Chats.
|
20 |
+
# Hier gehen wir von Pro aus, da Bildgenerierung gewünscht ist.
|
21 |
+
model = genai.GenerativeModel(
|
22 |
+
'gemini-1.5-pro',
|
23 |
+
tools=[genai.tool_for_Google Search()], # Ermöglicht Gemini die Internetrecherche
|
24 |
+
system_instruction="Du bist Moejra, eine hilfsbereite, geduldige und informative KI-Lernbegleitung von learn.create.repeat. Antworte immer im Stil von Moejra: unterstützend, ermutigend und auf Bildungstechnologie fokussiert. Nutze Informationen aus der Wissensdatenbank und bei Bedarf aus dem Internet. Wenn der Nutzer nach einem Bild fragt oder eine visuelle Idee beschreibt, generiere ein passendes Bild und erwähne es explizit. Gib sonst eine klare Textantwort."
|
25 |
+
)
|
26 |
+
chat = model.start_chat(history=[]) # Verwaltet den Chatverlauf für Konversationen
|
27 |
+
|
28 |
+
# --- Wissensdatenbank (RAG) Funktion ---
|
29 |
+
def retrieve_info_from_kb(query):
|
30 |
+
"""
|
31 |
+
Durchsucht die lokale Wissensdatenbank nach relevanten Informationen.
|
32 |
+
Hier eine einfache Keyword-Suche. Für komplexere Fälle: LangChain/LlamaIndex
|
33 |
+
mit Embeddings und einer lokalen Vektor-DB (z.B. ChromaDB).
|
34 |
+
"""
|
35 |
+
relevant_text = []
|
36 |
+
knowledge_base_dir = "knowledge_base"
|
37 |
+
if not os.path.exists(knowledge_base_dir):
|
38 |
+
print(f"Warnung: Wissensdatenbank-Verzeichnis '{knowledge_base_dir}' nicht gefunden.")
|
39 |
+
return ""
|
40 |
+
|
41 |
+
query_keywords = [word.lower() for word in query.split() if len(word) > 2] # Ignoriere sehr kurze Wörter
|
42 |
+
|
43 |
+
for filename in os.listdir(knowledge_base_dir):
|
44 |
+
if filename.endswith(".txt"):
|
45 |
+
filepath = os.path.join(knowledge_base_dir, filename)
|
46 |
+
try:
|
47 |
+
with open(filepath, "r", encoding="utf-8") as f:
|
48 |
+
content = f.read()
|
49 |
+
# Prüfe, ob relevante Keywords im Inhalt vorkommen
|
50 |
+
if any(keyword in content.lower() for keyword in query_keywords):
|
51 |
+
relevant_text.append(content)
|
52 |
+
except Exception as e:
|
53 |
+
print(f"Fehler beim Lesen der Wissensdatenbank-Datei {filename}: {e}")
|
54 |
+
|
55 |
+
return "\n---\n".join(relevant_text) if relevant_text else ""
|
56 |
+
|
57 |
+
|
58 |
+
# --- API-Endpunkt für den Chat ---
|
59 |
+
@app.route('/chat', methods=['POST'])
|
60 |
+
def handle_chat():
|
61 |
+
user_input = request.json.get('text')
|
62 |
+
if not user_input:
|
63 |
+
return jsonify({"error": "No text provided"}), 400
|
64 |
+
|
65 |
+
print(f"Received user input: {user_input}")
|
66 |
+
|
67 |
+
# 1. Wissensdatenbank abrufen
|
68 |
+
kb_info = retrieve_info_from_kb(user_input)
|
69 |
+
|
70 |
+
# 2. Prompt für Gemini erstellen
|
71 |
+
prompt_parts = [
|
72 |
+
f"Nutzerfrage: {user_input}",
|
73 |
+
]
|
74 |
+
if kb_info:
|
75 |
+
prompt_parts.append(f"Zusätzlicher relevanter Kontext aus der Wissensdatenbank von learn.create.repeat.:\n{kb_info}")
|
76 |
+
prompt_parts.append("Priorisiere Informationen aus diesem Kontext. Wenn die Frage eine Bildgenerierung impliziert, erstelle ein passendes Bild. Gib sonst eine klare, ermutigende Textantwort. Nutze deine gesamte Expertise.")
|
77 |
+
else:
|
78 |
+
prompt_parts.append("Nutze dein Wissen und recherchiere bei Bedarf im Internet. Wenn die Frage eine Bildgenerierung impliziert, erstelle ein passendes Bild. Gib sonst eine klare, ermutigende Textantwort. Nutze deine gesamte Expertise.")
|
79 |
+
|
80 |
+
response_text = ""
|
81 |
+
image_urls = []
|
82 |
+
|
83 |
+
try:
|
84 |
+
# Gemini aufrufen
|
85 |
+
gemini_response = chat.send_message(prompt_parts)
|
86 |
+
|
87 |
+
# Die Antwort durchgehen, um Text und ggf. Bilder zu extrahieren
|
88 |
+
for part in gemini_response.candidates[0].content.parts:
|
89 |
+
if hasattr(part, 'text'):
|
90 |
+
response_text += part.text
|
91 |
+
elif hasattr(part, 'image'):
|
92 |
+
# Wenn Gemini ein Bild generiert, ist es oft als Base64 kodiert
|
93 |
+
# Manchmal ist es auch eine URL (sollte dann im part.image.url sein)
|
94 |
+
if hasattr(part.image, 'base64'):
|
95 |
+
image_urls.append(f"data:image/jpeg;base64,{part.image.base64}")
|
96 |
+
elif hasattr(part.image, 'url'):
|
97 |
+
image_urls.append(part.image.url)
|
98 |
+
# Falls Gemini nur den Text der Bild-URL im Textfeld liefert,
|
99 |
+
# müsste dieser Text hier zusätzlich geparst werden.
|
100 |
+
# In der Regel liefert gemini-1.5-pro separate Bild-Parts.
|
101 |
+
print(f"Gemini response text: {response_text}, images: {len(image_urls)}")
|
102 |
+
|
103 |
+
except Exception as e:
|
104 |
+
print(f"Fehler beim Aufruf der Gemini API: {e}")
|
105 |
+
# Überprüfen, ob der Fehler durch Sicherheitsfilter verursacht wurde
|
106 |
+
if hasattr(gemini_response, 'prompt_feedback') and gemini_response.prompt_feedback.block_reason:
|
107 |
+
response_text = "Entschuldige, diese Anfrage konnte ich aus Sicherheitsgründen nicht bearbeiten. Bitte formuliere sie anders."
|
108 |
+
else:
|
109 |
+
response_text = "Entschuldige, ich konnte deine Anfrage leider nicht vollständig bearbeiten. Bitte versuch es später noch einmal oder formuliere deine Frage anders."
|
110 |
+
|
111 |
+
return jsonify({
|
112 |
+
"response_text": response_text,
|
113 |
+
"image_urls": image_urls # Liste der generierten Bild-URLs
|
114 |
+
})
|
115 |
+
|
116 |
+
# Starte die Flask-App
|
117 |
+
if __name__ == '__main__':
|
118 |
+
# Flask läuft standardmässig auf Port 5000, aber Hugging Face Spaces exposed 7860
|
119 |
+
app.run(host='0.0.0.0', port=7860)
|