From 39f33b0fb8190c64fab099e7d97426db4d665c20 Mon Sep 17 00:00:00 2001
From: valueon
Date: Sun, 16 Mar 2025 23:50:27 +0100
Subject: [PATCH] refactored gateway for scaling
---
.../mandates.json | 0
.../users.json | 0
.../agents.json | 0
.../files.json | 0
.../prompts.json | 0
.../workspaces.json | 0
gwserver/{results => _results}/.gitignore | 0
gwserver/{uploads => _uploads}/.gitignore | 0
gwserver/app.py | 439 ++----------------
gwserver/attributes.py | 113 +++++
gwserver/auth.py | 4 +-
gwserver/config.ini | 4 +-
.../agentservice_interface.py} | 0
.../gateway_interface.py} | 6 +-
.../gateway_model.py} | 0
.../lucydom_interface.py} | 2 +-
.../lucydom_model.py} | 0
gwserver/routes/agent.py | 91 ++++
gwserver/routes/attributes.py | 101 ++++
gwserver/routes/file.py | 132 ++++++
gwserver/routes/mandate.py | 73 +++
gwserver/routes/prompt.py | 88 ++++
gwserver/routes/workflow.py | 148 ++++++
gwserver/routes/workspace.py | 62 +++
24 files changed, 843 insertions(+), 420 deletions(-)
rename gwserver/{data_gateway => _database_gateway}/mandates.json (100%)
rename gwserver/{data_gateway => _database_gateway}/users.json (100%)
rename gwserver/{data_lucydom => _database_lucydom}/agents.json (100%)
rename gwserver/{data_lucydom => _database_lucydom}/files.json (100%)
rename gwserver/{data_lucydom => _database_lucydom}/prompts.json (100%)
rename gwserver/{data_lucydom => _database_lucydom}/workspaces.json (100%)
rename gwserver/{results => _results}/.gitignore (100%)
rename gwserver/{uploads => _uploads}/.gitignore (100%)
create mode 100644 gwserver/attributes.py
rename gwserver/{interface_agentservice.py => modules/agentservice_interface.py} (100%)
rename gwserver/{interface_gateway.py => modules/gateway_interface.py} (98%)
rename gwserver/{model_gateway.py => modules/gateway_model.py} (100%)
rename gwserver/{interface_lucydom.py => modules/lucydom_interface.py} (99%)
rename gwserver/{model_lucydom.py => modules/lucydom_model.py} (100%)
create mode 100644 gwserver/routes/agent.py
create mode 100644 gwserver/routes/attributes.py
create mode 100644 gwserver/routes/file.py
create mode 100644 gwserver/routes/mandate.py
create mode 100644 gwserver/routes/prompt.py
create mode 100644 gwserver/routes/workflow.py
create mode 100644 gwserver/routes/workspace.py
diff --git a/gwserver/data_gateway/mandates.json b/gwserver/_database_gateway/mandates.json
similarity index 100%
rename from gwserver/data_gateway/mandates.json
rename to gwserver/_database_gateway/mandates.json
diff --git a/gwserver/data_gateway/users.json b/gwserver/_database_gateway/users.json
similarity index 100%
rename from gwserver/data_gateway/users.json
rename to gwserver/_database_gateway/users.json
diff --git a/gwserver/data_lucydom/agents.json b/gwserver/_database_lucydom/agents.json
similarity index 100%
rename from gwserver/data_lucydom/agents.json
rename to gwserver/_database_lucydom/agents.json
diff --git a/gwserver/data_lucydom/files.json b/gwserver/_database_lucydom/files.json
similarity index 100%
rename from gwserver/data_lucydom/files.json
rename to gwserver/_database_lucydom/files.json
diff --git a/gwserver/data_lucydom/prompts.json b/gwserver/_database_lucydom/prompts.json
similarity index 100%
rename from gwserver/data_lucydom/prompts.json
rename to gwserver/_database_lucydom/prompts.json
diff --git a/gwserver/data_lucydom/workspaces.json b/gwserver/_database_lucydom/workspaces.json
similarity index 100%
rename from gwserver/data_lucydom/workspaces.json
rename to gwserver/_database_lucydom/workspaces.json
diff --git a/gwserver/results/.gitignore b/gwserver/_results/.gitignore
similarity index 100%
rename from gwserver/results/.gitignore
rename to gwserver/_results/.gitignore
diff --git a/gwserver/uploads/.gitignore b/gwserver/_uploads/.gitignore
similarity index 100%
rename from gwserver/uploads/.gitignore
rename to gwserver/_uploads/.gitignore
diff --git a/gwserver/app.py b/gwserver/app.py
index 5f2cff90..2e2f3e9e 100644
--- a/gwserver/app.py
+++ b/gwserver/app.py
@@ -1,45 +1,38 @@
-from fastapi import FastAPI, File, UploadFile, HTTPException, Depends, Body, Query, status
+from fastapi import FastAPI, HTTPException, Depends, Body, status
from fastapi.middleware.cors import CORSMiddleware
-from fastapi.responses import JSONResponse, Response
+from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
-from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
+from fastapi.security import OAuth2PasswordRequestForm
import uvicorn
-from typing import List, Dict, Any, Optional
-import uuid
+from typing import Dict, Any
import os
-import json
-import asyncio
import logging
-from datetime import datetime, timedelta
+from datetime import timedelta
# Import interfaces
-from interface_gateway import get_gateway_interface
-from interface_lucydom import get_lucydom_interface
-from interface_agentservice import get_agent_service
+from modules.gateway_interface import get_gateway_interface
+from modules.agentservice_interface import get_agent_service
# Import auth module
from auth import (
create_access_token,
- get_current_user,
get_current_active_user,
get_user_context,
ACCESS_TOKEN_EXPIRE_MINUTES
)
-# Import models (restructured)
-from model_gateway import User, UserInDB, Token, Mandate
-from model_lucydom import (
- Workspace,
- Agent,
- DataObject,
- Prompt,
- WorkflowRequest,
- WorkflowResponse,
- LogEntry,
- Result
-)
+# Import models - generisch importieren
+import modules.gateway_model as gateway_model
+# Import router modules
+from routes.attributes import router as attributes_router
+from routes.workspace import router as workspace_router
+from routes.agent import router as agent_router
+from routes.file import router as file_router
+from routes.prompt import router as prompt_router
+from routes.workflow import router as workflow_router
+from routes.mandate import router as mandate_router
# Konfiguration des Loggers
logging.basicConfig(
@@ -60,8 +53,6 @@ app.add_middleware(
allow_headers=["*"],
)
-# API - ENDPUNKTE
-
# Statischer Folder für Frontend
os.makedirs(os.path.join(os.getcwd(), "static"), exist_ok=True)
app.mount("/static", StaticFiles(directory="static"), name="static")
@@ -77,7 +68,7 @@ async def get_test():
return "OK 1.0"
# Token-Endpunkt für Login
-@app.post("/token", response_model=Token, tags=["General"])
+@app.post("/token", response_model=gateway_model.Token, tags=["General"])
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
# Gateway-Interface ohne Kontext initialisieren
gateway = get_gateway_interface()
@@ -106,7 +97,7 @@ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(
# Benutzerregistrierung
-@app.post("/api/users/register", response_model=User, tags=["General"])
+@app.post("/api/users/register", response_model=gateway_model.User, tags=["General"])
async def register_user(user_data: dict = Body(...), current_user: Dict[str, Any] = Depends(get_current_active_user)):
"""Neuen Benutzer registrieren"""
# Nur Benutzer des gleichen Mandanten dürfen erstellt werden
@@ -137,390 +128,14 @@ async def read_users_me(current_user: Dict[str, Any] = Depends(get_current_activ
return current_user
-# Workspace-Endpunkte
-@app.get("/api/workspaces", tags=["Workspaces"], response_model=List[Dict[str, Any]])
-async def get_workspaces(current_user: Dict[str, Any] = Depends(get_current_active_user)):
- """Alle verfügbaren Workspaces abrufen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- return lucy_interface.get_all_workspaces()
-
-
-@app.get("/api/workspaces/{workspace_id}", tags=["Workspaces"], response_model=Dict[str, Any])
-async def get_workspace(
- workspace_id: int,
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Einen bestimmten Workspace mit allen Details abrufen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- workspace = lucy_interface.get_workspace(workspace_id)
- if not workspace:
- raise HTTPException(status_code=404, detail=f"Workspace mit ID {workspace_id} nicht gefunden")
-
- return workspace
-
-
-@app.post("/api/workspaces", tags=["Workspaces"], response_model=Dict[str, Any])
-async def create_workspace(
- workspace: Dict[str, Any] = Body(...),
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Einen neuen Workspace erstellen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- new_workspace = lucy_interface.create_workspace(name=workspace.get("name", "Neuer Workspace"))
- return new_workspace
-
-
-# Agenten-Endpunkte
-@app.get("/api/agents", tags=["Agents"], response_model=List[Dict[str, Any]])
-async def get_agents(
- workspace_id: Optional[int] = Query(None),
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Alle Agenten oder Agenten eines bestimmten Workspaces abrufen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- if workspace_id:
- return lucy_interface.get_agents_by_workspace(workspace_id)
-
- return lucy_interface.get_all_agents()
-
-
-@app.post("/api/agents", tags=["Agents"], response_model=Dict[str, Any])
-async def create_agent(
- agent: Dict[str, Any] = Body(...),
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Einen neuen Agenten erstellen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- workspace_id = agent.get("workspace_id")
-
- if not workspace_id:
- raise HTTPException(status_code=400, detail="workspace_id ist erforderlich")
-
- # Workspace existiert?
- workspace = lucy_interface.get_workspace(workspace_id)
- if not workspace:
- raise HTTPException(status_code=404, detail=f"Workspace mit ID {workspace_id} nicht gefunden")
-
- new_agent = lucy_interface.create_agent(
- name=agent.get("name", "Neuer Agent"),
- agent_type=agent.get("type", "generic"),
- workspace_id=workspace_id,
- capabilities=agent.get("capabilities", []),
- description=agent.get("description", "")
- )
-
- return new_agent
-
-
-# Datei-Endpunkte
-@app.get("/api/files", tags=["Files"], response_model=List[Dict[str, Any]])
-async def get_files(current_user: Dict[str, Any] = Depends(get_current_active_user)):
- """Alle verfügbaren Dateien abrufen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- return lucy_interface.get_all_files()
-
-
-@app.post("/api/files/upload", tags=["Files"])
-async def upload_file(
- file: UploadFile = File(...),
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Eine Datei hochladen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- try:
- # Generiere eine eindeutige ID für die Datei
- file_id = str(uuid.uuid4())
- file_ext = os.path.splitext(file.filename)[1]
- file_path = os.path.join(get_agent_service(mandate_id, user_id).upload_dir, f"{file_id}{file_ext}")
-
- # Datei speichern
- with open(file_path, "wb") as f:
- content = await file.read()
- f.write(content)
-
- # Dateityp bestimmen
- file_type = "image" if file.content_type and "image" in file.content_type else "document"
-
- # In Datenbank speichern
- new_file = lucy_interface.create_file(
- name=file.filename,
- file_type=file_type,
- content_type=file.content_type,
- size=os.path.getsize(file_path),
- path=file_path
- )
-
- return {
- "id": new_file["id"],
- "name": new_file["name"],
- "type": new_file["type"],
- "size": f"{new_file['size'] / (1024 * 1024):.1f} MB",
- "upload_date": new_file.get("upload_date")
- }
-
- except Exception as e:
- logger.error(f"Fehler beim Hochladen der Datei: {e}")
- raise HTTPException(status_code=500, detail=f"Fehler beim Hochladen der Datei: {str(e)}")
-
-
-@app.delete("/api/files/{file_id}", tags=["Files"])
-async def delete_file(
- file_id: int,
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Löscht eine Datei"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- try:
- # Hole die Datei aus der Datenbank
- file = lucy_interface.get_file(file_id)
- if not file:
- raise HTTPException(status_code=404, detail=f"Datei mit ID {file_id} nicht gefunden")
-
- # Prüfe, ob die physische Datei existiert
- if "path" in file and os.path.exists(file["path"]):
- try:
- # Versuche, die physische Datei zu löschen
- os.remove(file["path"])
- except Exception as e:
- logger.warning(f"Konnte physische Datei nicht löschen: {e}")
-
- # Lösche die Datei aus der Datenbank
- success = lucy_interface.delete_file(file_id)
-
- if not success:
- raise HTTPException(status_code=500, detail="Fehler beim Löschen der Datei aus der Datenbank")
-
- return {"success": True, "message": f"Datei '{file.get('name', 'unbekannt')}' wurde gelöscht"}
-
- except HTTPException:
- raise
- except Exception as e:
- logger.error(f"Fehler beim Löschen der Datei: {e}")
- raise HTTPException(status_code=500, detail=f"Fehler beim Löschen der Datei: {str(e)}")
-
-
-# Prompt-Endpunkte
-@app.get("/api/prompts", tags=["Prompts"], response_model=List[Dict[str, Any]])
-async def get_prompts(
- workspace_id: Optional[int] = Query(None),
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Alle Prompts oder Prompts eines bestimmten Workspaces abrufen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- if workspace_id:
- return lucy_interface.get_prompts_by_workspace(workspace_id)
-
- return lucy_interface.get_all_prompts()
-
-
-@app.post("/api/prompts", tags=["Prompts"], response_model=Dict[str, Any])
-async def create_prompt(
- prompt: Dict[str, Any] = Body(...),
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Einen neuen Prompt erstellen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- workspace_id = prompt.get("workspace_id")
-
- if not workspace_id:
- raise HTTPException(status_code=400, detail="workspace_id ist erforderlich")
-
- # Workspace existiert?
- workspace = lucy_interface.get_workspace(workspace_id)
- if not workspace:
- raise HTTPException(status_code=404, detail=f"Workspace mit ID {workspace_id} nicht gefunden")
-
- new_prompt = lucy_interface.create_prompt(
- content=prompt.get("content", ""),
- workspace_id=workspace_id
- )
-
- return new_prompt
-
-
-# Workflow-Endpunkte
-@app.post("/api/workflow/run", tags=["Workflow"], response_model=Dict[str, Any])
-async def run_workflow(
- workflow_request: WorkflowRequest = Body(...),
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Führt einen Workflow mit den ausgewählten Agenten und Dateien aus"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # LucyDOM-Interface mit Benutzerkontext initialisieren
- lucy_interface = get_lucydom_interface(mandate_id, user_id)
-
- # AgentService mit Benutzerkontext initialisieren
- agent_service = get_agent_service(mandate_id, user_id)
-
- workspace = lucy_interface.get_workspace(workflow_request.workspace_id)
- if not workspace:
- raise HTTPException(status_code=404, detail=f"Workspace mit ID {workflow_request.workspace_id} nicht gefunden")
-
- # Prüfen, ob Dateien existieren
- files = []
- for file_id in workflow_request.files:
- file = lucy_interface.get_file(file_id)
- if not file:
- raise HTTPException(status_code=404, detail=f"Datei mit ID {file_id} nicht gefunden")
- files.append(file)
-
- # Prüfen, ob Agenten existieren
- agents = []
- for agent_id in workflow_request.agents:
- agent = lucy_interface.get_agent(agent_id)
- if not agent:
- raise HTTPException(status_code=404, detail=f"Agent mit ID {agent_id} nicht gefunden")
- agents.append(agent)
-
- # Workflow ID generieren
- workflow_id = str(uuid.uuid4())
-
- # Workflow starten (asynchron)
- workflow_task = asyncio.create_task(
- agent_service.execute_workflow(
- workflow_id,
- workflow_request.prompt,
- agents,
- files
- )
- )
-
- # Sofort eine Antwort zurückgeben
- return {
- "workflow_id": workflow_id,
- "status": "running",
- "message": "Workflow wurde gestartet"
- }
-
-
-@app.get("/api/workflow/{workflow_id}/status", tags=["Workflow"])
-async def get_workflow_status(
- workflow_id: str,
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Status eines laufenden Workflows abrufen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # AgentService mit Benutzerkontext initialisieren
- agent_service = get_agent_service(mandate_id, user_id)
-
- status = agent_service.get_workflow_status(workflow_id)
- if not status:
- raise HTTPException(status_code=404, detail=f"Workflow mit ID {workflow_id} nicht gefunden")
-
- return status
-
-
-@app.get("/api/workflow/{workflow_id}/logs", tags=["Workflow"], response_model=List[Dict[str, Any]])
-async def get_workflow_logs(
- workflow_id: str,
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Protokolle eines Workflows abrufen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # AgentService mit Benutzerkontext initialisieren
- agent_service = get_agent_service(mandate_id, user_id)
-
- logs = agent_service.get_workflow_logs(workflow_id)
- if logs is None:
- raise HTTPException(status_code=404, detail=f"Workflow mit ID {workflow_id} nicht gefunden")
-
- return logs
-
-
-@app.get("/api/workflow/{workflow_id}/results", tags=["Workflow"], response_model=List[Dict[str, Any]])
-async def get_workflow_results(
- workflow_id: str,
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Ergebnisse eines Workflows abrufen"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # AgentService mit Benutzerkontext initialisieren
- agent_service = get_agent_service(mandate_id, user_id)
-
- results = agent_service.get_workflow_results(workflow_id)
- if results is None:
- raise HTTPException(status_code=404, detail=f"Workflow mit ID {workflow_id} nicht gefunden")
-
- return results
-
-
-# Mandanten-Endpunkte (neu)
-@app.get("/api/mandates", tags=["Mandates"], response_model=List[Dict[str, Any]])
-async def get_mandates(current_user: Dict[str, Any] = Depends(get_current_active_user)):
- """Alle verfügbaren Mandanten abrufen (nur für Admin-Benutzer)"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # Gateway-Interface mit Benutzerkontext initialisieren
- gateway = get_gateway_interface(mandate_id, user_id)
-
- # TODO: Hier sollte eine Berechtigungsprüfung erfolgen
-
- return gateway.get_all_mandates()
-
-
-@app.post("/api/mandates", tags=["Mandates"], response_model=Dict[str, Any])
-async def create_mandate(
- mandate: Dict[str, Any] = Body(...),
- current_user: Dict[str, Any] = Depends(get_current_active_user)
-):
- """Einen neuen Mandanten erstellen (nur für Admin-Benutzer)"""
- mandate_id, user_id = await get_user_context(current_user)
-
- # Gateway-Interface mit Benutzerkontext initialisieren
- gateway = get_gateway_interface(mandate_id, user_id)
-
- # TODO: Hier sollte eine Berechtigungsprüfung erfolgen
-
- new_mandate = gateway.create_mandate(
- name=mandate.get("name", "Neuer Mandant"),
- language=mandate.get("language", "de")
- )
-
- return new_mandate
+# Alle Router einbinden
+app.include_router(attributes_router)
+app.include_router(workspace_router)
+app.include_router(agent_router)
+app.include_router(file_router)
+app.include_router(prompt_router)
+app.include_router(workflow_router)
+app.include_router(mandate_router)
# Event handler beim Herunterfahren
diff --git a/gwserver/attributes.py b/gwserver/attributes.py
new file mode 100644
index 00000000..4ccc4e9a
--- /dev/null
+++ b/gwserver/attributes.py
@@ -0,0 +1,113 @@
+from pydantic import BaseModel, Field
+from typing import List, Dict, Any, Optional
+
+# Definiere das Modell für Attributdefinitionen
+class AttributeDefinition(BaseModel):
+ name: str
+ label: str
+ type: str
+ required: bool = False
+ placeholder: Optional[str] = None
+ default_value: Optional[Any] = None
+ options: Optional[List[Dict[str, Any]]] = None
+ editable: bool = True
+ visible: bool = True
+ order: int = 0
+ validation: Optional[Dict[str, Any]] = None
+ help_text: Optional[str] = None
+
+# Hilfsklassen für Typzuordnung
+type_mappings = {
+ "int": "number",
+ "str": "string",
+ "float": "number",
+ "bool": "boolean",
+ "List[int]": "array",
+ "List[str]": "array",
+ "Dict[str, Any]": "object",
+ "Optional[str]": "string",
+ "Optional[int]": "number",
+ "Optional[Dict[str, Any]]": "object"
+}
+
+# Spezielle Feldtypen basierend auf Namenskonventionen
+special_field_types = {
+ "content": "textarea",
+ "description": "textarea",
+ "instructions": "textarea",
+ "password": "password",
+ "email": "email",
+ "workspace_id": "select",
+ "agent_id": "select",
+ "type": "select"
+}
+
+# Funktion zum Konvertieren eines Pydantic-Modells in Attributdefinitionen
+def get_model_attributes(model_class, user_language="de"):
+ """
+ Konvertiert ein Pydantic-Modell in eine Liste von AttributeDefinition-Objekten
+ """
+ attributes = []
+
+ # Gehe alle Felder im Modell durch
+ for i, (field_name, field) in enumerate(model_class.__fields__.items()):
+ # Überspringe interne Felder
+ if field_name.startswith('_') or field_name in ["label", "field_labels"]:
+ continue
+
+ # Bestimme den Feldtyp
+ field_type = type_mappings.get(str(field.type_), "string")
+
+ # Prüfe auf spezielle Feldtypen
+ if field_name in special_field_types:
+ field_type = special_field_types[field_name]
+
+ # Hole das Label (falls vorhanden)
+ field_label = field_name.replace('_', ' ').capitalize()
+ if hasattr(model_class, 'field_labels') and field_name in model_class.field_labels:
+ label_obj = model_class.field_labels[field_name]
+ field_label = label_obj.get_label(user_language)
+
+ # Standardwerte und Required-Status ermitteln
+ required = field.required
+ default_value = field.default if not field.required else None
+
+ # Hinweise auf Validierungsregeln
+ validation = None
+ if field.validators:
+ validation = {"has_validators": True}
+
+ # Platzhaltertext
+ placeholder = f"Bitte {field_label} eingeben"
+
+ # Spezielle Optionen für Select-Felder
+ options = None
+ if field_type == "select":
+ if field_name == "type" and model_class.__name__ == "Agent":
+ options = [
+ {"value": "Analyse", "label": "Analyse"},
+ {"value": "Transformation", "label": "Transformation"},
+ {"value": "Generierung", "label": "Generierung"},
+ {"value": "Klassifikation", "label": "Klassifikation"},
+ {"value": "Benutzerdefiniert", "label": "Benutzerdefiniert"}
+ ]
+
+ # Attributdefinition erstellen
+ attr_def = AttributeDefinition(
+ name=field_name,
+ label=field_label,
+ type=field_type,
+ required=required,
+ placeholder=placeholder,
+ default_value=default_value,
+ options=options,
+ editable=field_name not in ["id", "mandate_id", "user_id", "created_at", "upload_date"],
+ visible=field_name not in ["hashed_password", "mandate_id", "user_id"],
+ order=i,
+ validation=validation,
+ help_text=field.description
+ )
+
+ attributes.append(attr_def)
+
+ return attributes
\ No newline at end of file
diff --git a/gwserver/auth.py b/gwserver/auth.py
index 8f032a4f..e0238edf 100644
--- a/gwserver/auth.py
+++ b/gwserver/auth.py
@@ -4,8 +4,8 @@ from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
import logging
-from interface_gateway import get_gateway_interface
-from model_gateway import User, Token
+from modules.gateway_interface import get_gateway_interface
+from modules.gateway_model import User, Token
import configload
diff --git a/gwserver/config.ini b/gwserver/config.ini
index 9c98fc39..1f0bcfba 100644
--- a/gwserver/config.ini
+++ b/gwserver/config.ini
@@ -1,7 +1,7 @@
[Application]
DEBUG = True
-UPLOAD_DIR = ./uploads
-RESULTS_DIR = ./results
+UPLOAD_DIR = ./_uploads
+RESULTS_DIR = ./_results
[Access]
SECRET_KEY = dein-geheimer-schlüssel
diff --git a/gwserver/interface_agentservice.py b/gwserver/modules/agentservice_interface.py
similarity index 100%
rename from gwserver/interface_agentservice.py
rename to gwserver/modules/agentservice_interface.py
diff --git a/gwserver/interface_gateway.py b/gwserver/modules/gateway_interface.py
similarity index 98%
rename from gwserver/interface_gateway.py
rename to gwserver/modules/gateway_interface.py
index 8fc726e2..4c43b375 100644
--- a/gwserver/interface_gateway.py
+++ b/gwserver/modules/gateway_interface.py
@@ -4,7 +4,7 @@ from typing import Dict, Any, List, Optional, Union
import importlib
from passlib.context import CryptContext
from connector_db_json import JSONDatabaseConnector
-from model_gateway import User, UserInDB, Token, Mandate
+from modules.gateway_model import User, UserInDB, Token, Mandate
logger = logging.getLogger(__name__)
@@ -33,9 +33,9 @@ class GatewayInterface:
self.user_id = user_id if user_id is not None else 1 # Admin-Benutzer als Standard
# Datenverzeichnis
- self.data_folder = "data_gateway"
+ self.data_folder = "_database_gateway"
os.makedirs(self.data_folder, exist_ok=True)
- logger.info("db for data_gateway atteached")
+ logger.info("db for data_gateway attached")
# Datenmodell-Modul importieren
try:
diff --git a/gwserver/model_gateway.py b/gwserver/modules/gateway_model.py
similarity index 100%
rename from gwserver/model_gateway.py
rename to gwserver/modules/gateway_model.py
diff --git a/gwserver/interface_lucydom.py b/gwserver/modules/lucydom_interface.py
similarity index 99%
rename from gwserver/interface_lucydom.py
rename to gwserver/modules/lucydom_interface.py
index 6f5248dc..0121a3f0 100644
--- a/gwserver/interface_lucydom.py
+++ b/gwserver/modules/lucydom_interface.py
@@ -26,7 +26,7 @@ class LucyDOMInterface:
self.user_id = user_id
# Datenverzeichnis
- self.data_folder = "data_lucydom"
+ self.data_folder = "_database_lucydom"
os.makedirs(self.data_folder, exist_ok=True)
# Datenmodell-Modul importieren
diff --git a/gwserver/model_lucydom.py b/gwserver/modules/lucydom_model.py
similarity index 100%
rename from gwserver/model_lucydom.py
rename to gwserver/modules/lucydom_model.py
diff --git a/gwserver/routes/agent.py b/gwserver/routes/agent.py
new file mode 100644
index 00000000..bb76eb2b
--- /dev/null
+++ b/gwserver/routes/agent.py
@@ -0,0 +1,91 @@
+from fastapi import APIRouter, HTTPException, Depends, Body, Query, Path
+from typing import List, Dict, Any, Optional
+from fastapi import status
+
+# Import auth module
+from auth import get_current_active_user, get_user_context
+
+# Import interfaces
+from modules.lucydom_interface import get_lucydom_interface
+
+# Router für Agenten-Endpunkte erstellen
+router = APIRouter(
+ prefix="/api/agents",
+ tags=["Agents"],
+ responses={404: {"description": "Not found"}}
+)
+
+@router.get("", response_model=List[Dict[str, Any]])
+async def get_agents(
+ workspace_id: Optional[int] = Query(None),
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Alle Agenten oder Agenten eines bestimmten Workspaces abrufen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ if workspace_id:
+ return lucy_interface.get_agents_by_workspace(workspace_id)
+
+ return lucy_interface.get_all_agents()
+
+
+@router.post("", response_model=Dict[str, Any])
+async def create_agent(
+ agent: Dict[str, Any] = Body(...),
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Einen neuen Agenten erstellen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ workspace_id = agent.get("workspace_id")
+
+ if not workspace_id:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="workspace_id ist erforderlich"
+ )
+
+ # Workspace existiert?
+ workspace = lucy_interface.get_workspace(workspace_id)
+ if not workspace:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Workspace mit ID {workspace_id} nicht gefunden"
+ )
+
+ new_agent = lucy_interface.create_agent(
+ name=agent.get("name", "Neuer Agent"),
+ agent_type=agent.get("type", "generic"),
+ workspace_id=workspace_id,
+ capabilities=agent.get("capabilities", []),
+ description=agent.get("description", "")
+ )
+
+ return new_agent
+
+
+@router.get("/{agent_id}", response_model=Dict[str, Any])
+async def get_agent(
+ agent_id: int,
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Einen bestimmten Agenten abrufen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ agent = lucy_interface.get_agent(agent_id)
+ if not agent:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Agent mit ID {agent_id} nicht gefunden"
+ )
+
+ return agent
\ No newline at end of file
diff --git a/gwserver/routes/attributes.py b/gwserver/routes/attributes.py
new file mode 100644
index 00000000..7b29112c
--- /dev/null
+++ b/gwserver/routes/attributes.py
@@ -0,0 +1,101 @@
+from fastapi import APIRouter, HTTPException, Depends, Path
+from typing import List, Dict, Any
+from fastapi import status
+
+# Import auth module
+from auth import get_current_active_user, get_user_context
+
+# Importiere die Attributdefinition und Hilfsfunktionen
+from attributes import AttributeDefinition, get_model_attributes
+
+# Importiere die Modellmodule (ohne spezifische Klassen)
+import modules.gateway_model as gateway_model
+import modules.lucydom_model as lucydom_model
+
+# Erstelle ein Dictionary, das die Modellklassen ihren Typen zuordnet
+model_classes = {
+ "workspace": lucydom_model.Workspace,
+ "agent": lucydom_model.Agent,
+ "file": lucydom_model.DataObject,
+ "prompt": lucydom_model.Prompt,
+ "user": gateway_model.User,
+ "mandate": gateway_model.Mandate,
+ "workflow_request": lucydom_model.WorkflowRequest,
+ "workflow_response": lucydom_model.WorkflowResponse,
+ "log_entry": lucydom_model.LogEntry,
+ "result": lucydom_model.Result
+}
+
+# Erstelle einen Router für die Attribute-Endpunkte
+router = APIRouter(
+ prefix="/api/attributes",
+ tags=["Attributes"],
+ responses={404: {"description": "Not found"}}
+)
+
+@router.get("/{entity_type}", response_model=List[AttributeDefinition])
+async def get_entity_attributes(
+ entity_type: str = Path(..., description="Typ der Entität (z.B. workspace, agent, prompt)"),
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """
+ Ruft die Attributdefinitionen für eine bestimmte Entität ab.
+ Dies kann für die dynamische Generierung von Formularen verwendet werden.
+ """
+ # Authentifizierung und Benutzerkontext
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # Bevorzugte Sprache des Benutzers ermitteln
+ user_language = current_user.get("language", "de")
+
+ # Prüfen, ob Entitätstyp bekannt ist
+ if entity_type not in model_classes:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Entitätstyp '{entity_type}' nicht gefunden."
+ )
+
+ # Model-Klasse abrufen und Attribute daraus ableiten
+ model_class = model_classes[entity_type]
+ attributes = get_model_attributes(model_class, user_language)
+
+ # Nur bearbeitbare und sichtbare Attribute zurückgeben
+ visible_attributes = [attr for attr in attributes if attr.visible]
+
+ # Wenn es sich um die Workspace-Auswahl handelt, Daten aus der Datenbank laden
+ for attr in visible_attributes:
+ if attr.name == "workspace_id" and attr.type == "select":
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ from modules.lucydom_interface import get_lucydom_interface
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ # Alle verfügbaren Workspaces abrufen
+ workspaces = lucy_interface.get_all_workspaces()
+
+ # Options-Liste aus Workspaces erstellen
+ attr.options = [
+ {"value": ws["id"], "label": ws["name"]}
+ for ws in workspaces
+ ]
+
+ # Wenn es sich um Agent-Auswahl handelt und ein Workspace-Kontext existiert
+ elif attr.name == "agent_id" and attr.type == "select" and "workspace_id" in [a.name for a in visible_attributes]:
+ # Wenn ein aktueller Workspace gesetzt ist
+ workspace_id_attr = next((a for a in visible_attributes if a.name == "workspace_id"), None)
+ if workspace_id_attr and workspace_id_attr.default_value:
+ workspace_id = workspace_id_attr.default_value
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ from modules.lucydom_interface import get_lucydom_interface
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ # Agenten für den Workspace abrufen
+ agents = lucy_interface.get_agents_by_workspace(workspace_id)
+
+ # Options-Liste aus Agenten erstellen
+ attr.options = [
+ {"value": agent["id"], "label": agent["name"]}
+ for agent in agents
+ ]
+
+ return visible_attributes
\ No newline at end of file
diff --git a/gwserver/routes/file.py b/gwserver/routes/file.py
new file mode 100644
index 00000000..9893ed1b
--- /dev/null
+++ b/gwserver/routes/file.py
@@ -0,0 +1,132 @@
+from fastapi import APIRouter, HTTPException, Depends, File, UploadFile, Path
+from typing import List, Dict, Any
+from fastapi import status
+import uuid
+import os
+import logging
+
+# Import auth module
+from auth import get_current_active_user, get_user_context
+
+# Import interfaces
+from modules.lucydom_interface import get_lucydom_interface
+from modules.agentservice_interface import get_agent_service
+
+# Logger konfigurieren
+logger = logging.getLogger(__name__)
+
+# Router für Datei-Endpunkte erstellen
+router = APIRouter(
+ prefix="/api/files",
+ tags=["Files"],
+ responses={404: {"description": "Not found"}}
+)
+
+@router.get("", response_model=List[Dict[str, Any]])
+async def get_files(current_user: Dict[str, Any] = Depends(get_current_active_user)):
+ """Alle verfügbaren Dateien abrufen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ return lucy_interface.get_all_files()
+
+
+@router.post("/upload")
+async def upload_file(
+ file: UploadFile = File(...),
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Eine Datei hochladen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ try:
+ # Generiere eine eindeutige ID für die Datei
+ file_id = str(uuid.uuid4())
+ file_ext = os.path.splitext(file.filename)[1]
+ file_path = os.path.join(get_agent_service(mandate_id, user_id).upload_dir, f"{file_id}{file_ext}")
+
+ # Datei speichern
+ with open(file_path, "wb") as f:
+ content = await file.read()
+ f.write(content)
+
+ # Dateityp bestimmen
+ file_type = "image" if file.content_type and "image" in file.content_type else "document"
+
+ # In Datenbank speichern
+ new_file = lucy_interface.create_file(
+ name=file.filename,
+ file_type=file_type,
+ content_type=file.content_type,
+ size=os.path.getsize(file_path),
+ path=file_path
+ )
+
+ return {
+ "id": new_file["id"],
+ "name": new_file["name"],
+ "type": new_file["type"],
+ "size": f"{new_file['size'] / (1024 * 1024):.1f} MB",
+ "upload_date": new_file.get("upload_date")
+ }
+
+ except Exception as e:
+ logger.error(f"Fehler beim Hochladen der Datei: {e}")
+ raise HTTPException(
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+ detail=f"Fehler beim Hochladen der Datei: {str(e)}"
+ )
+
+
+@router.delete("/{file_id}")
+async def delete_file(
+ file_id: int,
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Löscht eine Datei"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ try:
+ # Hole die Datei aus der Datenbank
+ file = lucy_interface.get_file(file_id)
+ if not file:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Datei mit ID {file_id} nicht gefunden"
+ )
+
+ # Prüfe, ob die physische Datei existiert
+ if "path" in file and os.path.exists(file["path"]):
+ try:
+ # Versuche, die physische Datei zu löschen
+ os.remove(file["path"])
+ except Exception as e:
+ logger.warning(f"Konnte physische Datei nicht löschen: {e}")
+
+ # Lösche die Datei aus der Datenbank
+ success = lucy_interface.delete_file(file_id)
+
+ if not success:
+ raise HTTPException(
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+ detail="Fehler beim Löschen der Datei aus der Datenbank"
+ )
+
+ return {"success": True, "message": f"Datei '{file.get('name', 'unbekannt')}' wurde gelöscht"}
+
+ except HTTPException:
+ raise
+ except Exception as e:
+ logger.error(f"Fehler beim Löschen der Datei: {e}")
+ raise HTTPException(
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+ detail=f"Fehler beim Löschen der Datei: {str(e)}"
+ )
\ No newline at end of file
diff --git a/gwserver/routes/mandate.py b/gwserver/routes/mandate.py
new file mode 100644
index 00000000..9c5f7b7c
--- /dev/null
+++ b/gwserver/routes/mandate.py
@@ -0,0 +1,73 @@
+from fastapi import APIRouter, HTTPException, Depends, Body
+from typing import List, Dict, Any
+from fastapi import status
+
+# Import auth module
+from auth import get_current_active_user, get_user_context
+
+# Import interfaces
+from modules.gateway_interface import get_gateway_interface
+
+# Router für Mandanten-Endpunkte erstellen
+router = APIRouter(
+ prefix="/api/mandates",
+ tags=["Mandates"],
+ responses={404: {"description": "Not found"}}
+)
+
+@router.get("", response_model=List[Dict[str, Any]])
+async def get_mandates(current_user: Dict[str, Any] = Depends(get_current_active_user)):
+ """Alle verfügbaren Mandanten abrufen (nur für Admin-Benutzer)"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # Gateway-Interface mit Benutzerkontext initialisieren
+ gateway = get_gateway_interface(mandate_id, user_id)
+
+ # TODO: Hier sollte eine Berechtigungsprüfung erfolgen
+
+ return gateway.get_all_mandates()
+
+
+@router.post("", response_model=Dict[str, Any])
+async def create_mandate(
+ mandate: Dict[str, Any] = Body(...),
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Einen neuen Mandanten erstellen (nur für Admin-Benutzer)"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # Gateway-Interface mit Benutzerkontext initialisieren
+ gateway = get_gateway_interface(mandate_id, user_id)
+
+ # TODO: Hier sollte eine Berechtigungsprüfung erfolgen
+
+ new_mandate = gateway.create_mandate(
+ name=mandate.get("name", "Neuer Mandant"),
+ language=mandate.get("language", "de")
+ )
+
+ return new_mandate
+
+
+@router.get("/{mandate_id}", response_model=Dict[str, Any])
+async def get_mandate(
+ mandate_id: int,
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Einen bestimmten Mandanten abrufen"""
+ user_mandate_id, user_id = await get_user_context(current_user)
+
+ # Gateway-Interface mit Benutzerkontext initialisieren
+ gateway = get_gateway_interface(user_mandate_id, user_id)
+
+ # TODO: Hier sollte eine Berechtigungsprüfung erfolgen
+ # Nur Admins oder Benutzer des gleichen Mandanten dürfen den Mandanten sehen
+
+ mandate = gateway.get_mandate(mandate_id)
+ if not mandate:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Mandant mit ID {mandate_id} nicht gefunden"
+ )
+
+ return mandate
\ No newline at end of file
diff --git a/gwserver/routes/prompt.py b/gwserver/routes/prompt.py
new file mode 100644
index 00000000..c069164b
--- /dev/null
+++ b/gwserver/routes/prompt.py
@@ -0,0 +1,88 @@
+from fastapi import APIRouter, HTTPException, Depends, Body, Query, Path
+from typing import List, Dict, Any, Optional
+from fastapi import status
+
+# Import auth module
+from auth import get_current_active_user, get_user_context
+
+# Import interfaces
+from modules.lucydom_interface import get_lucydom_interface
+
+# Router für Prompt-Endpunkte erstellen
+router = APIRouter(
+ prefix="/api/prompts",
+ tags=["Prompts"],
+ responses={404: {"description": "Not found"}}
+)
+
+@router.get("", response_model=List[Dict[str, Any]])
+async def get_prompts(
+ workspace_id: Optional[int] = Query(None),
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Alle Prompts oder Prompts eines bestimmten Workspaces abrufen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ if workspace_id:
+ return lucy_interface.get_prompts_by_workspace(workspace_id)
+
+ return lucy_interface.get_all_prompts()
+
+
+@router.post("", response_model=Dict[str, Any])
+async def create_prompt(
+ prompt: Dict[str, Any] = Body(...),
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Einen neuen Prompt erstellen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ workspace_id = prompt.get("workspace_id")
+
+ if not workspace_id:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="workspace_id ist erforderlich"
+ )
+
+ # Workspace existiert?
+ workspace = lucy_interface.get_workspace(workspace_id)
+ if not workspace:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Workspace mit ID {workspace_id} nicht gefunden"
+ )
+
+ new_prompt = lucy_interface.create_prompt(
+ content=prompt.get("content", ""),
+ workspace_id=workspace_id
+ )
+
+ return new_prompt
+
+
+@router.get("/{prompt_id}", response_model=Dict[str, Any])
+async def get_prompt(
+ prompt_id: int,
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Einen bestimmten Prompt abrufen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ prompt = lucy_interface.get_prompt(prompt_id)
+ if not prompt:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Prompt mit ID {prompt_id} nicht gefunden"
+ )
+
+ return prompt
\ No newline at end of file
diff --git a/gwserver/routes/workflow.py b/gwserver/routes/workflow.py
new file mode 100644
index 00000000..f762e420
--- /dev/null
+++ b/gwserver/routes/workflow.py
@@ -0,0 +1,148 @@
+from fastapi import APIRouter, HTTPException, Depends, Body, Path
+from typing import List, Dict, Any
+from fastapi import status
+import asyncio
+import uuid
+
+# Import auth module
+from auth import get_current_active_user, get_user_context
+
+# Import interfaces
+from modules.lucydom_interface import get_lucydom_interface
+from modules.agentservice_interface import get_agent_service
+
+# Import models
+import modules.lucydom_model as lucydom_model
+
+# Router für Workflow-Endpunkte erstellen
+router = APIRouter(
+ prefix="/api/workflow",
+ tags=["Workflow"],
+ responses={404: {"description": "Not found"}}
+)
+
+@router.post("/run", response_model=Dict[str, Any])
+async def run_workflow(
+ workflow_request: lucydom_model.WorkflowRequest = Body(...),
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Führt einen Workflow mit den ausgewählten Agenten und Dateien aus"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ # AgentService mit Benutzerkontext initialisieren
+ agent_service = get_agent_service(mandate_id, user_id)
+
+ workspace = lucy_interface.get_workspace(workflow_request.workspace_id)
+ if not workspace:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Workspace mit ID {workflow_request.workspace_id} nicht gefunden"
+ )
+
+ # Prüfen, ob Dateien existieren
+ files = []
+ for file_id in workflow_request.files:
+ file = lucy_interface.get_file(file_id)
+ if not file:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Datei mit ID {file_id} nicht gefunden"
+ )
+ files.append(file)
+
+ # Prüfen, ob Agenten existieren
+ agents = []
+ for agent_id in workflow_request.agents:
+ agent = lucy_interface.get_agent(agent_id)
+ if not agent:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Agent mit ID {agent_id} nicht gefunden"
+ )
+ agents.append(agent)
+
+ # Workflow ID generieren
+ workflow_id = str(uuid.uuid4())
+
+ # Workflow starten (asynchron)
+ workflow_task = asyncio.create_task(
+ agent_service.execute_workflow(
+ workflow_id,
+ workflow_request.prompt,
+ agents,
+ files
+ )
+ )
+
+ # Sofort eine Antwort zurückgeben
+ return {
+ "workflow_id": workflow_id,
+ "status": "running",
+ "message": "Workflow wurde gestartet"
+ }
+
+
+@router.get("/{workflow_id}/status")
+async def get_workflow_status(
+ workflow_id: str,
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Status eines laufenden Workflows abrufen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # AgentService mit Benutzerkontext initialisieren
+ agent_service = get_agent_service(mandate_id, user_id)
+
+ status = agent_service.get_workflow_status(workflow_id)
+ if not status:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Workflow mit ID {workflow_id} nicht gefunden"
+ )
+
+ return status
+
+
+@router.get("/{workflow_id}/logs", response_model=List[Dict[str, Any]])
+async def get_workflow_logs(
+ workflow_id: str,
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Protokolle eines Workflows abrufen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # AgentService mit Benutzerkontext initialisieren
+ agent_service = get_agent_service(mandate_id, user_id)
+
+ logs = agent_service.get_workflow_logs(workflow_id)
+ if logs is None:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Workflow mit ID {workflow_id} nicht gefunden"
+ )
+
+ return logs
+
+
+@router.get("/{workflow_id}/results", response_model=List[Dict[str, Any]])
+async def get_workflow_results(
+ workflow_id: str,
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Ergebnisse eines Workflows abrufen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # AgentService mit Benutzerkontext initialisieren
+ agent_service = get_agent_service(mandate_id, user_id)
+
+ results = agent_service.get_workflow_results(workflow_id)
+ if results is None:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Workflow mit ID {workflow_id} nicht gefunden"
+ )
+
+ return results
\ No newline at end of file
diff --git a/gwserver/routes/workspace.py b/gwserver/routes/workspace.py
new file mode 100644
index 00000000..c252e190
--- /dev/null
+++ b/gwserver/routes/workspace.py
@@ -0,0 +1,62 @@
+from fastapi import APIRouter, HTTPException, Depends, Body, Path
+from typing import List, Dict, Any
+from fastapi import status
+
+# Import auth module
+from auth import get_current_active_user, get_user_context
+
+# Import interfaces
+from modules.lucydom_interface import get_lucydom_interface
+
+# Router für Workspace-Endpunkte erstellen
+router = APIRouter(
+ prefix="/api/workspaces",
+ tags=["Workspaces"],
+ responses={404: {"description": "Not found"}}
+)
+
+@router.get("", response_model=List[Dict[str, Any]])
+async def get_workspaces(current_user: Dict[str, Any] = Depends(get_current_active_user)):
+ """Alle verfügbaren Workspaces abrufen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ return lucy_interface.get_all_workspaces()
+
+
+@router.get("/{workspace_id}", response_model=Dict[str, Any])
+async def get_workspace(
+ workspace_id: int,
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Einen bestimmten Workspace mit allen Details abrufen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ workspace = lucy_interface.get_workspace(workspace_id)
+ if not workspace:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Workspace mit ID {workspace_id} nicht gefunden"
+ )
+
+ return workspace
+
+
+@router.post("", response_model=Dict[str, Any])
+async def create_workspace(
+ workspace: Dict[str, Any] = Body(...),
+ current_user: Dict[str, Any] = Depends(get_current_active_user)
+):
+ """Einen neuen Workspace erstellen"""
+ mandate_id, user_id = await get_user_context(current_user)
+
+ # LucyDOM-Interface mit Benutzerkontext initialisieren
+ lucy_interface = get_lucydom_interface(mandate_id, user_id)
+
+ new_workspace = lucy_interface.create_workspace(name=workspace.get("name", "Neuer Workspace"))
+ return new_workspace
\ No newline at end of file