From fce4c721c69b6ceb5aa3f3e22755075a41c7708e Mon Sep 17 00:00:00 2001
From: valueon
Date: Sat, 15 Mar 2025 19:53:40 +0100
Subject: [PATCH] mvp running
---
backend/agent_service.py | 630 ++++++++++++++++++++++++---
backend/app.py | 43 +-
backend/config.ini | 15 +
backend/data/.gitignore | 4 -
backend/data/agents.json | 47 ++
backend/data/files.json | 65 +++
backend/data/prompts.json | 14 +
backend/data/workspaces.json | 18 +
backend/database.py | 2 +-
backend/models.py | 4 +-
backend/static/.gitignore | 4 -
backend/webparts/part-agents.html | 15 +
backend/webparts/part-agents.js | 204 +++++++++
backend/webparts/part-data.html | 18 +
backend/webparts/part-data.js | 111 +++++
backend/webparts/part-prompts.html | 15 +
backend/webparts/part-prompts.js | 168 +++++++
backend/webparts/part-workflow.html | 103 +++++
backend/webparts/part-workflow.js | 408 +++++++++++++++++
backend/webparts/test.js | Bin 0 -> 52 bytes
backend/webparts/test.txt | 15 +
frontend/index.html | 147 +------
frontend/main.js | 321 ++++++++++++++
frontend/script.js | 654 ----------------------------
frontend/styles.css | 241 ++++++++++
requirements.txt | 9 +-
start.bat | 2 +-
start.sh | 2 +-
28 files changed, 2390 insertions(+), 889 deletions(-)
create mode 100644 backend/config.ini
delete mode 100644 backend/data/.gitignore
create mode 100644 backend/data/agents.json
create mode 100644 backend/data/files.json
create mode 100644 backend/data/prompts.json
create mode 100644 backend/data/workspaces.json
delete mode 100644 backend/static/.gitignore
create mode 100644 backend/webparts/part-agents.html
create mode 100644 backend/webparts/part-agents.js
create mode 100644 backend/webparts/part-data.html
create mode 100644 backend/webparts/part-data.js
create mode 100644 backend/webparts/part-prompts.html
create mode 100644 backend/webparts/part-prompts.js
create mode 100644 backend/webparts/part-workflow.html
create mode 100644 backend/webparts/part-workflow.js
create mode 100644 backend/webparts/test.js
create mode 100644 backend/webparts/test.txt
create mode 100644 frontend/main.js
delete mode 100644 frontend/script.js
diff --git a/backend/agent_service.py b/backend/agent_service.py
index 8cf6d4a2..55750a66 100644
--- a/backend/agent_service.py
+++ b/backend/agent_service.py
@@ -1,24 +1,125 @@
import asyncio
import uuid
+import os
+import json
+import logging
+import time
+import configparser
+import re
from typing import List, Dict, Any, Optional
from datetime import datetime
-import logging
-import json
-import os
+import sys
+import httpx
+from fastapi import HTTPException
+import requests
+from bs4 import BeautifulSoup
+
+# Logger konfigurieren
logger = logging.getLogger(__name__)
+# Konfiguration aus config.ini laden
+def load_config():
+ config = configparser.ConfigParser()
+ config_path = os.path.join(os.path.dirname(__file__), 'config.ini')
+
+ # Standardkonfiguration
+ default_config = {
+ "openai": {
+ "api_key": "VOKEY",
+ "api_url": "https://api.openai.com/v1/chat/completions",
+ "model_name": "gpt-4o"
+ },
+ "application": {
+ "debug": "True",
+ "upload_dir": "./uploads",
+ "results_dir": "./results"
+ },
+ "webscraping": {
+ "timeout": "10",
+ "max_urls": "3",
+ "max_content_length": "3000",
+ "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
+ }
+ }
+
+ if not os.path.exists(config_path):
+ logger.warning(f"Konfigurationsdatei nicht gefunden: {config_path}")
+ return default_config
+
+ try:
+ config.read(config_path)
+
+ # Konfiguration aus der Datei lesen
+ result = {
+ "openai": {
+ "api_key": config.get('OpenAI', 'API_KEY', fallback=default_config["openai"]["api_key"]),
+ "api_url": config.get('OpenAI', 'API_URL', fallback=default_config["openai"]["api_url"]),
+ "model_name": config.get('OpenAI', 'MODEL_NAME', fallback=default_config["openai"]["model_name"])
+ },
+ "application": {
+ "debug": config.get('Application', 'DEBUG', fallback=default_config["application"]["debug"]),
+ "upload_dir": config.get('Application', 'UPLOAD_DIR', fallback=default_config["application"]["upload_dir"]),
+ "results_dir": config.get('Application', 'RESULTS_DIR', fallback=default_config["application"]["results_dir"])
+ },
+ "webscraping": {
+ "timeout": config.get('WebScraping', 'TIMEOUT', fallback=default_config["webscraping"]["timeout"]),
+ "max_urls": config.get('WebScraping', 'MAX_URLS', fallback=default_config["webscraping"]["max_urls"]),
+ "max_content_length": config.get('WebScraping', 'MAX_CONTENT_LENGTH', fallback=default_config["webscraping"]["max_content_length"]),
+ "user_agent": config.get('WebScraping', 'USER_AGENT', fallback=default_config["webscraping"]["user_agent"])
+ }
+ }
+
+ # Debug-Modus einstellen
+ if result["application"]["debug"].lower() == "true":
+ logging.basicConfig(level=logging.DEBUG)
+ logger.setLevel(logging.DEBUG)
+ logger.debug("Debug-Modus aktiviert")
+
+ return result
+ except Exception as e:
+ logger.error(f"Fehler beim Laden der Konfiguration: {e}")
+ return default_config
+
class AgentService:
"""
- Service für die Verwaltung und Ausführung von Multi-Agent-Workflows.
- In einer Produktionsumgebung würde hier eine Integration mit echten KI-Diensten
- und eine Orchestrierung der verschiedenen Agenten stattfinden.
+ Service für die Verwaltung und Ausführung von Multi-Agent-Workflows mit OpenAI GPT-4o.
"""
def __init__(self):
- self.workflows = {}
- self.results_dir = "./results"
+ # Konfiguration laden
+ self.config = load_config()
+
+ # Verzeichnisse aus der Konfiguration übernehmen
+ self.results_dir = self.config["application"]["results_dir"]
+ self.upload_dir = self.config["application"]["upload_dir"]
+
+ # Web-Scraping-Konfiguration
+ self.scraping_timeout = int(self.config["webscraping"]["timeout"])
+ self.scraping_max_urls = int(self.config["webscraping"]["max_urls"])
+ self.scraping_max_content_length = int(self.config["webscraping"]["max_content_length"])
+ self.scraping_user_agent = self.config["webscraping"]["user_agent"]
+
+ # Verzeichnisse erstellen
os.makedirs(self.results_dir, exist_ok=True)
+ os.makedirs(self.upload_dir, exist_ok=True)
+
+ logger.info(f"AgentService initialisiert mit:")
+ logger.info(f" - Modell: {self.config['openai']['model_name']}")
+ logger.info(f" - Ergebnisverzeichnis: {self.results_dir}")
+ logger.info(f" - Upload-Verzeichnis: {self.upload_dir}")
+
+ # Workflow-Speicher
+ self.workflows = {}
+
+ # HttpClient für API-Aufrufe
+ self.http_client = httpx.AsyncClient(
+ timeout=120.0, # Längeres Timeout für komplexe Anfragen
+ headers={
+ "Authorization": f"Bearer {self.config['openai']['api_key']}",
+ "Content-Type": "application/json"
+ }
+ )
async def execute_workflow(
self,
@@ -29,10 +130,7 @@ class AgentService:
) -> str:
"""
Führt einen Workflow mit den angegebenen Agenten und Dateien aus.
- Diese Methode simuliert die Ausführung für Demo-Zwecke.
-
- In einer echten Implementierung würde hier die tatsächliche Orchestrierung
- der Agenten und die Verarbeitung der Dateien stattfinden.
+ Verwendet OpenAI GPT-4o für die Verarbeitung der Anfragen.
"""
logger.info(f"Starte Workflow {workflow_id} mit {len(agents)} Agenten und {len(files)} Dateien")
@@ -52,10 +150,122 @@ class AgentService:
self._add_log(workflow_id, "Workflow gestartet", "info")
self._add_log(workflow_id, f"Verarbeite {len(files)} Dateien...", "info")
- self.workflows[workflow_id]["progress"] = 0.1
- await asyncio.sleep(1) # Simuliere Verarbeitungszeit
+ # Dateikontexte und Inhalte vorbereiten
+ file_contexts = []
+ file_contents = {}
- # Verarbeitung pro Agent simulieren
+ for file in files:
+ file_id = file["id"]
+ file_name = file["name"]
+ file_type = file["type"]
+ file_path = file.get("path", "")
+
+ # Wenn kein Pfad angegeben ist, versuche, ihn aus dem Upload-Verzeichnis abzuleiten
+ if not file_path and file_name:
+ possible_path = os.path.join(self.upload_dir, file_name)
+ if os.path.exists(possible_path):
+ file_path = possible_path
+ logger.debug(f"Pfad für Datei {file_name} gefunden: {file_path}")
+
+ file_contexts.append({
+ "id": file_id,
+ "name": file_name,
+ "type": file_type,
+ "size": file.get("size", "Unbekannt"),
+ "path": file_path
+ })
+
+ # Dateiinhalt lesen, wenn der Pfad verfügbar ist
+ if file_path and os.path.exists(file_path):
+ try:
+ # Text-basierte Dateien direkt lesen
+ if file_type == "document":
+ # Einfache Textdateien
+ if file_name.endswith(('.txt', '.csv', '.md', '.json')):
+ with open(file_path, 'r', encoding='utf-8') as f:
+ file_contents[file_id] = f.read()
+ self._add_log(workflow_id, f"Datei {file_name} gelesen", "info")
+
+ # Excel-Dateien
+ elif file_name.endswith(('.xlsx', '.xls')):
+ import pandas as pd
+ try:
+ df = pd.read_excel(file_path)
+ file_contents[file_id] = f"Excel-Datei mit {len(df)} Zeilen und {len(df.columns)} Spalten.\n"
+ file_contents[file_id] += f"Spalten: {', '.join(df.columns.tolist())}\n"
+ file_contents[file_id] += "Erste 5 Zeilen:\n"
+ file_contents[file_id] += df.head(5).to_string()
+ self._add_log(workflow_id, f"Excel-Datei {file_name} gelesen", "info")
+ except Exception as e:
+ self._add_log(workflow_id, f"Fehler beim Lesen der Excel-Datei {file_name}: {str(e)}", "error")
+
+ # CSV-Dateien
+ elif file_name.endswith('.csv'):
+ import pandas as pd
+ try:
+ df = pd.read_csv(file_path)
+ file_contents[file_id] = f"CSV-Datei mit {len(df)} Zeilen und {len(df.columns)} Spalten.\n"
+ file_contents[file_id] += f"Spalten: {', '.join(df.columns.tolist())}\n"
+ file_contents[file_id] += "Erste 5 Zeilen:\n"
+ file_contents[file_id] += df.head(5).to_string()
+ self._add_log(workflow_id, f"CSV-Datei {file_name} gelesen", "info")
+ except Exception as e:
+ self._add_log(workflow_id, f"Fehler beim Lesen der CSV-Datei {file_name}: {str(e)}", "error")
+
+ # PDF-Dateien
+ elif file_name.endswith('.pdf'):
+ try:
+ # Falls PyPDF2 installiert ist
+ try:
+ from PyPDF2 import PdfReader
+ reader = PdfReader(file_path)
+ text = ""
+ for page in reader.pages:
+ text += page.extract_text() + "\n\n"
+ file_contents[file_id] = f"PDF mit {len(reader.pages)} Seiten.\nInhalt:\n{text[:2000]}..."
+ self._add_log(workflow_id, f"PDF-Datei {file_name} gelesen", "info")
+ except ImportError:
+ self._add_log(workflow_id, "PyPDF2 nicht installiert. PDF-Inhalt kann nicht extrahiert werden.", "warning")
+ file_contents[file_id] = f"PDF-Datei (Inhalt nicht verfügbar, PyPDF2 fehlt)"
+ except Exception as e:
+ self._add_log(workflow_id, f"Fehler beim Lesen der PDF-Datei {file_name}: {str(e)}", "error")
+
+ # Andere Dokumenttypen
+ else:
+ self._add_log(workflow_id, f"Nicht unterstütztes Dokumentformat: {file_name}", "warning")
+ file_contents[file_id] = f"Dateiinhalt nicht verfügbar (Nicht unterstütztes Format)"
+
+ # Bilddateien werden nicht direkt gelesen, nur Metadaten gespeichert
+ elif file_type == "image":
+ file_contents[file_id] = f"Bilddatei: {file_name} (Inhalt nicht als Text verfügbar)"
+ except Exception as e:
+ logger.error(f"Fehler beim Lesen der Datei {file_name}: {str(e)}")
+ self._add_log(workflow_id, f"Fehler beim Lesen der Datei {file_name}: {str(e)}", "error")
+ else:
+ if file_path:
+ self._add_log(workflow_id, f"Datei {file_name} nicht gefunden: {file_path}", "warning")
+ else:
+ self._add_log(workflow_id, f"Kein Pfad für Datei {file_name} verfügbar", "warning")
+ file_contents[file_id] = f"Dateiinhalt nicht verfügbar"
+
+ # Erstelle einen Kontext mit Dateiliste und Inhalten für leichteren Zugriff
+ file_context_text = "Verfügbare Dateien:\n" + "\n".join([f"- {file['name']} ({file['type']}, {file['size']})" for file in file_contexts])
+
+ # Füge Dateiinhalte hinzu (mit Längenbegrenzung)
+ for file_id, content in file_contents.items():
+ file_name = next((f['name'] for f in file_contexts if f['id'] == file_id), "Unbekannte Datei")
+ file_context_text += f"\n\n==== DATEIINHALT: {file_name} ====\n"
+
+ # Begrenze den Inhalt, um Token-Limits zu respektieren
+ max_content_length = 5000 # Anpassen je nach Anzahl der Dateien und Umfang
+ if len(content) > max_content_length:
+ file_context_text += content[:max_content_length] + "...\n[Dateiinhalt gekürzt aus Platzgründen]"
+ else:
+ file_context_text += content
+
+ self.workflows[workflow_id]["progress"] = 0.1
+
+ # Verarbeitung pro Agent ausführen
for i, agent in enumerate(agents):
agent_id = agent["id"]
agent_name = agent["name"]
@@ -70,31 +280,85 @@ class AgentService:
agent_name
)
- # Simuliere Verarbeitungszeit
+ # Fortschritt aktualisieren
self.workflows[workflow_id]["progress"] = 0.1 + (i + 1) * (0.8 / len(agents)) * 0.5
- await asyncio.sleep(2)
- # Agent-Ergebnis simulieren
- result = self._generate_simulated_result(workflow_id, agent, i, prompt, files)
- self.workflows[workflow_id]["results"].append(result)
+ try:
+ # Agent-spezifische Anweisungen erstellen
+ agent_instructions = self._get_agent_instructions(agent_type)
+
+ # Vollständige Anfrage an OpenAI erstellen
+ full_prompt = f"""
+ # Aufgabe
+ {prompt}
+
+ # Dateikontexte
+ {file_context_text}
+
+ # Agent-Rolle
+ Du bist ein {agent_name} ({agent_type}).
+
+ {agent_instructions}
+
+ Bitte analysiere die Daten und beantworte die Anfrage gemäß deiner Rolle.
+ """
+
+ # Für Web-Scraper: Führe Web-Scraping durch
+ if agent_type == "scraper":
+ self._add_log(workflow_id, "Führe Web-Scraping durch...", "info", agent_id, agent_name)
+ web_data = await self._scrape_web_data(workflow_id, prompt)
+ if web_data:
+ full_prompt += f"\n\n# Gescrapte Web-Daten\n{web_data}"
+ self._add_log(workflow_id, "Web-Scraping abgeschlossen", "info", agent_id, agent_name)
+
+ # API-Anfrage an OpenAI senden
+ response = await self._call_openai_api(full_prompt)
+
+ # Ergebnis extrahieren und speichern
+ result_content = response.get("choices", [{}])[0].get("message", {}).get("content", "Keine Antwort erhalten")
+
+ # Agent-Ergebnis erstellen
+ result = self._create_agent_result(workflow_id, agent, i, prompt, file_contexts, result_content)
+ self.workflows[workflow_id]["results"].append(result)
+
+ self._add_log(
+ workflow_id,
+ f"Agent '{agent_name}' hat die Verarbeitung abgeschlossen",
+ "complete",
+ agent_id,
+ agent_name
+ )
+
+ except Exception as e:
+ logger.error(f"Fehler bei der Ausführung von Agent '{agent_name}': {str(e)}")
+ self._add_log(
+ workflow_id,
+ f"Fehler bei der Ausführung: {str(e)}",
+ "error",
+ agent_id,
+ agent_name
+ )
+ self.workflows[workflow_id]["agent_statuses"][agent_id] = "failed"
+ continue
- self._add_log(
- workflow_id,
- f"Agent '{agent_name}' hat die Verarbeitung abgeschlossen",
- "complete",
- agent_id,
- agent_name
- )
self.workflows[workflow_id]["agent_statuses"][agent_id] = "completed"
-
self.workflows[workflow_id]["progress"] = 0.1 + (i + 1) * (0.8 / len(agents))
+
+ # Kurze Pause zwischen Agent-Aufrufen
await asyncio.sleep(1)
- # Workflow abschließen
- self._add_log(workflow_id, "Alle Agenten haben ihre Aufgaben abgeschlossen", "info")
- self._add_log(workflow_id, "Workflow erfolgreich beendet", "success")
+ # Workflow-Status prüfen
+ failed_agents = [a for a, s in self.workflows[workflow_id]["agent_statuses"].items() if s == "failed"]
+
+ if failed_agents:
+ self._add_log(workflow_id, f"{len(failed_agents)} Agent(s) sind fehlgeschlagen", "error")
+ self._add_log(workflow_id, "Workflow mit Fehlern beendet", "error")
+ self.workflows[workflow_id]["status"] = "failed"
+ else:
+ self._add_log(workflow_id, "Alle Agenten haben ihre Aufgaben abgeschlossen", "info")
+ self._add_log(workflow_id, "Workflow erfolgreich beendet", "success")
+ self.workflows[workflow_id]["status"] = "completed"
- self.workflows[workflow_id]["status"] = "completed"
self.workflows[workflow_id]["progress"] = 1.0
self.workflows[workflow_id]["completed_at"] = datetime.now().isoformat()
@@ -103,6 +367,253 @@ class AgentService:
return workflow_id
+ async def _scrape_web_data(self, workflow_id: str, prompt: str) -> str:
+ """
+ Führt Web-Scraping basierend auf dem Prompt durch
+ """
+ try:
+ # Extrahiere mögliche Schlüsselwörter oder URLs aus dem Prompt
+ keywords = self._extract_keywords(prompt)
+ urls = self._extract_urls(prompt)
+
+ results = []
+
+ # Falls direkte URLs im Prompt enthalten sind
+ if urls:
+ self._add_log(workflow_id, f"Gefundene URLs: {', '.join(urls[:self.scraping_max_urls])}", "info")
+ for url in urls[:self.scraping_max_urls]: # Begrenze auf max_urls (Standard: 3)
+ try:
+ self._add_log(workflow_id, f"Scrape URL: {url}", "info")
+ content = self._scrape_url(url)
+ if content:
+ results.append(f"## Inhalt von {url}\n{content}")
+ self._add_log(workflow_id, f"Scraping von {url} erfolgreich", "info")
+ except Exception as e:
+ logger.error(f"Fehler beim Scrapen von {url}: {e}")
+ self._add_log(workflow_id, f"Fehler beim Scrapen von {url}: {e}", "error")
+
+ # Falls keine URLs, versuche Suche mit Schlüsselwörtern
+ elif keywords:
+ self._add_log(workflow_id, f"Verwende Keywords für Suche: {keywords}", "info")
+ search_results = self._search_web(keywords)
+ if search_results:
+ results.append(f"## Suchergebnisse für: {keywords}\n{search_results}")
+ self._add_log(workflow_id, "Suche abgeschlossen", "info")
+
+ if results:
+ return "\n\n".join(results)
+
+ self._add_log(workflow_id, "Keine relevanten Web-Daten gefunden", "warning")
+ return "Keine relevanten Web-Daten gefunden."
+
+ except Exception as e:
+ logger.error(f"Fehler beim Web-Scraping: {e}")
+ self._add_log(workflow_id, f"Fehler beim Web-Scraping: {e}", "error")
+ return f"Web-Scraping konnte nicht durchgeführt werden: {str(e)}"
+
+ def _extract_keywords(self, text: str) -> str:
+ """Extrahiert Schlüsselwörter aus dem Text"""
+ # Einfache Implementierung - in der Praxis könntest du NLP verwenden
+ words = text.split()
+ # Filtere kurze Wörter und häufige Stopwörter
+ stopwords = ["einen", "einer", "eines", "keine", "nicht", "diese", "dieses", "zwischen",
+ "und", "oder", "aber", "denn", "wenn", "weil", "obwohl", "während", "für",
+ "mit", "von", "aus", "nach", "bei", "über", "unter", "durch", "gegen"]
+ keywords = [w for w in words if len(w) > 4 and w.lower() not in stopwords]
+ return " ".join(keywords[:5]) # Begrenze auf 5 Keywords
+
+ def _extract_urls(self, text: str) -> List[str]:
+ """Extrahiert URLs aus dem Text"""
+ # Einfacher URL-Extraktions-Regex
+ url_pattern = re.compile(r'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+[/\w\.-]*(?:\?\S+)?')
+ return url_pattern.findall(text)
+
+ def _scrape_url(self, url: str) -> str:
+ """Scrapt den Inhalt einer URL"""
+ headers = {
+ 'User-Agent': self.scraping_user_agent
+ }
+
+ response = requests.get(url, headers=headers, timeout=self.scraping_timeout)
+ response.raise_for_status()
+
+ soup = BeautifulSoup(response.text, 'html.parser')
+
+ # Entferne Skripte, Styles und andere unwichtige Elemente
+ for script in soup(["script", "style", "meta", "noscript", "iframe"]):
+ script.extract()
+
+ # Extrahiere den Hauptinhalt
+ main_content = ""
+
+ # Versuche, Hauptcontainer zu finden (häufige IDs und Klassen)
+ main_elements = soup.select('main, #main, .main, #content, .content, article, .article, .post, #post')
+
+ if main_elements:
+ # Nehme den ersten gefundenen Hauptcontainer
+ main_content = main_elements[0].get_text(separator='\n', strip=True)
+ else:
+ # Falls kein Hauptcontainer gefunden, nehme den Body-Text
+ main_content = soup.body.get_text(separator='\n', strip=True)
+
+ # Bereinige den Text (entferne mehrfache Leerzeilen etc.)
+ lines = [line.strip() for line in main_content.split('\n') if line.strip()]
+ main_content = '\n'.join(lines)
+
+ # Begrenze die Länge
+ if len(main_content) > self.scraping_max_content_length:
+ main_content = main_content[:self.scraping_max_content_length] + "...\n[Inhalt gekürzt]"
+
+ return main_content
+
+ def _search_web(self, query: str) -> str:
+ """
+ Simuliert eine Websuche (in einer vollständigen Implementierung
+ würdest du hier eine echte Suchmaschinen-API verwenden)
+ """
+ # HINWEIS: Dies ist eine Simulation! In einer echten Anwendung
+ # würdest du Google Custom Search API, SerpAPI oder ähnliches verwenden
+
+ # Für eine echte Implementierung:
+ # - Google Custom Search API: https://developers.google.com/custom-search/v1/overview
+ # - SerpAPI: https://serpapi.com/
+ # - Oder ähnliche Dienste
+
+ return f"Hinweis: Dies ist eine Demo-Implementierung ohne echte Websuche. In der Produktion würde hier der Agent tatsächlich nach '{query}' suchen."
+
+ async def _call_openai_api(self, prompt: str) -> Dict[str, Any]:
+ """Ruft die OpenAI API auf und gibt die Antwort zurück"""
+ try:
+ payload = {
+ "model": self.config["openai"]["model_name"],
+ "messages": [
+ {"role": "system", "content": "Du bist ein spezialisierter Agent in einem Multi-Agent-System zur Datenanalyse und -verarbeitung."},
+ {"role": "user", "content": prompt}
+ ],
+ "temperature": 0.2, # Niedrige Temperatur für konsistentere Ergebnisse
+ "max_tokens": 2000
+ }
+
+ response = await self.http_client.post(
+ self.config["openai"]["api_url"],
+ json=payload
+ )
+
+ if response.status_code != 200:
+ logger.error(f"OpenAI API-Fehler: {response.status_code} - {response.text}")
+ raise HTTPException(status_code=500, detail="Fehler bei der Kommunikation mit OpenAI API")
+
+ return response.json()
+
+ except Exception as e:
+ logger.error(f"Fehler beim Aufruf der OpenAI API: {str(e)}")
+ raise HTTPException(status_code=500, detail=f"Fehler beim Aufruf der OpenAI API: {str(e)}")
+
+ def _get_agent_instructions(self, agent_type: str) -> str:
+ """
+ Gibt agententypspezifische Anweisungen zurück, die aus der agents.json geladen werden.
+ Falls die Datei nicht existiert oder der Agententyp nicht gefunden wird,
+ werden Standard-Anweisungen zurückgegeben.
+ """
+ try:
+ # Pfad zur agents.json-Datei
+ agents_file = os.path.join(os.path.dirname(__file__), 'data', 'agents.json')
+
+ # Überprüfen, ob die Datei existiert
+ if not os.path.exists(agents_file):
+ logger.warning(f"Agents-Definitionen nicht gefunden: {agents_file}")
+ return self._get_default_agent_instructions(agent_type)
+
+ # Datei lesen
+ with open(agents_file, 'r', encoding='utf-8') as f:
+ agents_data = json.load(f)
+
+ # Nach dem Agententyp suchen
+ for agent in agents_data:
+ if agent.get("type") == agent_type:
+ # Anweisungen zurückgeben, wenn vorhanden
+ instructions = agent.get("instructions")
+ if instructions:
+ logger.debug(f"Anweisungen für Agent-Typ '{agent_type}' aus agents.json geladen")
+ return instructions
+
+ # Wenn kein passender Agent gefunden wurde, Standardanweisungen verwenden
+ logger.warning(f"Keine Anweisungen für Agent-Typ '{agent_type}' in agents.json gefunden")
+ return self._get_default_agent_instructions(agent_type)
+
+ except Exception as e:
+ logger.error(f"Fehler beim Laden der Agent-Anweisungen aus agents.json: {e}")
+ return self._get_default_agent_instructions(agent_type)
+
+ def _get_default_agent_instructions(self, agent_type: str) -> str:
+ """
+ Gibt Standard-Anweisungen für einen Agententyp zurück,
+ wenn keine spezifischen Anweisungen in der agents.json gefunden wurden.
+ """
+ default_instructions = {
+ "analyzer": """
+ Als Datenanalyse-Agent ist es deine Aufgabe, die bereitgestellten Daten zu analysieren und wichtige Erkenntnisse zu extrahieren.
+
+ Folge diesen Anweisungen zur Analyse der Dateien:
+ 1. Lese und verstehe den Inhalt der bereitgestellten Dateien gründlich
+ 2. Identifiziere welchen Datentyp jede Datei enthält (z.B. Zeitreihendaten, kategorische Daten, Text)
+ 3. Wenn es sich um tabellarische Daten handelt:
+ - Identifiziere Muster, Trends und Anomalien
+ - Berechne relevante statistische Kennzahlen (Mittelwerte, Mediane, Standardabweichungen)
+ - Suche nach Korrelationen zwischen verschiedenen Spalten
+ - Identifiziere Ausreißer und ungewöhnliche Datenpunkte
+ 4. Wenn es sich um Textdaten handelt:
+ - Analysiere Schlüsselthemen und -begriffe
+ - Identifiziere Stimmung und Tonalität, wenn relevant
+ - Extrahiere zentrale Aussagen und Schlussfolgerungen
+ 5. Erstelle eine strukturierte Zusammenfassung deiner Erkenntnisse
+ 6. Gib konkrete, datengestützte Empfehlungen, wenn möglich
+
+ In deiner Antwort:
+ - Beginne mit einer kurzen Übersicht der analysierten Daten
+ - Struktur
+ - Strukturiere deine Erkenntnisse klar mit Überschriften und Aufzählungen
+ - Füge quantitative Erkenntnisse ein, wo immer möglich
+ - Schließe mit einer Zusammenfassung der wichtigsten Punkte ab
+ """,
+ "visualizer": """
+ Als Visualisierungs-Agent ist es deine Aufgabe, die Daten visuell zu beschreiben.
+ - Beschreibe, welche Art von Visualisierungen für die Daten sinnvoll wären
+ - Erkläre das Layout und die Komponenten der Visualisierung
+ - Beschreibe, wie die Daten grafisch dargestellt werden sollten
+ - Erkläre, welche Erkenntnisse aus dieser Visualisierung gewonnen werden können
+ """,
+ "writer": """
+ Als Text-Generator ist es deine Aufgabe, verständliche Berichte und Zusammenfassungen zu erstellen.
+ - Fasse die wichtigsten Erkenntnisse aus den Daten zusammen
+ - Strukturiere den Bericht klar und prägnant
+ - Verwende eine sachliche und professionelle Sprache
+ - Formuliere konkrete Empfehlungen basierend auf den Daten
+ - Nutze Markdown für bessere Formatierung
+ """,
+ "scraper": """
+ Als Web-Scraper-Agent ist es deine Aufgabe, Webseiten zu durchsuchen und relevante Informationen zu extrahieren.
+
+ Folge diesen Anweisungen:
+ 1. Analysiere die bereitgestellten Web-Daten sorgfältig
+ 2. Extrahiere die wichtigsten Informationen und Fakten aus den Webinhalten
+ 3. Organisiere die Informationen in klare Kategorien
+ 4. Identifiziere Trends, Muster und wichtige Erkenntnisse
+ 5. Vergleiche die Informationen aus verschiedenen Quellen, wenn verfügbar
+ 6. Fasse die gefundenen Informationen prägnant zusammen
+ 7. Erkenne mögliche Einschränkungen oder Verzerrungen in den Quellen
+
+ In deiner Antwort:
+ - Beginne mit einer Zusammenfassung der gefundenen Informationen
+ - Strukturiere die extrahierten Daten in logische Abschnitte
+ - Hebe wichtige Fakten und Zahlen hervor
+ - Gib Quellenhinweise an
+ - Formuliere Schlussfolgerungen basierend auf den gesammelten Daten
+ """
+ }
+
+ return default_instructions.get(agent_type, "Du bist ein Datenverarbeitungs-Agent. Analysiere die gegebenen Informationen und liefere relevante Erkenntnisse.")
+
def _add_log(
self,
workflow_id: str,
@@ -126,76 +637,59 @@ class AgentService:
workflow["logs"].append(log_entry)
logger.info(f"Workflow {workflow_id}: {message}")
- def _generate_simulated_result(
+ def _create_agent_result(
self,
workflow_id: str,
agent: Dict[str, Any],
index: int,
prompt: str,
- files: List[Dict[str, Any]]
+ file_contexts: List[Dict[str, Any]],
+ content: str
) -> Dict[str, Any]:
- """Generiert ein simuliertes Ergebnis basierend auf dem Agententyp"""
+ """Erstellt ein Ergebnisobjekt basierend auf dem Agententyp und der API-Antwort"""
agent_type = agent["type"]
agent_id = agent["id"]
agent_name = agent["name"]
- # Dateien als Teil des Kontexts einbinden
- file_names = [f["name"] for f in files]
- file_context = f"Basierend auf der Analyse von: {', '.join(file_names)}"
-
+ # Grundlegende Ergebnisstruktur
result = {
"id": f"result_{workflow_id}_{index}",
"agent_id": agent_id,
"agent_name": agent_name,
"timestamp": datetime.now().isoformat(),
+ "type": "text", # Standardtyp
"metadata": {
- "files_processed": file_names,
+ "files_processed": [file["name"] for file in file_contexts],
"prompt": prompt
}
}
+ # Titel und Inhalt basierend auf dem Agententyp anpassen
if agent_type == "analyzer":
result.update({
"title": "Datenanalyse-Ergebnis",
- "type": "text",
- "content": f"{file_context}\n\nDie Analyse der bereitgestellten Daten zeigt folgende Schlüsselerkenntnisse:\n\n1. Die Umsätze stiegen im Q1 2025 um 12% im Vergleich zum Vorjahr\n2. Produktkategorie A zeigt die stärkste Leistung mit 23% Wachstum\n3. In den Regionen Nord und West wurde das größte Wachstum beobachtet\n4. Die Betriebskosten sind um 5% gesunken, was zu einer verbesserten Marge führt"
+ "content": content,
})
elif agent_type == "visualizer":
result.update({
- "title": "Umsatzentwicklung nach Quartal",
- "type": "chart",
- "content": "Diagramm der Umsatzentwicklung (simuliert)",
- "metadata": {
- **result["metadata"],
- "chart_type": "line",
- "chart_data": {
- "labels": ["Q1 2024", "Q2 2024", "Q3 2024", "Q4 2024", "Q1 2025"],
- "datasets": [
- {
- "label": "Umsatz (Mio. €)",
- "data": [2.1, 2.4, 2.8, 3.2, 3.6]
- }
- ]
- }
- }
+ "title": "Visualisierungsvorschlag",
+ "content": content,
+ "type": "chart" # Auch wenn kein echtes Diagramm, markieren wir es als solches
})
elif agent_type == "writer":
result.update({
"title": "Zusammenfassung und Empfehlungen",
- "type": "text",
- "content": f"{file_context}\n\n# Zusammenfassung Q1 2025\n\nDie Unternehmensdaten zeigen ein starkes erstes Quartal mit signifikanten Verbesserungen in allen Geschäftsbereichen. Der Gesamtumsatz stieg um 12%, während die Betriebskosten um 5% sanken, was zu einer verbesserten Gewinnmarge führte.\n\n## Empfehlungen\n\n1. **Investitionen in Produktkategorie A ausbauen** - Diese Kategorie zeigt das stärkste Wachstum und sollte weiter gefördert werden\n2. **Marketingaktivitäten in Süd- und Ostregionen verstärken** - Diese Regionen zeigen Potenzial für Wachstum\n3. **Kostenoptimierungen beibehalten** - Die Maßnahmen zur Kostensenkung haben sich als effektiv erwiesen\n\n## Prognose für Q2 2025\n\nBei gleichbleibenden Marktbedingungen erwarten wir ein weiteres Wachstum von 8-10% im Q2 2025."
+ "content": content,
})
elif agent_type == "scraper":
result.update({
- "title": "Web-Scraping Ergebnisse",
- "type": "text",
- "content": f"# Marktdaten aus Web-Quellen\n\nBasierend auf der Analyse von 15 führenden Branchenwebsites wurden folgende Trends identifiziert:\n\n1. Der Gesamtmarkt wächst mit einer Rate von 8,5% jährlich\n2. Hauptwettbewerber A und B haben kürzlich neue Produktlinien gestartet\n3. Die durchschnittlichen Marktpreise sind in den letzten 6 Monaten um 3,2% gestiegen"
+ "title": "Web-Recherche Ergebnisse",
+ "content": content,
})
else:
result.update({
- "title": "Verarbeitungsergebnis",
- "type": "text",
- "content": f"Allgemeines Ergebnis der Verarbeitung durch {agent_name}"
+ "title": f"Ergebnis von {agent_name}",
+ "content": content,
})
return result
@@ -242,6 +736,10 @@ class AgentService:
return None
return workflow["results"]
+
+ async def close(self):
+ """Schließt den HTTP-Client beim Beenden der Anwendung"""
+ await self.http_client.aclose()
# Singleton-Instanz des AgentService
diff --git a/backend/app.py b/backend/app.py
index 458dce16..571d66a2 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -1,6 +1,7 @@
+import uvicorn
from fastapi import FastAPI, File, UploadFile, HTTPException, Depends, Body, Query
from fastapi.middleware.cors import CORSMiddleware
-from fastapi.responses import JSONResponse
+from fastapi.responses import JSONResponse, Response
from fastapi.staticfiles import StaticFiles
from typing import List, Dict, Any, Optional
import uuid
@@ -42,28 +43,35 @@ app.add_middleware(
allow_headers=["*"],
)
-# Statische Dateien (für Frontend)
-app.mount("/static", StaticFiles(directory="static"), name="static")
+
+# Pfad zum Webparts-Verzeichnis
+WEBPARTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "webparts")
# Verzeichnis für hochgeladene Dateien erstellen
UPLOAD_DIR = os.path.join(os.getcwd(), "uploads")
os.makedirs(UPLOAD_DIR, exist_ok=True)
+# Komponenten des Frontends
+app.mount("/webparts", StaticFiles(directory="webparts"), name="webparts")
@app.get("/", tags=["General"])
async def root():
"""API-Statusendpunkt"""
return {"status": "online", "message": "Data Platform API ist aktiv"}
+# Test Element
+@app.get("/api/test", tags=["Test"])
+async def get_test():
+ return "OK 1.0"
# Workspace-Endpunkte
-@app.get("/workspaces", tags=["Workspaces"], response_model=List[Workspace])
+@app.get("/api/workspaces", tags=["Workspaces"], response_model=List[Workspace])
async def get_workspaces(db: Database = Depends(get_db)):
"""Alle verfügbaren Workspaces abrufen"""
return db.get_all_workspaces()
-@app.get("/workspaces/{workspace_id}", tags=["Workspaces"], response_model=Workspace)
+@app.get("/api/workspaces/{workspace_id}", tags=["Workspaces"], response_model=Workspace)
async def get_workspace(workspace_id: str, db: Database = Depends(get_db)):
"""Einen bestimmten Workspace mit allen Details abrufen"""
workspace = db.get_workspace(workspace_id)
@@ -72,7 +80,7 @@ async def get_workspace(workspace_id: str, db: Database = Depends(get_db)):
return workspace
-@app.post("/workspaces", tags=["Workspaces"], response_model=Workspace)
+@app.post("/api/workspaces", tags=["Workspaces"], response_model=Workspace)
async def create_workspace(workspace: Dict[str, Any] = Body(...), db: Database = Depends(get_db)):
"""Einen neuen Workspace erstellen"""
workspace_id = str(uuid.uuid4())
@@ -89,8 +97,8 @@ async def create_workspace(workspace: Dict[str, Any] = Body(...), db: Database =
return new_workspace
-# Agent-Endpunkte
-@app.get("/agents", tags=["Agents"], response_model=List[Agent])
+# Agenten-Endpunkte
+@app.get("/api/agents", tags=["Agents"], response_model=List[Agent])
async def get_agents(workspace_id: Optional[str] = Query(None), db: Database = Depends(get_db)):
"""Alle Agenten oder Agenten eines bestimmten Workspaces abrufen"""
if workspace_id:
@@ -98,7 +106,7 @@ async def get_agents(workspace_id: Optional[str] = Query(None), db: Database = D
return db.get_all_agents()
-@app.post("/agents", tags=["Agents"], response_model=Agent)
+@app.post("/api/agents", tags=["Agents"], response_model=Agent)
async def create_agent(
agent: Dict[str, Any] = Body(...),
db: Database = Depends(get_db)
@@ -128,13 +136,13 @@ async def create_agent(
# Datei-Endpunkte
-@app.get("/files", tags=["Files"], response_model=List[DataObject])
+@app.get("/api/files", tags=["Files"], response_model=List[DataObject])
async def get_files(db: Database = Depends(get_db)):
"""Alle verfügbaren Dateien abrufen"""
return db.get_all_files()
-@app.post("/files/upload", tags=["Files"])
+@app.post("/api/files/upload", tags=["Files"])
async def upload_file(
file: UploadFile = File(...),
db: Database = Depends(get_db)
@@ -180,7 +188,7 @@ async def upload_file(
# Prompt-Endpunkte
-@app.get("/prompts", tags=["Prompts"], response_model=List[Prompt])
+@app.get("/api/prompts", tags=["Prompts"], response_model=List[Prompt])
async def get_prompts(workspace_id: Optional[str] = Query(None), db: Database = Depends(get_db)):
"""Alle Prompts oder Prompts eines bestimmten Workspaces abrufen"""
if workspace_id:
@@ -188,7 +196,7 @@ async def get_prompts(workspace_id: Optional[str] = Query(None), db: Database =
return db.get_all_prompts()
-@app.post("/prompts", tags=["Prompts"], response_model=Prompt)
+@app.post("/api/prompts", tags=["Prompts"], response_model=Prompt)
async def create_prompt(
prompt: Dict[str, Any] = Body(...),
db: Database = Depends(get_db)
@@ -216,7 +224,7 @@ async def create_prompt(
# Workflow-Endpunkte
-@app.post("/workflow/run", tags=["Workflow"], response_model=WorkflowResponse)
+@app.post("/api/workflow/run", tags=["Workflow"], response_model=WorkflowResponse)
async def run_workflow(
workflow_request: WorkflowRequest,
db: Database = Depends(get_db),
@@ -264,7 +272,7 @@ async def run_workflow(
)
-@app.get("/workflow/{workflow_id}/status", tags=["Workflow"])
+@app.get("/api/workflow/{workflow_id}/status", tags=["Workflow"])
async def get_workflow_status(
workflow_id: str,
agent_service: AgentService = Depends(get_agent_service)
@@ -276,7 +284,7 @@ async def get_workflow_status(
return status
-@app.get("/workflow/{workflow_id}/logs", tags=["Workflow"], response_model=List[LogEntry])
+@app.get("/api/workflow/{workflow_id}/logs", tags=["Workflow"], response_model=List[LogEntry])
async def get_workflow_logs(
workflow_id: str,
agent_service: AgentService = Depends(get_agent_service)
@@ -288,7 +296,7 @@ async def get_workflow_logs(
return logs
-@app.get("/workflow/{workflow_id}/results", tags=["Workflow"], response_model=List[Result])
+@app.get("/api/workflow/{workflow_id}/results", tags=["Workflow"], response_model=List[Result])
async def get_workflow_results(
workflow_id: str,
agent_service: AgentService = Depends(get_agent_service)
@@ -301,5 +309,4 @@ async def get_workflow_results(
if __name__ == "__main__":
- import uvicorn
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
\ No newline at end of file
diff --git a/backend/config.ini b/backend/config.ini
new file mode 100644
index 00000000..90ffd97e
--- /dev/null
+++ b/backend/config.ini
@@ -0,0 +1,15 @@
+[OpenAI]
+API_KEY = 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
+API_URL = https://api.openai.com/v1/chat/completions
+MODEL_NAME = gpt-4o
+
+[Application]
+DEBUG = True
+UPLOAD_DIR = ./uploads
+RESULTS_DIR = ./results
+
+[WebScraping]
+TIMEOUT = 10
+MAX_URLS = 3
+MAX_CONTENT_LENGTH = 3000
+USER_AGENT = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
\ No newline at end of file
diff --git a/backend/data/.gitignore b/backend/data/.gitignore
deleted file mode 100644
index 86d0cb27..00000000
--- a/backend/data/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-# Ignore everything in this directory
-*
-# Except this file
-!.gitignore
\ No newline at end of file
diff --git a/backend/data/agents.json b/backend/data/agents.json
new file mode 100644
index 00000000..a48dff00
--- /dev/null
+++ b/backend/data/agents.json
@@ -0,0 +1,47 @@
+[
+ {
+ "id": "agent_001",
+ "name": "Datenanalyse-Agent",
+ "type": "analyzer",
+ "workspace_id": "workspace_001",
+ "capabilities": ["Datenanalyse", "Statistik", "Trendanalyse"],
+ "description": "Spezialisiert auf die Analyse von strukturierten Daten und Informationen.",
+ "instructions": "Als Datenanalyse-Agent ist es deine Aufgabe, die bereitgestellten Daten zu analysieren und wichtige Erkenntnisse zu extrahieren.\n\nFolge diesen Anweisungen zur Analyse der Dateien:\n1. Lese und verstehe den Inhalt der bereitgestellten Dateien gründlich\n2. Identifiziere welchen Datentyp jede Datei enthält (z.B. Zeitreihendaten, kategorische Daten, Text)\n3. Wenn es sich um tabellarische Daten handelt:\n - Identifiziere Muster, Trends und Anomalien\n - Berechne relevante statistische Kennzahlen (Mittelwerte, Mediane, Standardabweichungen)\n - Suche nach Korrelationen zwischen verschiedenen Spalten\n - Identifiziere Ausreißer und ungewöhnliche Datenpunkte\n4. Wenn es sich um Textdaten handelt:\n - Analysiere Schlüsselthemen und -begriffe\n - Identifiziere Stimmung und Tonalität, wenn relevant\n - Extrahiere zentrale Aussagen und Schlussfolgerungen\n5. Erstelle eine strukturierte Zusammenfassung deiner Erkenntnisse\n6. Gib konkrete, datengestützte Empfehlungen, wenn möglich\n\nIn deiner Antwort:\n- Beginne mit einer kurzen Übersicht der analysierten Daten\n- Strukturiere deine Erkenntnisse klar mit Überschriften und Aufzählungen\n- Füge quantitative Erkenntnisse ein, wo immer möglich\n- Schließe mit einer Zusammenfassung der wichtigsten Punkte ab"
+ },
+ {
+ "id": "agent_002",
+ "name": "Visualisierungs-Agent",
+ "type": "visualizer",
+ "workspace_id": "workspace_001",
+ "capabilities": ["Datenvisualisierung", "Diagrammerstellung"],
+ "description": "Erstellt visuelle Darstellungen aus Daten",
+ "instructions": "Als Visualisierungs-Agent ist es deine Aufgabe, die Daten visuell zu beschreiben.\n\nFolge diesen Anweisungen:\n1. Analysiere die bereitgestellten Daten und identifiziere die wichtigsten Aspekte für eine Visualisierung\n2. Empfehle geeignete Visualisierungstypen basierend auf der Art der Daten:\n - Für zeitliche Verläufe: Liniendiagramme, Area-Charts\n - Für Kategorienvergleiche: Balkendiagramme, gestapelte Balken\n - Für Verteilungen: Histogramme, Box-Plots\n - Für Anteile: Kreisdiagramme, Donut-Charts\n - Für Beziehungen: Streudiagramme, Heatmaps\n3. Beschreibe detailliert, wie die Visualisierung aufgebaut werden sollte:\n - Welche Daten auf welcher Achse abgebildet werden sollten\n - Farben und ihre Bedeutung\n - Beschriftungen und Legenden\n - Skalen und deren Anpassung\n4. Erkläre, welche Erkenntnisse die Visualisierung vermitteln würde\n5. Gib Empfehlungen für Interaktionsmöglichkeiten oder zusätzliche Visualisierungen"
+ },
+ {
+ "id": "agent_003",
+ "name": "Text-Generator",
+ "type": "writer",
+ "workspace_id": "workspace_001",
+ "capabilities": ["Texterstellung", "Zusammenfassung"],
+ "description": "Verfasst verständliche Berichte und Zusammenfassungen",
+ "instructions": "Als Text-Generator ist es deine Aufgabe, verständliche Berichte und Zusammenfassungen zu erstellen.\n\nFolge diesen Anweisungen:\n1. Destilliere die wichtigsten Erkenntnisse aus den bereitgestellten Daten und Analysen\n2. Strukturiere den Bericht logisch mit:\n - Einer prägnanten Einleitung mit Kontext und Haupterkenntnissen\n - Einem detaillierten Hauptteil, der die Erkenntnisse systematisch präsentiert\n - Einem Abschluss mit Zusammenfassung und Handlungsempfehlungen\n3. Verwende eine klare, präzise und faktenbasierte Sprache\n4. Integriere Zahlen und Daten direkt in den Text, um Aussagen zu untermauern\n5. Nutze Markdown für eine bessere Formatierung:\n - Überschriften für verschiedene Abschnitte\n - Aufzählungspunkte für Listen\n - Hervorhebungen für wichtige Punkte\n - Tabellen für strukturierte Daten"
+ },
+ {
+ "id": "agent_004",
+ "name": "Web-Scraper",
+ "type": "scraper",
+ "workspace_id": "workspace_002",
+ "capabilities": ["Datensammlung", "Webanalyse", "Echtzeit-Recherche"],
+ "description": "Sammelt und analysiert Daten aus Webquellen in Echtzeit",
+ "instructions": "Als Web-Scraper-Agent ist es deine Aufgabe, Webseiten zu durchsuchen und relevante Informationen zu extrahieren.\n\nFolge diesen Anweisungen:\n1. Analysiere die bereitgestellten Web-Daten sorgfältig\n2. Extrahiere die wichtigsten Informationen und Fakten aus den Webinhalten\n3. Organisiere die Informationen in klare Kategorien\n4. Identifiziere Trends, Muster und wichtige Erkenntnisse\n5. Vergleiche die Informationen aus verschiedenen Quellen, wenn verfügbar\n6. Fasse die gefundenen Informationen prägnant zusammen\n7. Erkenne mögliche Einschränkungen oder Verzerrungen in den Quellen\n\nIn deiner Antwort:\n- Beginne mit einer Zusammenfassung der gefundenen Informationen\n- Strukturiere die extrahierten Daten in logische Abschnitte\n- Hebe wichtige Fakten und Zahlen hervor\n- Gib Quellenhinweise an\n- Formuliere Schlussfolgerungen basierend auf den gesammelten Daten"
+ },
+ {
+ "id": "agent_005",
+ "name": "Marktanalyse-Agent",
+ "type": "analyzer",
+ "workspace_id": "workspace_002",
+ "capabilities": ["Wettbewerbsanalyse", "Markttrends"],
+ "description": "Spezialisiert auf Wettbewerbsanalyse und Markttrends",
+ "instructions": "Als Marktanalyse-Agent ist es deine Aufgabe, Wettbewerbsdaten und Markttrends zu analysieren.\n\nFolge diesen Anweisungen:\n1. Identifiziere die wichtigsten Wettbewerber und ihre Positionierung im Markt\n2. Analysiere Markttrends und -entwicklungen:\n - Wachstumsraten und Marktgrößen\n - Veränderungen im Konsumentenverhalten\n - Technologische Entwicklungen\n - Regulatorische Änderungen\n3. Vergleiche Produkte und Dienstleistungen nach relevanten Kriterien\n4. Identifiziere Stärken, Schwächen, Chancen und Risiken (SWOT-Analyse)\n5. Erstelle eine fundierte Einschätzung der Marktposition\n6. Entwickle strategische Empfehlungen basierend auf der Marktanalyse"
+ }
+]
\ No newline at end of file
diff --git a/backend/data/files.json b/backend/data/files.json
new file mode 100644
index 00000000..547c68f6
--- /dev/null
+++ b/backend/data/files.json
@@ -0,0 +1,65 @@
+[
+ {
+ "id": "file_001",
+ "name": "Quartalsbericht Q1 2025.pdf",
+ "type": "document",
+ "content_type": "application/pdf",
+ "size": 2500000,
+ "upload_date": "2025-03-14T00:25:25.076526",
+ "path": "uploads/dummy_file1.pdf"
+ },
+ {
+ "id": "file_002",
+ "name": "Marktanalyse-Diagramm.png",
+ "type": "image",
+ "content_type": "image/png",
+ "size": 1150000,
+ "upload_date": "2025-03-14T00:25:25.076526",
+ "path": "uploads/dummy_file2.png"
+ },
+ {
+ "id": "file_003",
+ "name": "Finanzdaten_2024-2025.xlsx",
+ "type": "document",
+ "content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ "size": 3880000,
+ "upload_date": "2025-03-14T00:25:25.076526",
+ "path": "uploads/dummy_file3.xlsx"
+ },
+ {
+ "id": "77bb2097-7044-4267-aae6-f2ca1c119ab8",
+ "name": "11 PowerOn HLD - Journey.pdf",
+ "type": "document",
+ "path": "D:\\Athi\\Local\\Web\\git-apps\\gateway\\backend\\uploads\\77bb2097-7044-4267-aae6-f2ca1c119ab8.pdf",
+ "content_type": "application/pdf",
+ "size": 56687,
+ "upload_date": "2025-03-14T00:25:55.641089"
+ },
+ {
+ "id": "f066b3d6-7d59-4851-a8eb-de4334f4a64e",
+ "name": "11 PowerOn HLD - Journey.pdf",
+ "type": "document",
+ "path": "D:\\Athi\\Local\\Web\\git-apps\\gateway\\backend\\uploads\\f066b3d6-7d59-4851-a8eb-de4334f4a64e.pdf",
+ "content_type": "application/pdf",
+ "size": 56687,
+ "upload_date": "2025-03-14T09:46:37.306858"
+ },
+ {
+ "id": "bb36ca5e-cae0-441d-b59c-feb80ecf6d51",
+ "name": "11 PowerOn HLD - Journey.pdf",
+ "type": "document",
+ "path": "D:\\Athi\\Local\\Web\\git-apps\\gateway\\backend\\uploads\\bb36ca5e-cae0-441d-b59c-feb80ecf6d51.pdf",
+ "content_type": "application/pdf",
+ "size": 56687,
+ "upload_date": "2025-03-14T10:53:08.043663"
+ },
+ {
+ "id": "a40ec285-afab-45fe-9668-ba99d4da3a70",
+ "name": "ReportingSheet2025.xlsx",
+ "type": "document",
+ "path": "D:\\Athi\\Local\\Web\\git-apps\\gateway\\backend\\uploads\\a40ec285-afab-45fe-9668-ba99d4da3a70.xlsx",
+ "content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ "size": 14727,
+ "upload_date": "2025-03-14T13:55:15.194910"
+ }
+]
\ No newline at end of file
diff --git a/backend/data/prompts.json b/backend/data/prompts.json
new file mode 100644
index 00000000..4d0dafda
--- /dev/null
+++ b/backend/data/prompts.json
@@ -0,0 +1,14 @@
+[
+ {
+ "id": "prompt_001",
+ "content": "Analysiere die Quartalsdaten und erstelle eine Zusammenfassung mit wichtigsten Trends",
+ "workspace_id": "workspace_001",
+ "created_at": "2025-03-14T00:25:25.076526"
+ },
+ {
+ "id": "prompt_002",
+ "content": "Erstelle eine Prognose für das nächste Quartal basierend auf historischen Daten",
+ "workspace_id": "workspace_001",
+ "created_at": "2025-03-14T00:25:25.076526"
+ }
+]
\ No newline at end of file
diff --git a/backend/data/workspaces.json b/backend/data/workspaces.json
new file mode 100644
index 00000000..c477adb1
--- /dev/null
+++ b/backend/data/workspaces.json
@@ -0,0 +1,18 @@
+[
+ {
+ "id": "workspace_001",
+ "name": "Datenanalyse-Projekt",
+ "created_at": "2025-03-14T00:25:25.076526",
+ "prompts": [],
+ "agents": [],
+ "dataObjectReferences": []
+ },
+ {
+ "id": "workspace_002",
+ "name": "Marktforschung",
+ "created_at": "2025-03-14T00:25:25.076526",
+ "prompts": [],
+ "agents": [],
+ "dataObjectReferences": []
+ }
+]
\ No newline at end of file
diff --git a/backend/database.py b/backend/database.py
index 32c8281f..d29c026b 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -141,7 +141,7 @@ class Database:
"created_at": datetime.now().isoformat()
}
])
-
+
def _load_data(self, file_path):
"""Lädt Daten aus einer JSON-Datei"""
try:
diff --git a/backend/models.py b/backend/models.py
index b3ba9909..5e1438c0 100644
--- a/backend/models.py
+++ b/backend/models.py
@@ -7,8 +7,10 @@ class Agent(BaseModel):
id: str
name: str
type: str
+ workspace_id: str
capabilities: List[str] = []
description: Optional[str] = None
+ instructions: Optional[str] = None
class DataObject(BaseModel):
"""Datenmodell für ein Datenobjekt"""
@@ -33,7 +35,7 @@ class Workspace(BaseModel):
created_at: str
prompts: List[Prompt] = []
agents: List[Agent] = []
- dataObjectReferences: List[str] = []
+ dataObjectReferences: List[str] = []
class WorkflowRequest(BaseModel):
"""Anforderung zur Ausführung eines Workflows"""
diff --git a/backend/static/.gitignore b/backend/static/.gitignore
deleted file mode 100644
index 86d0cb27..00000000
--- a/backend/static/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-# Ignore everything in this directory
-*
-# Except this file
-!.gitignore
\ No newline at end of file
diff --git a/backend/webparts/part-agents.html b/backend/webparts/part-agents.html
new file mode 100644
index 00000000..893642bb
--- /dev/null
+++ b/backend/webparts/part-agents.html
@@ -0,0 +1,15 @@
+
+
diff --git a/backend/webparts/part-agents.js b/backend/webparts/part-agents.js
new file mode 100644
index 00000000..2eaaf3ce
--- /dev/null
+++ b/backend/webparts/part-agents.js
@@ -0,0 +1,204 @@
+(function() {
+ // DOM-Elemente
+ let addAgentBtn, agentsListContainer;
+
+ // Initialisierungsfunktion für das Agenten-Modul
+ function initAgentsModule(globalState) {
+ // DOM-Elemente referenzieren
+ addAgentBtn = document.getElementById('add-agent-btn');
+ agentsListContainer = document.getElementById('agents-list-container');
+
+ // Event-Listener hinzufügen
+ setupEventListeners(globalState);
+
+ // Initialdaten rendern
+ renderMyAgents(globalState);
+ }
+
+ // Event-Listener einrichten
+ function setupEventListeners(globalState) {
+ // Neuen Agenten hinzufügen
+ addAgentBtn.addEventListener('click', () => {
+ openAgentCreationModal(globalState);
+ });
+ }
+
+ // Modal zum Erstellen eines neuen Agenten öffnen
+ function openAgentCreationModal(globalState) {
+ // Einfaches Prompt als Platzhalter - in einer realen Anwendung würde dies
+ // durch ein ansprechendes Modal oder Formular ersetzt
+ const agentName = prompt('Name des neuen Agenten:');
+ const agentType = prompt('Typ des Agenten (z.B. Analyse, Transformation):');
+ const agentDescription = prompt('Beschreibung des Agenten:');
+
+ if (agentName && agentType) {
+ const newAgent = {
+ id: `agent_${Date.now()}`,
+ name: agentName,
+ type: agentType,
+ description: agentDescription || '',
+ workspace_id: globalState.currentWorkspace?.id,
+ capabilities: []
+ };
+
+ // Agent zur globalen Liste hinzufügen
+ globalState.availableAgents.push(newAgent);
+
+ // UI aktualisieren
+ renderMyAgents(globalState);
+
+ // Optional: Agent speichern
+ saveAgent(newAgent, globalState);
+ }
+ }
+
+ // Meine Agenten rendern
+ function renderMyAgents(globalState) {
+ console.info()
+ if (!agentsListContainer) {
+ console.error("No agents container")
+ return;
+ }
+
+ agentsListContainer.innerHTML = '';
+
+ // Agenten für den aktuellen Workspace filtern
+ const workspaceAgents = globalState.availableAgents.filter(
+ agent => agent.workspace_id === globalState.currentWorkspace.id
+ );
+ // console.info("debug: agentid-wsid: "+ (globalState.availableAgents[0].workspace_id) + " - " + globalState.currentWorkspace.id + ", wsAgents: " + workspaceAgents.length)
+
+ if (workspaceAgents.length === 0) {
+ agentsListContainer.innerHTML = 'Keine Agenten vorhanden.
';
+ return;
+ }
+
+ workspaceAgents.forEach(agent => {
+ const agentItem = document.createElement('div');
+ agentItem.className = 'agent-list-item';
+ agentItem.innerHTML = `
+
+ ${agent.description || ''}
+
+ ${agent.capabilities ? agent.capabilities.map(cap =>
+ `${cap}`
+ ).join('') : ''}
+
+
+
+
+
+ `;
+ agentsListContainer.appendChild(agentItem);
+ });
+
+ // Event-Listener für Agenten-Aktionen einrichten
+ setupAgentActionListeners(globalState);
+ }
+
+ // Event-Listener für Agenten-Aktionen
+ function setupAgentActionListeners(globalState) {
+ // Bearbeiten-Button
+ document.querySelectorAll('.edit-agent-btn').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const agentId = btn.getAttribute('data-id');
+ const agent = globalState.availableAgents.find(a => a.id === agentId);
+
+ if (agent) {
+ editAgent(agent, globalState);
+ }
+ });
+ });
+
+ // Löschen-Button
+ document.querySelectorAll('.delete-agent-btn').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const agentId = btn.getAttribute('data-id');
+
+ if (confirm('Möchten Sie diesen Agenten wirklich löschen?')) {
+ deleteAgent(agentId, globalState);
+ }
+ });
+ });
+ }
+
+ // Agent bearbeiten
+ function editAgent(agent, globalState) {
+ // Einfaches Prompt als Platzhalter - in einer realen Anwendung würde dies
+ // durch ein ansprechendes Modal oder Formular ersetzt
+ const newName = prompt('Name des Agenten:', agent.name);
+ const newType = prompt('Typ des Agenten:', agent.type);
+ const newDescription = prompt('Beschreibung des Agenten:', agent.description);
+
+ if (newName) {
+ // Agent aktualisieren
+ agent.name = newName;
+ agent.type = newType || agent.type;
+ agent.description = newDescription || agent.description;
+
+ // UI aktualisieren
+ renderMyAgents(globalState);
+
+ // Optional: Aktualisierung auf dem Server
+ updateAgent(agent);
+ }
+ }
+
+ // Agent speichern
+ async function saveAgent(agentData, globalState) {
+ try {
+ // Agent auf dem Server speichern
+ const response = await window.globalUtils.postData('/api/agents/save', agentData);
+
+ // Rückmeldung vom Server verarbeiten
+ if (response && response.id) {
+ // Wenn der Server eine ID zurückgibt, diese aktualisieren
+ const index = globalState.availableAgents.findIndex(a => a.id === agentData.id);
+ if (index !== -1) {
+ globalState.availableAgents[index].id = response.id;
+ }
+
+ // UI aktualisieren
+ renderMyAgents(globalState);
+ }
+ } catch (error) {
+ console.error("Fehler beim Speichern des Agenten:", error);
+ }
+ }
+
+ // Agent aktualisieren
+ async function updateAgent(agent) {
+ try {
+ // Agent auf dem Server aktualisieren
+ await window.globalUtils.postData('/api/agents/update', agent);
+ } catch (error) {
+ console.error("Fehler beim Aktualisieren des Agenten:", error);
+ }
+ }
+
+ // Agent löschen
+ async function deleteAgent(agentId, globalState) {
+ try {
+ // Aus dem globalen State entfernen
+ globalState.availableAgents = globalState.availableAgents.filter(a => a.id !== agentId);
+
+ // UI aktualisieren
+ renderMyAgents(globalState);
+
+ // Agent vom Server löschen
+ await window.globalUtils.postData('/api/agents/delete', { id: agentId });
+ } catch (error) {
+ console.error("Fehler beim Löschen des Agenten:", error);
+ }
+ }
+
+ // Modul-Initialisierung exportieren
+ window.initAgentsModule = initAgentsModule;
+})();
diff --git a/backend/webparts/part-data.html b/backend/webparts/part-data.html
new file mode 100644
index 00000000..16c53db6
--- /dev/null
+++ b/backend/webparts/part-data.html
@@ -0,0 +1,18 @@
+
+
diff --git a/backend/webparts/part-data.js b/backend/webparts/part-data.js
new file mode 100644
index 00000000..608a7867
--- /dev/null
+++ b/backend/webparts/part-data.js
@@ -0,0 +1,111 @@
+// data-module.js
+(function() {
+ // DOM-Elemente
+ let dataFileInput, uploadBtn, myFilesList;
+
+ // Initialisierungsfunktion für das Daten-Modul
+ function initDataModule(globalState) {
+ // DOM-Elemente referenzieren
+ dataFileInput = document.getElementById('data-file-input');
+ uploadBtn = document.getElementById('data-upload-btn');
+ myFilesList = document.getElementById('my-files-list');
+
+ // Event-Listener hinzufügen
+ setupEventListeners(globalState);
+
+ // Initialdaten rendern
+ renderMyFiles(globalState);
+ }
+
+ // Event-Listener einrichten
+ function setupEventListeners(globalState) {
+ // Datei-Upload
+ uploadBtn.addEventListener('click', () => dataFileInput.click());
+ dataFileInput.addEventListener('change',
+ window.globalUtils.handleFileUpload(dataFileInput, (newFile) => {
+ // Nach erfolgreichem Upload
+ globalState.availableFiles.push(newFile);
+ renderMyFiles(globalState);
+ })
+ );
+ }
+
+ // Meine Dateien rendern
+ function renderMyFiles(globalState) {
+ if (!myFilesList) return;
+
+ myFilesList.innerHTML = '';
+
+ if (globalState.availableFiles.length === 0) {
+ myFilesList.innerHTML = 'Keine Dateien vorhanden.
';
+ return;
+ }
+
+ globalState.availableFiles.forEach(file => {
+ const li = document.createElement('li');
+ li.className = 'file-item';
+ li.innerHTML = `
+
+
+ ${file.name}
+
+
+ ${file.size || ''}
+ ${new Date(file.upload_date || Date.now()).toLocaleString()}
+
+
+
+
+
+ `;
+ myFilesList.appendChild(li);
+ });
+
+ // Event-Listener für Datei-Aktionen
+ setupFileActionListeners(globalState);
+ }
+
+ // Event-Listener für Datei-Aktionen
+ function setupFileActionListeners(globalState) {
+ // Datei anzeigen
+ document.querySelectorAll('.view-file-btn').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const fileId = btn.getAttribute('data-id');
+ const file = globalState.availableFiles.find(f => f.id === fileId);
+ if (file && file.path) {
+ window.open(file.path, '_blank');
+ } else {
+ alert('Datei nicht verfügbar');
+ }
+ });
+ });
+
+ // Datei löschen
+ document.querySelectorAll('.delete-file-btn').forEach(btn => {
+ btn.addEventListener('click', async () => {
+ const fileId = btn.getAttribute('data-id');
+ if (confirm('Möchten Sie diese Datei wirklich löschen?')) {
+ try {
+ // TODO: API-Endpunkt zum Löschen implementieren
+ // await fetch(`/files/${fileId}`, { method: 'DELETE' });
+
+ // Aus dem globalen State entfernen
+ globalState.availableFiles = globalState.availableFiles.filter(f => f.id !== fileId);
+
+ // UI aktualisieren
+ renderMyFiles(globalState);
+ } catch (error) {
+ console.error("Fehler beim Löschen der Datei:", error);
+ }
+ }
+ });
+ });
+ }
+
+ // Modul-Initialisierung exportieren
+ window.initDataModule = initDataModule;
+})();
diff --git a/backend/webparts/part-prompts.html b/backend/webparts/part-prompts.html
new file mode 100644
index 00000000..8286f109
--- /dev/null
+++ b/backend/webparts/part-prompts.html
@@ -0,0 +1,15 @@
+
+
diff --git a/backend/webparts/part-prompts.js b/backend/webparts/part-prompts.js
new file mode 100644
index 00000000..db2bbfcf
--- /dev/null
+++ b/backend/webparts/part-prompts.js
@@ -0,0 +1,168 @@
+// prompts-module.js
+(function() {
+ // DOM-Elemente
+ let addPromptBtn, promptsList;
+
+ // Initialisierungsfunktion für das Prompts-Modul
+ function initPromptsModule(globalState) {
+ // DOM-Elemente referenzieren
+ addPromptBtn = document.getElementById('add-prompt-btn');
+ promptsList = document.getElementById('prompts-list');
+
+ // Event-Listener hinzufügen
+ setupEventListeners(globalState);
+
+ // Initialdaten rendern
+ renderMyPrompts(globalState);
+ }
+
+ // Event-Listener einrichten
+ function setupEventListeners(globalState) {
+ // Neuen Prompt hinzufügen
+ addPromptBtn.addEventListener('click', () => {
+ const newPrompt = prompt('Geben Sie den neuen Prompt ein:');
+ if (newPrompt && newPrompt.trim()) {
+ const promptData = {
+ id: `prompt_${Date.now()}`,
+ content: newPrompt.trim(),
+ created_at: new Date().toISOString()
+ };
+
+ // Prompt zur globalen Liste hinzufügen
+ globalState.availablePrompts.push(promptData);
+
+ // UI aktualisieren
+ renderMyPrompts(globalState);
+
+ // Optional: Prompt speichern
+ savePrompt(promptData, globalState);
+ }
+ });
+ }
+
+ // Meine Prompts rendern
+ function renderMyPrompts(globalState) {
+ if (!promptsList) return;
+
+ promptsList.innerHTML = '';
+
+ if (globalState.availablePrompts.length === 0) {
+ promptsList.innerHTML = 'Keine Prompts vorhanden.
';
+ return;
+ }
+
+ globalState.availablePrompts.forEach(prompt => {
+ const promptItem = document.createElement('div');
+ promptItem.className = 'prompt-item';
+ promptItem.innerHTML = `
+ ${prompt.content}
+ Erstellt am: ${new Date(prompt.created_at).toLocaleString()}
+
+
+
+
+ `;
+ promptsList.appendChild(promptItem);
+ });
+
+ // Event-Listener für Prompt-Aktionen einrichten
+ setupPromptActionListeners(globalState);
+ }
+
+ // Event-Listener für Prompt-Aktionen
+ function setupPromptActionListeners(globalState) {
+ // "Verwenden"-Button
+ document.querySelectorAll('.use-prompt-btn').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const promptId = btn.getAttribute('data-id');
+ const selectedPrompt = globalState.availablePrompts.find(p => p.id === promptId);
+
+ if (selectedPrompt) {
+ // Workflow-Modul aktivieren und Prompt übernehmen
+ document.querySelector('a[href="#workflow"]').click();
+
+ // Prompt in Workflow-Eingabefeld setzen
+ const promptInput = document.getElementById('prompt-input');
+ const promptSelect = document.getElementById('prompt-select');
+
+ if (promptInput) {
+ promptInput.value = selectedPrompt.content;
+ }
+
+ if (promptSelect) {
+ // Wenn der Prompt bereits existiert, ihn in der Dropdown auswählen
+ const existingOption = Array.from(promptSelect.options).find(
+ option => option.textContent.includes(selectedPrompt.content.substring(0, 50))
+ );
+
+ if (existingOption) {
+ promptSelect.value = existingOption.value;
+ }
+ }
+ }
+ });
+ });
+
+ // "Löschen"-Button
+ document.querySelectorAll('.delete-prompt-btn').forEach(btn => {
+ btn.addEventListener('click', async () => {
+ const promptId = btn.getAttribute('data-id');
+
+ if (confirm('Möchten Sie diesen Prompt wirklich löschen?')) {
+ try {
+ // Aus dem globalen State entfernen
+ globalState.availablePrompts = globalState.availablePrompts.filter(p => p.id !== promptId);
+
+ // UI aktualisieren
+ renderMyPrompts(globalState);
+
+ // Optional: Prompt vom Server löschen
+ await deletePrompt(promptId);
+ } catch (error) {
+ console.error("Fehler beim Löschen des Prompts:", error);
+ }
+ }
+ });
+ });
+ }
+
+ // Prompt speichern
+ async function savePrompt(promptData, globalState) {
+ try {
+ // Prompt auf dem Server speichern
+ const response = await window.globalUtils.postData('/api/prompts/save', promptData);
+
+ // Rückmeldung vom Server verarbeiten
+ if (response && response.id) {
+ // Wenn der Server eine ID zurückgibt, diese aktualisieren
+ const index = globalState.availablePrompts.findIndex(p => p.id === promptData.id);
+ if (index !== -1) {
+ globalState.availablePrompts[index].id = response.id;
+ }
+
+ // UI aktualisieren
+ renderMyPrompts(globalState);
+ }
+ } catch (error) {
+ console.error("Fehler beim Speichern des Prompts:", error);
+ }
+ }
+
+ // Prompt löschen
+ async function deletePrompt(promptId) {
+ try {
+ // Prompt vom Server löschen
+ await window.globalUtils.postData('/api/prompts/delete', { id: promptId });
+ } catch (error) {
+ console.error("Fehler beim Löschen des Prompts:", error);
+ throw error;
+ }
+ }
+
+ // Modul-Initialisierung exportieren
+ window.initPromptsModule = initPromptsModule;
+})();
diff --git a/backend/webparts/part-workflow.html b/backend/webparts/part-workflow.html
new file mode 100644
index 00000000..1cdbdbba
--- /dev/null
+++ b/backend/webparts/part-workflow.html
@@ -0,0 +1,103 @@
+
+
+
+
+
Workflow-Konfiguration
+
+
+
+
1. Dateien auswählen
+
+
+
+
+
+
Ausgewählte Dateien
+
+
+
+ Keine Dateien ausgewählt
+
+
+
+
+
+
+
+
+
+
2. Prompt eingeben oder auswählen
+
+
+
+
+
+
+
+
+
+
+
+
+
+
3. Agenten auswählen
+
+
+
+
+
+
+
+
+
+
+
+
+
Ausführung & Ergebnisse
+
+
+
+
Ausführungsprotokoll
+
+
Workflow noch nicht gestartet. Protokoll wird hier angezeigt.
+
+
+
+
+
+
Ergebnisse
+
+
+
Keine Ergebnisse verfügbar
+
Führen Sie einen Workflow aus, um Ergebnisse zu sehen
+
+
+
+
+
+
+
+
diff --git a/backend/webparts/part-workflow.js b/backend/webparts/part-workflow.js
new file mode 100644
index 00000000..c123d621
--- /dev/null
+++ b/backend/webparts/part-workflow.js
@@ -0,0 +1,408 @@
+// workflow-module.js
+(function() {
+ // Workflow-Modul-Zustand
+ let workflowState = {
+ selectedFiles: [],
+ selectedAgents: [],
+ prompt: "",
+ logs: [],
+ results: [],
+ isRunning: false
+ };
+
+ // DOM-Elemente
+ let availableFiles, selectedFiles, emptyFilesState,
+ fileInput, uploadBtn, promptInput, promptSelect,
+ agentList, resetBtn, startWorkflowBtn,
+ executionLog, resultsContainer,
+ emptyResultsState, resultsList;
+
+ // Initialisierungsfunktion für das Workflow-Modul
+ function initWorkflowModule(globalState) {
+ // DOM-Elemente referenzieren
+ availableFiles = document.getElementById('available-files');
+ selectedFiles = document.getElementById('selected-files');
+ emptyFilesState = document.getElementById('empty-files-state');
+ fileInput = document.getElementById('file-input');
+ uploadBtn = document.getElementById('upload-btn');
+ promptInput = document.getElementById('prompt-input');
+ promptSelect = document.getElementById('prompt-select');
+ agentList = document.getElementById('agent-list');
+ resetBtn = document.getElementById('reset-btn');
+ startWorkflowBtn = document.getElementById('start-workflow-btn');
+ executionLog = document.getElementById('execution-log');
+ resultsContainer = document.getElementById('results-container');
+ emptyResultsState = document.getElementById('empty-results-state');
+ resultsList = document.getElementById('results-list');
+
+ // Event-Listener hinzufügen
+ setupEventListeners(globalState);
+
+ // Initialdaten rendern
+ renderFiles(globalState);
+ renderPromptSelector(globalState);
+ renderAgents(globalState);
+ }
+
+ // Event-Listener einrichten
+ function setupEventListeners(globalState) {
+ // Datei-Upload
+ uploadBtn.addEventListener('click', () => fileInput.click());
+ fileInput.addEventListener('change',
+ window.globalUtils.handleFileUpload(fileInput, (newFile) => {
+ // Nach erfolgreichem Upload
+ workflowState.selectedFiles.push(newFile);
+ renderSelectedFiles();
+ renderFiles(globalState);
+ })
+ );
+
+ // Prompt-Eingabe
+ promptInput.addEventListener('input', (e) => {
+ workflowState.prompt = e.target.value;
+ });
+
+ // Prompt-Auswahl
+ promptSelect.addEventListener('change', () => {
+ const selectedPromptId = promptSelect.value;
+ if (selectedPromptId) {
+ const selectedPrompt = globalState.availablePrompts.find(p => p.id === selectedPromptId);
+ if (selectedPrompt) {
+ promptInput.value = selectedPrompt.content;
+ workflowState.prompt = selectedPrompt.content;
+ }
+ }
+ });
+
+ // Zurücksetzen
+ resetBtn.addEventListener('click', resetWorkflow);
+
+ // Workflow starten
+ startWorkflowBtn.addEventListener('click', () => runWorkflow(globalState));
+ }
+
+ // Verfügbare Dateien rendern
+ function renderFiles(globalState) {
+ availableFiles.innerHTML = '';
+ globalState.availableFiles.forEach(file => {
+ const li = document.createElement('li');
+ li.className = 'file-item';
+ const isSelected = workflowState.selectedFiles.some(f => f.id === file.id);
+ li.innerHTML = `
+
+
+
+
+ ${file.size || ''}
+ `;
+ const checkbox = li.querySelector(`#file-${file.id}`);
+ checkbox.addEventListener('change', () => {
+ if (checkbox.checked) {
+ workflowState.selectedFiles.push(file);
+ } else {
+ workflowState.selectedFiles = workflowState.selectedFiles.filter(f => f.id !== file.id);
+ }
+ renderSelectedFiles();
+ });
+ availableFiles.appendChild(li);
+ });
+ }
+
+ // Ausgewählte Dateien rendern
+ function renderSelectedFiles() {
+ if (workflowState.selectedFiles.length === 0) {
+ emptyFilesState.style.display = 'flex';
+ selectedFiles.innerHTML = '';
+ } else {
+ emptyFilesState.style.display = 'none';
+ selectedFiles.innerHTML = '';
+ workflowState.selectedFiles.forEach(file => {
+ const li = document.createElement('li');
+ li.className = 'selected-file-item';
+ li.innerHTML = `
+
+
+ ${file.name}
+
+
+ `;
+ selectedFiles.appendChild(li);
+ });
+
+ // Event-Listener für Entfernen-Buttons
+ document.querySelectorAll('.remove-file-btn').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const fileId = btn.getAttribute('data-id');
+ workflowState.selectedFiles = workflowState.selectedFiles.filter(f => f.id !== fileId);
+ renderSelectedFiles();
+ renderFiles(window.globalState);
+ });
+ });
+ }
+ }
+
+ // Prompt-Auswahlbox rendern
+ function renderPromptSelector(globalState) {
+ // Existierende Optionen entfernen (außer der ersten)
+ while (promptSelect.options.length > 1) {
+ promptSelect.remove(1);
+ }
+
+ // Prompts aus dem globalen State hinzufügen
+ globalState.availablePrompts.forEach(prompt => {
+ const option = document.createElement('option');
+ option.value = prompt.id;
+ option.textContent = prompt.content.substring(0, 50) + (prompt.content.length > 50 ? '...' : '');
+ promptSelect.appendChild(option);
+ });
+ }
+
+ // Agenten rendern
+ function renderAgents(globalState) {
+ agentList.innerHTML = '';
+
+ // Agenten für den aktuellen Workspace filtern
+ const workspaceAgents = globalState.availableAgents.filter(
+ agent => agent.workspace_id === globalState.currentWorkspace?.id
+ );
+
+ if (workspaceAgents.length === 0) {
+ agentList.innerHTML = 'Keine Agenten im ausgewählten Workspace verfügbar.
';
+ return;
+ }
+
+ workspaceAgents.forEach(agent => {
+ const li = document.createElement('li');
+ li.className = 'agent-item';
+ const isSelected = workflowState.selectedAgents.some(a => a.id === agent.id);
+ li.innerHTML = `
+
+ ${agent.description || ''}
+ `;
+ const checkbox = li.querySelector(`#agent-${agent.id}`);
+ checkbox.addEventListener('change', () => {
+ if (checkbox.checked) {
+ workflowState.selectedAgents.push(agent);
+ } else {
+ workflowState.selectedAgents = workflowState.selectedAgents.filter(a => a.id !== agent.id);
+ }
+ });
+ agentList.appendChild(li);
+ });
+ }
+
+ // Ausführungsprotokoll aktualisieren
+ function addLogEntry(message, type = 'info') {
+ const log = {
+ id: `log_${Date.now()}`,
+ message,
+ type,
+ timestamp: new Date().toISOString()
+ };
+ workflowState.logs.push(log);
+ renderLogs();
+ return log;
+ }
+
+ // Protokoll rendern
+ function renderLogs() {
+ if (workflowState.logs.length === 0) {
+ executionLog.innerHTML = 'Workflow noch nicht gestartet. Protokoll wird hier angezeigt.
';
+ return;
+ }
+
+ executionLog.innerHTML = '';
+ workflowState.logs.forEach(log => {
+ const logEntry = document.createElement('div');
+ logEntry.className = 'log-entry';
+ logEntry.innerHTML = `
+ [${new Date(log.timestamp).toLocaleTimeString()}]
+ ${log.message}
+ `;
+ executionLog.appendChild(logEntry);
+ });
+
+ // Zum Ende scrollen
+ executionLog.scrollTop = executionLog.scrollHeight;
+ }
+
+ // Ergebnis hinzufügen
+ function addResult(result) {
+ workflowState.results.push(result);
+ renderResults();
+ }
+
+ // Ergebnisse rendern
+ function renderResults() {
+ if (workflowState.results.length === 0) {
+ emptyResultsState.style.display = 'flex';
+ resultsList.style.display = 'none';
+ return;
+ }
+
+ emptyResultsState.style.display = 'none';
+ resultsList.style.display = 'block';
+ resultsList.innerHTML = '';
+
+ workflowState.results.forEach(result => {
+ const resultItem = document.createElement('div');
+ resultItem.className = 'result-item';
+
+ if (result.type === 'text') {
+ resultItem.innerHTML = `
+
+ ${result.content}
+ `;
+ } else if (result.type === 'chart') {
+ resultItem.innerHTML = `
+
+
+
Umsatzentwicklung nach Quartal
+
${result.content}
+
+ `;
+ }
+
+ resultsList.appendChild(resultItem);
+ });
+ }
+
+ // Workflow-Ausführung
+ async function runWorkflow(globalState) {
+ if (workflowState.selectedFiles.length === 0 ||
+ workflowState.selectedAgents.length === 0 ||
+ !workflowState.prompt.trim()) {
+ alert("Bitte wählen Sie Dateien und Agenten aus und geben Sie einen Prompt ein.");
+ return;
+ }
+
+ workflowState.isRunning = true;
+ workflowState.logs = [];
+ workflowState.results = [];
+
+ startWorkflowBtn.textContent = 'Wird ausgeführt...';
+ startWorkflowBtn.classList.add('running');
+ startWorkflowBtn.disabled = true;
+
+ renderLogs();
+ renderResults();
+
+ try {
+ // API-Aufruf an das Backend für Workflow-Ausführung
+ const workflowData = {
+ workspace_id: globalState.currentWorkspace.id,
+ files: workflowState.selectedFiles.map(file => file.id),
+ agents: workflowState.selectedAgents.map(agent => agent.id),
+ prompt: workflowState.prompt,
+ workflow_name: "Workflow " + new Date().toLocaleString()
+ };
+
+ const response = await window.globalUtils.postData('/api/workflow/run', workflowData);
+
+ if (response && response.workflow_id) {
+ const workflowId = response.workflow_id;
+ addLogEntry("Workflow gestartet", "info");
+ addLogEntry(`Workflow-ID: ${workflowId}`, "info");
+
+ // Polling für Workflow-Status und -Ergebnisse starten
+ pollWorkflowStatus(workflowId);
+ } else {
+ throw new Error("Workflow konnte nicht gestartet werden");
+ }
+ } catch (error) {
+ console.error("Fehler beim Starten des Workflows:", error);
+ workflowState.isRunning = false;
+ startWorkflowBtn.textContent = 'Workflow starten';
+ startWorkflowBtn.classList.remove('running');
+ startWorkflowBtn.disabled = false;
+
+ addLogEntry(`Fehler beim Starten des Workflows: ${error.message}`, "error");
+ }
+ }
+
+ // Workflow-Status abfragen
+ async function pollWorkflowStatus(workflowId) {
+ try {
+ // Status abrufen
+ const status = await window.globalUtils.fetchData(`/api/workflow/${workflowId}/status`);
+
+ // Logs abrufen und anzeigen
+ const logs = await window.globalUtils.fetchData(`/api/workflow/${workflowId}/logs`);
+ workflowState.logs = logs;
+ renderLogs();
+
+ // Status aktualisieren
+ if (status) {
+ // Fortschritt anzeigen
+ addLogEntry(`Fortschritt: ${Math.round(status.progress * 100)}%`, "info");
+
+ // Wenn der Workflow abgeschlossen ist, Ergebnisse abrufen
+ if (status.status === 'completed' || status.status === 'failed') {
+ const results = await window.globalUtils.fetchData(`/api/workflow/${workflowId}/results`);
+ workflowState.results = results;
+ renderResults();
+
+ addLogEntry(`Workflow ${status.status === 'completed' ? 'erfolgreich beendet' : 'fehlgeschlagen'}`,
+ status.status === 'completed' ? "success" : "error");
+
+ workflowState.isRunning = false;
+ startWorkflowBtn.textContent = 'Workflow starten';
+ startWorkflowBtn.classList.remove('running');
+ startWorkflowBtn.disabled = false;
+ return;
+ }
+ }
+
+ // Weiteres Polling nach einer kurzen Verzögerung
+ setTimeout(() => pollWorkflowStatus(workflowId), 2000);
+ } catch (error) {
+ console.error("Fehler beim Abrufen des Workflow-Status:", error);
+ addLogEntry(`Fehler beim Abrufen des Workflow-Status: ${error.message}`, "error");
+
+ workflowState.isRunning = false;
+ startWorkflowBtn.textContent = 'Workflow starten';
+ startWorkflowBtn.classList.remove('running');
+ startWorkflowBtn.disabled = false;
+ }
+ }
+
+ // Workflow zurücksetzen
+ function resetWorkflow() {
+ workflowState.selectedFiles = [];
+ workflowState.selectedAgents = [];
+ promptInput.value = "";
+ promptSelect.selectedIndex = 0;
+ workflowState.prompt = "";
+
+ renderFiles(window.globalState);
+ renderSelectedFiles();
+ renderAgents(window.globalState);
+ }
+
+ // Modul-Initialisierung exportieren
+ window.initWorkflowModule = initWorkflowModule;
+})();
diff --git a/backend/webparts/test.js b/backend/webparts/test.js
new file mode 100644
index 0000000000000000000000000000000000000000..6fec95654e2cf395f13cc0f63e3b166967f19ce9
GIT binary patch
literal 52
zcmezWFPR~qA&;RLNair4GUx%xe1>!e4F+|F5+J`ANa_J`1rVz
+
diff --git a/frontend/index.html b/frontend/index.html
index e3458bd3..8727dca4 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -3,7 +3,7 @@
- Data Platform - Multi-Agent Service
+ PowerOn | Multi-Agent Service
@@ -13,7 +13,7 @@
Data Platform
-
Demo User
+
Shelly Miller
@@ -61,144 +61,15 @@
-
-
-
-
-
Meine Prompts
-
-
Gespeicherte Prompts
-
-
-
-
-
-
-
-
-
-
Meine Agents
-
-
Verfügbare Agents
-
-
-
-
-
-
-
-
-
-
-
Workflow-Konfiguration
-
-
-
-
1. Dateien auswählen
-
-
-
-
-
-
Ausgewählte Dateien
-
-
-
- Keine Dateien ausgewählt
-
-
-
-
-
-
-
-
-
-
2. Prompt eingeben
-
-
-
-
-
-
3. Agenten auswählen
-
-
-
-
-
-
-
-
-
-
-
-
-
Ausführung & Ergebnisse
-
-
-
-
Ausführungsprotokoll
-
-
Workflow noch nicht gestartet. Protokoll wird hier angezeigt.
-
-
-
-
-
-
Ergebnisse
-
-
-
Keine Ergebnisse verfügbar
-
Führen Sie einen Workflow aus, um Ergebnisse zu sehen
-
-
-
-
-
-
-
-
-
-
-
-
Meine Daten
-
-
Verfügbare Dateien
-
-
-
-
-
-
+
+
+
+
+
-
+
+