refactored gateway for scaling

This commit is contained in:
valueon 2025-03-16 23:50:27 +01:00
parent 5d5fb4d56f
commit 39f33b0fb8
24 changed files with 843 additions and 420 deletions

View file

@ -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.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, Response from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer from fastapi.security import OAuth2PasswordRequestForm
import uvicorn import uvicorn
from typing import List, Dict, Any, Optional from typing import Dict, Any
import uuid
import os import os
import json
import asyncio
import logging import logging
from datetime import datetime, timedelta from datetime import timedelta
# Import interfaces # Import interfaces
from interface_gateway import get_gateway_interface from modules.gateway_interface import get_gateway_interface
from interface_lucydom import get_lucydom_interface from modules.agentservice_interface import get_agent_service
from interface_agentservice import get_agent_service
# Import auth module # Import auth module
from auth import ( from auth import (
create_access_token, create_access_token,
get_current_user,
get_current_active_user, get_current_active_user,
get_user_context, get_user_context,
ACCESS_TOKEN_EXPIRE_MINUTES ACCESS_TOKEN_EXPIRE_MINUTES
) )
# Import models (restructured) # Import models - generisch importieren
from model_gateway import User, UserInDB, Token, Mandate import modules.gateway_model as gateway_model
from model_lucydom import (
Workspace,
Agent,
DataObject,
Prompt,
WorkflowRequest,
WorkflowResponse,
LogEntry,
Result
)
# 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 # Konfiguration des Loggers
logging.basicConfig( logging.basicConfig(
@ -60,8 +53,6 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
# API - ENDPUNKTE
# Statischer Folder für Frontend # Statischer Folder für Frontend
os.makedirs(os.path.join(os.getcwd(), "static"), exist_ok=True) os.makedirs(os.path.join(os.getcwd(), "static"), exist_ok=True)
app.mount("/static", StaticFiles(directory="static"), name="static") app.mount("/static", StaticFiles(directory="static"), name="static")
@ -77,7 +68,7 @@ async def get_test():
return "OK 1.0" return "OK 1.0"
# Token-Endpunkt für Login # 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()): async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
# Gateway-Interface ohne Kontext initialisieren # Gateway-Interface ohne Kontext initialisieren
gateway = get_gateway_interface() gateway = get_gateway_interface()
@ -106,7 +97,7 @@ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(
# Benutzerregistrierung # 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)): async def register_user(user_data: dict = Body(...), current_user: Dict[str, Any] = Depends(get_current_active_user)):
"""Neuen Benutzer registrieren""" """Neuen Benutzer registrieren"""
# Nur Benutzer des gleichen Mandanten dürfen erstellt werden # 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 return current_user
# Workspace-Endpunkte # Alle Router einbinden
@app.get("/api/workspaces", tags=["Workspaces"], response_model=List[Dict[str, Any]]) app.include_router(attributes_router)
async def get_workspaces(current_user: Dict[str, Any] = Depends(get_current_active_user)): app.include_router(workspace_router)
"""Alle verfügbaren Workspaces abrufen""" app.include_router(agent_router)
mandate_id, user_id = await get_user_context(current_user) app.include_router(file_router)
app.include_router(prompt_router)
# LucyDOM-Interface mit Benutzerkontext initialisieren app.include_router(workflow_router)
lucy_interface = get_lucydom_interface(mandate_id, user_id) app.include_router(mandate_router)
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
# Event handler beim Herunterfahren # Event handler beim Herunterfahren

113
gwserver/attributes.py Normal file
View file

@ -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

View file

@ -4,8 +4,8 @@ from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt from jose import JWTError, jwt
import logging import logging
from interface_gateway import get_gateway_interface from modules.gateway_interface import get_gateway_interface
from model_gateway import User, Token from modules.gateway_model import User, Token
import configload import configload

View file

@ -1,7 +1,7 @@
[Application] [Application]
DEBUG = True DEBUG = True
UPLOAD_DIR = ./uploads UPLOAD_DIR = ./_uploads
RESULTS_DIR = ./results RESULTS_DIR = ./_results
[Access] [Access]
SECRET_KEY = dein-geheimer-schlüssel SECRET_KEY = dein-geheimer-schlüssel

View file

@ -4,7 +4,7 @@ from typing import Dict, Any, List, Optional, Union
import importlib import importlib
from passlib.context import CryptContext from passlib.context import CryptContext
from connector_db_json import JSONDatabaseConnector 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__) 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 self.user_id = user_id if user_id is not None else 1 # Admin-Benutzer als Standard
# Datenverzeichnis # Datenverzeichnis
self.data_folder = "data_gateway" self.data_folder = "_database_gateway"
os.makedirs(self.data_folder, exist_ok=True) 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 # Datenmodell-Modul importieren
try: try:

View file

@ -26,7 +26,7 @@ class LucyDOMInterface:
self.user_id = user_id self.user_id = user_id
# Datenverzeichnis # Datenverzeichnis
self.data_folder = "data_lucydom" self.data_folder = "_database_lucydom"
os.makedirs(self.data_folder, exist_ok=True) os.makedirs(self.data_folder, exist_ok=True)
# Datenmodell-Modul importieren # Datenmodell-Modul importieren

91
gwserver/routes/agent.py Normal file
View file

@ -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

View file

@ -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

132
gwserver/routes/file.py Normal file
View file

@ -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)}"
)

View file

@ -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

88
gwserver/routes/prompt.py Normal file
View file

@ -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

148
gwserver/routes/workflow.py Normal file
View file

@ -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

View file

@ -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