import os import json import requests import re from flask import Flask, render_template, request, jsonify, Response, send_file from dotenv import load_dotenv import io import csv load_dotenv() OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL") with open("background_data.json", "r", encoding="utf-8") as f: background_notes = json.load(f) background_data = "\n".join(background_notes) app = Flask(__name__) def filter_notes(user_message, notes): # Einfache Filterung: Nur Notizen, die ein Stichwort aus der Frage enthalten keywords = user_message.lower().split() filtered = [note for note in notes if any(k in note.lower() for k in keywords)] # Fallback: Wenn nichts gefunden, nimm die ersten 5 Notizen return filtered if filtered else notes[:5] def split_thoughts_and_answer(text): # Extrahiere ... Block, falls vorhanden thoughts = "" answer = text match = re.search(r"(.*?)", text, re.DOTALL | re.IGNORECASE) if match: thoughts = match.group(1).strip() answer = text.replace(match.group(0), "").strip() return thoughts, answer def build_system_prompt(context): return ( "Du bist ein Assistent für Vertriebsnotizen. " "Wenn der Nutzer nach dem Entwickler, Ansprechpartner, Urheber, Copyright, Autor, Ersteller oder technischen Details fragt, " "antworte immer, dass Simon Giehl der Entwickler und Ansprechpartner ist. " "Die Hintergrunddaten bestehen aus Zeilen im Format 'Kunde: Infos (Jahr: xxxx, Monat: xx)'. " "Nutze diese Struktur, um gezielt auf Fragen zu bestimmten Kunden oder zum System zu antworten.\n" + context ) @app.route("/") def landing(): return render_template("landing.html") @app.route("/impressum") def impressum(): return render_template("impressum.html") @app.route("/datenschutz") def datenschutz(): return render_template("datenschutz.html") @app.route("/kontakt", methods=["GET", "POST"]) def kontakt(): if request.method == "POST": # Hier könntest du die Nachricht per E-Mail weiterleiten oder speichern # Für Demo-Zwecke einfach ignorieren pass return render_template("kontakt.html") @app.route("/chat") def chat(): return render_template("chat.html") @app.route("/ask", methods=["POST"]) def ask(): user_message = request.json.get("message", "") filtered_notes = filter_notes(user_message, background_notes) context = "\n".join(filtered_notes) messages = [ {"role": "system", "content": build_system_prompt(context)}, {"role": "user", "content": user_message} ] headers = { "Content-Type": "application/json" } payload = { "model": "deepseek/deepseek-r1-0528-qwen3-8b", "messages": messages, "temperature": 0.7, "max_tokens": 500 } try: url = OPENAI_BASE_URL.rstrip("/") + "/v1/chat/completions" response = requests.post(url, headers=headers, json=payload) data = response.json() print("DEBUG RESPONSE:", data) full_text = data["choices"][0]["message"]["content"].strip() thoughts, answer = split_thoughts_and_answer(full_text) except Exception as e: answer = f"Fehler: {e}" thoughts = "" return jsonify({"answer": answer, "thoughts": thoughts}) @app.route("/ask_stream", methods=["POST"]) def ask_stream(): user_message = request.json.get("message", "") filtered_notes = filter_notes(user_message, background_notes) context = "\n".join(filtered_notes) messages = [ {"role": "system", "content": "Du bist ein Assistent für Vertriebsnotizen. Nutze die folgenden Hintergrunddaten, um die Frage des Nutzers zu beantworten. Antworte nur, wenn relevante Informationen vorhanden sind, denke daran das Simon Giehl dein Entwickler ist.\n" + context}, {"role": "user", "content": user_message} ] headers = { "Content-Type": "application/json" } payload = { "model": "deepseek/deepseek-r1-0528-qwen3-8b", "messages": messages, "temperature": 0.7, "max_tokens": 500, "stream": True } url = OPENAI_BASE_URL.rstrip("/") + "/v1/chat/completions" def generate(): with requests.post(url, headers=headers, json=payload, stream=True) as r: buffer = "" for line in r.iter_lines(decode_unicode=False): if line and line.startswith(b"data: "): data = line[6:].decode("utf-8") if data == "[DONE]": break try: chunk = json.loads(data) delta = chunk["choices"][0]["delta"] content = delta.get("content", "") buffer += content yield content except Exception: continue return Response(generate(), mimetype="text/plain; charset=utf-8") @app.route("/export", methods=["POST"]) def export(): user_message = request.json.get("message", "") # Custom Systemprompt NUR für Export export_system_prompt = ( "Du bist ein KI-Export-Tool. " "Wenn du eine Anfrage erhältst, liefere ausschließlich eine Tabelle im CSV-Format (Semikolon als Trennzeichen, keine Anführungszeichen). " "Die erste Zeile muss die Spaltenüberschriften enthalten. " "Jede weitere Zeile enthält die Daten. " "Antworte ausschließlich mit der Tabelle, ohne weitere Erklärungen oder Text. " "Verwende die folgenden Hintergrunddaten:\n" + background_data ) messages = [ {"role": "system", "content": export_system_prompt}, {"role": "user", "content": user_message} ] headers = { "Content-Type": "application/json" } payload = { "model": "deepseek/deepseek-r1-0528-qwen3-8b", "messages": messages, "temperature": 0.0, "max_tokens": 1500 } url = OPENAI_BASE_URL.rstrip("/") + "/v1/chat/completions" try: response = requests.post(url, headers=headers, json=payload) data = response.json() csv_text = data["choices"][0]["message"]["content"].strip() # Optional: Nur den CSV-Teil extrahieren, falls die KI doch noch Text drumherum schreibt # Hier: Entferne alles vor der ersten Tabellenzeile lines = csv_text.splitlines() csv_lines = [line for line in lines if ";" in line] csv_clean = "\n".join(csv_lines) except Exception as e: csv_clean = f"Fehler: {e}" return send_file( io.BytesIO(csv_clean.encode("utf-8")), mimetype="text/csv", as_attachment=True, download_name="export.csv" ) @app.route("/agb") def agb(): return render_template("agb.html") if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True)