all components refactored
This commit is contained in:
parent
5c066feb19
commit
f794872b45
11 changed files with 211 additions and 204 deletions
60
app.py
60
app.py
|
|
@ -6,6 +6,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|||
from fastapi.responses import JSONResponse, FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
import uvicorn
|
||||
from typing import Dict, Any
|
||||
|
|
@ -17,8 +18,6 @@ import pathlib
|
|||
from modules.configuration import APP_CONFIG
|
||||
from modules.gateway_interface import get_gateway_interface
|
||||
|
||||
|
||||
|
||||
# Import auth module
|
||||
from modules.auth import (
|
||||
create_access_token,
|
||||
|
|
@ -27,7 +26,7 @@ from modules.auth import (
|
|||
ACCESS_TOKEN_EXPIRE_MINUTES
|
||||
)
|
||||
|
||||
# Import models - generisch importieren zur INITIALISIERUNG, auch wenn dummy!
|
||||
# Import models - import generically for INITIALIZATION, even if dummy!
|
||||
import modules.gateway_model as gateway_model
|
||||
import modules.lucydom_interface as lucydom_model
|
||||
|
||||
|
|
@ -67,14 +66,28 @@ def init_logging():
|
|||
)
|
||||
|
||||
|
||||
# START APP
|
||||
|
||||
# Initialize logging
|
||||
init_logging()
|
||||
logger = logging.getLogger(__name__)
|
||||
instance_label=APP_CONFIG.get("APP_ENV_LABEL")
|
||||
app = FastAPI(title="PowerOn | Data Platform API", description=f"Backend-API für die Multi-Agent Platform von ValueOn AG ({instance_label})")
|
||||
instance_label = APP_CONFIG.get("APP_ENV_LABEL")
|
||||
|
||||
# CORS-Konfiguration für Frontend-Anfragen
|
||||
# Define lifespan context manager for application startup/shutdown events
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# Startup logic (if any)
|
||||
logger.info("Application is starting up")
|
||||
yield
|
||||
# Shutdown logic
|
||||
logger.info("Application has been shut down")
|
||||
|
||||
# START APP
|
||||
app = FastAPI(
|
||||
title="PowerOn | Data Platform API",
|
||||
description=f"Backend API for the Multi-Agent Platform by ValueOn AG ({instance_label})",
|
||||
lifespan=lifespan
|
||||
)
|
||||
|
||||
# CORS configuration for frontend requests
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["http://localhost:8080","https://poweron-lucyagents-xxx.germanywestcentral-01.azurewebsites.net"],
|
||||
|
|
@ -82,10 +95,10 @@ app.add_middleware(
|
|||
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||
allow_headers=["*"],
|
||||
expose_headers=["*"],
|
||||
max_age=86400 # Erhöhung des Caching für Preflight-Anfragen
|
||||
max_age=86400 # Increased caching for preflight requests
|
||||
)
|
||||
|
||||
# Statischer Folder für Frontend - mit absolutem Pfad arbeiten
|
||||
# Static folder for frontend - work with absolute path
|
||||
base_dir = pathlib.Path(__file__).parent
|
||||
static_folder = base_dir / "static"
|
||||
os.makedirs(static_folder, exist_ok=True)
|
||||
|
|
@ -97,11 +110,11 @@ async def favicon():
|
|||
favicon_path = static_folder / "favicon.ico"
|
||||
return FileResponse(str(favicon_path))
|
||||
|
||||
# Generelle Elements
|
||||
# General Elements
|
||||
@app.get("/", tags=["General"])
|
||||
async def root():
|
||||
"""API-Statusendpunkt"""
|
||||
return {"status": "online", "message": "Data Platform API ist aktiv"}
|
||||
"""API status endpoint"""
|
||||
return {"status": "online", "message": "Data Platform API is active"}
|
||||
|
||||
@app.get("/api/test", tags=["General"])
|
||||
async def get_test():
|
||||
|
|
@ -112,23 +125,23 @@ async def options_route(full_path: str):
|
|||
return Response(status_code=200)
|
||||
|
||||
|
||||
# Token-Endpunkt für Login
|
||||
# Token endpoint for login
|
||||
@app.post("/api/token", response_model=gateway_model.Token, tags=["General"])
|
||||
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
# Gateway-Interface ohne Kontext initialisieren
|
||||
# Initialize Gateway interface without context
|
||||
gateway = get_gateway_interface()
|
||||
|
||||
# Benutzer authentifizieren
|
||||
# Authenticate user
|
||||
user = gateway.authenticate_user(form_data.username, form_data.password)
|
||||
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Ungültiger Benutzername oder Passwort",
|
||||
detail="Invalid username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# Token mit Mandanten-ID erstellen
|
||||
# Create token with tenant ID
|
||||
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={
|
||||
|
|
@ -140,12 +153,12 @@ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(
|
|||
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
# Benutzerinfo abrufen
|
||||
# Get user info
|
||||
@app.get("/api/user/me", response_model=Dict[str, Any], tags=["General"])
|
||||
async def read_user_me(current_user: Dict[str, Any] = Depends(get_current_active_user)):
|
||||
return current_user
|
||||
|
||||
# Alle Router einbinden
|
||||
# Include all routers
|
||||
from routes.attributes import router as attributes_router
|
||||
app.include_router(attributes_router)
|
||||
|
||||
|
|
@ -164,12 +177,5 @@ app.include_router(prompt_router)
|
|||
from routes.workflows import router as workflow_router
|
||||
app.include_router(workflow_router)
|
||||
|
||||
|
||||
# Event handler beim Herunterfahren
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown_event():
|
||||
"""Führt Aufräumarbeiten beim Herunterfahren der Anwendung durch"""
|
||||
logger.info("Anwendung wurde heruntergefahren")
|
||||
|
||||
#if __name__ == "__main__":
|
||||
# uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
|
||||
|
|
@ -343,7 +343,6 @@ JSON_OUTPUT = {{
|
|||
"current_round": 1,
|
||||
"status": "running",
|
||||
"last_activity": current_time,
|
||||
"waiting_for_user": False
|
||||
}
|
||||
|
||||
# Save to database - only the workflow metadata
|
||||
|
|
@ -357,7 +356,6 @@ JSON_OUTPUT = {{
|
|||
"data_stats": workflow["data_stats"],
|
||||
"current_round": workflow["current_round"],
|
||||
"last_activity": workflow["last_activity"],
|
||||
"waiting_for_user": workflow["waiting_for_user"],
|
||||
"message_ids": workflow["message_ids"] # Include message_ids
|
||||
}
|
||||
self.lucy_interface.create_workflow(workflow_db)
|
||||
|
|
@ -378,7 +376,6 @@ JSON_OUTPUT = {{
|
|||
# Update status and increment round counter
|
||||
workflow["status"] = "running"
|
||||
workflow["last_activity"] = current_time
|
||||
workflow["waiting_for_user"] = False
|
||||
|
||||
# Increment current_round if it exists, otherwise set it to 1
|
||||
if "current_round" in workflow:
|
||||
|
|
@ -390,7 +387,6 @@ JSON_OUTPUT = {{
|
|||
workflow_update = {
|
||||
"status": workflow["status"],
|
||||
"last_activity": workflow["last_activity"],
|
||||
"waiting_for_user": workflow["waiting_for_user"],
|
||||
"current_round": workflow["current_round"]
|
||||
}
|
||||
self.lucy_interface.update_workflow(workflow_id, workflow_update)
|
||||
|
|
@ -411,13 +407,11 @@ JSON_OUTPUT = {{
|
|||
workflow_update = {
|
||||
"status": "completed",
|
||||
"last_activity": datetime.now().isoformat(),
|
||||
"waiting_for_user": True
|
||||
}
|
||||
|
||||
# Update the workflow object in memory
|
||||
workflow["status"] = workflow_update["status"]
|
||||
workflow["last_activity"] = workflow_update["last_activity"]
|
||||
workflow["waiting_for_user"] = workflow_update["waiting_for_user"]
|
||||
|
||||
# Save workflow state to database - only relevant fields, not the messages list
|
||||
self.lucy_interface.update_workflow(workflow["id"], workflow_update)
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ class AgentWebcrawler(AgentBase):
|
|||
return plan
|
||||
else:
|
||||
# Fallback plan
|
||||
logger.warning(f"Not able creating research plan, generating fallback plan")
|
||||
return {
|
||||
"requires_web_research": True,
|
||||
"research_questions": ["What information can be found about this topic?"],
|
||||
|
|
@ -304,6 +305,7 @@ class AgentWebcrawler(AgentBase):
|
|||
result["summary"] = summary
|
||||
else:
|
||||
# Fallback if no AI service
|
||||
logger.warning(f"Not able to summarize result, using fallback plan.")
|
||||
result["summary"] = f"Content from {result['url']} ({len(content)} characters)"
|
||||
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -1035,7 +1035,7 @@ class LucyDOMInterface:
|
|||
"mandate_id": workflow.get("mandate_id", self.mandate_id),
|
||||
"user_id": workflow.get("user_id", self.user_id),
|
||||
"name": workflow.get("name", f"Workflow {workflow_id}"),
|
||||
"status": workflow.get("status", "unknown"),
|
||||
"status": workflow.get("status", "completed"),
|
||||
"started_at": workflow.get("started_at", self._get_current_timestamp()),
|
||||
"last_activity": workflow.get("last_activity", self._get_current_timestamp()),
|
||||
"data_stats": workflow.get("data_stats", {})
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ class Workflow(BaseModel):
|
|||
name: Optional[str] = Field(None, description="Name of the workflow")
|
||||
mandate_id: int = Field(description="ID of the mandate")
|
||||
user_id: int = Field(description="ID of the user")
|
||||
status: str = Field(description="Status of the workflow ('running', 'failed', 'stopped')")
|
||||
status: str = Field(description="Status of the workflow ('running', 'completed')")
|
||||
started_at: str = Field(description="Start timestamp")
|
||||
last_activity: str = Field(description="Timestamp of the last activity")
|
||||
message_ids: List[str] = Field(default=[], description="List of message IDs in this workflow")
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
....................... TASKS
|
||||
|
||||
|
||||
can you do following adaptions
|
||||
can you do following adaptions for the workflow management for the frontend:
|
||||
- german comments in logs and prompts to translate to english. where to adapt what?
|
||||
- can you enhance all ai prompts to include, that the output is delivered in the language of the user? Perhaps an option to have a global variable for this, which is also transferred with the task to the agents? Perhaps to do simple ai call with some words to ask AI? I want a solution with minimum impact to the code and simple to use.
|
||||
- can you check all self.log_add(...) statements and rearrange them. They are for the progress of a workflow to show in the front-end. I want all messages to be in a standardizes format and organized along the workflow, that user understands the logical progress. Not too much information, but the relevant steps to show. Within loops to tell progress in percent by having a log_add in the loops (so to add progress attribute to the function call)
|
||||
|
||||
everywhere:
|
||||
- to remove base64 checks ot tests. only to use base64_encoded attribute
|
||||
- to use the enhanced attributes for document ("data" containing filedata in base64 format) and content ("data", "base64_encoded", "data_extracted")
|
||||
topics for log_add log_entry object:
|
||||
- always to include workflow "status" for frontend polling support
|
||||
- agent_id to remove
|
||||
- agent_name to take from a global variable in the according module (same global variables set like global user language)
|
||||
- add atrribute to show progress
|
||||
|
||||
please tell me, where to adapt what in the code. I do not neew fully new code.
|
||||
please deliver adapted modules when more than 3 parts have to be adapted, otherwise the parts to adapt.
|
||||
|
||||
|
||||
|
||||
|
|
@ -16,16 +21,12 @@ please tell me, where to adapt what in the code. I do not neew fully new code.
|
|||
|
||||
|
||||
|
||||
german comments in logs and prompts to translate to english. where to adapt what?
|
||||
|
||||
can you enhance all ai prompts to include, that the output is delivered in the language of the user?
|
||||
An option to have a global variable for this, which is also trasferred with the task to the agents?
|
||||
|
||||
streamline self.log_add --> to use in a standardized format and to reduce messages to relevant steps
|
||||
|
||||
add connector to myoutlook
|
||||
|
||||
todo an agent for "code writing and editing" connected to the codebase, working in loops over each document...
|
||||
FRONTEND:
|
||||
- General: Adapt to backend changes and simplify polling and frontend objects status, remove unnecessary elements.
|
||||
- Workflow object has only one attribute for status, this is "status" with value "completed" or "running". All other status objects for workflow to remove.
|
||||
- polling start/finish and frontend elements status have only to look for "status" value of workflow. especially all the routines for button "stop", "send", animations only rely on this status.
|
||||
- for log entries to show: check last log-entry and also change in progress attribute of last entry
|
||||
|
||||
|
||||
|
||||
|
|
@ -33,13 +34,17 @@ todo an agent for "code writing and editing" connected to the codebase, working
|
|||
|
||||
PRIO1:
|
||||
|
||||
sharepoint connector with document search, content search, content extraction
|
||||
|
||||
|
||||
add connector to myoutlook
|
||||
|
||||
todo an agent for "code writing and editing" connected to the codebase, working in loops over each document...
|
||||
|
||||
sharepoint connector with document search, content search, content extraction
|
||||
|
||||
Split big files into content-parts
|
||||
|
||||
|
||||
|
||||
PRIO2:
|
||||
|
||||
implement cleanup routines for files in lucydom_interface (File_Management_CLEANUP_INTERVAL): temp older than interval, all orphaned
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from fastapi import status
|
|||
from modules.auth import get_current_active_user, get_user_context
|
||||
|
||||
# Importiere die Attributdefinition und Hilfsfunktionen
|
||||
from modules.attributes import AttributeDefinition, get_model_attributes
|
||||
from gateway.modules.def_attributes import AttributeDefinition, get_model_attributes
|
||||
|
||||
# Importiere die Modellmodule (ohne spezifische Klassen)
|
||||
import modules.gateway_model as gateway_model
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ from modules.configuration import APP_CONFIG
|
|||
from modules.lucydom_interface import get_lucydom_interface, FileError, FileNotFoundError, FileStorageError, FilePermissionError, FileDeletionError
|
||||
from modules.lucydom_model import FileItem
|
||||
|
||||
# Logger konfigurieren
|
||||
# Configure logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Alle Attribute des Models ermitteln (außer interne/spezielle Attribute)
|
||||
# Get all attributes of the model (except internal/special attributes)
|
||||
def get_model_attributes(model_class):
|
||||
return [attr for attr in dir(model_class)
|
||||
if not callable(getattr(model_class, attr))
|
||||
|
|
@ -27,25 +27,25 @@ def get_model_attributes(model_class):
|
|||
and attr != 'label'
|
||||
and attr != 'field_labels']
|
||||
|
||||
# Modell-Attribute für FileItem
|
||||
# Model attributes for FileItem
|
||||
file_attributes = get_model_attributes(FileItem)
|
||||
|
||||
@dataclass
|
||||
class AppContext:
|
||||
"""Kontext-Objekt für alle benötigten Verbindungen und Benutzerinformationen"""
|
||||
"""Context object for all required connections and user information"""
|
||||
mandate_id: int
|
||||
user_id: int
|
||||
interface_data: Any # LucyDOM Interface
|
||||
|
||||
async def get_context(current_user: Dict[str, Any]) -> AppContext:
|
||||
"""
|
||||
Erstellt ein zentrales Kontext-Objekt mit allen benötigten Interfaces
|
||||
Creates a central context object with all required interfaces
|
||||
|
||||
Args:
|
||||
current_user: Aktueller Benutzer aus der Authentifizierung
|
||||
current_user: Current user from authentication
|
||||
|
||||
Returns:
|
||||
AppContext-Objekt mit allen benötigten Verbindungen
|
||||
AppContext object with all required connections
|
||||
"""
|
||||
mandate_id, user_id = await get_user_context(current_user)
|
||||
interface_data = get_lucydom_interface(mandate_id, user_id)
|
||||
|
|
@ -56,7 +56,7 @@ async def get_context(current_user: Dict[str, Any]) -> AppContext:
|
|||
interface_data=interface_data
|
||||
)
|
||||
|
||||
# Router für Datei-Endpunkte erstellen
|
||||
# Create router for file endpoints
|
||||
router = APIRouter(
|
||||
prefix="/api/files",
|
||||
tags=["Files"],
|
||||
|
|
@ -71,18 +71,18 @@ router = APIRouter(
|
|||
|
||||
@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"""
|
||||
"""Get all available files"""
|
||||
try:
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Alle Dateien generisch abrufen - nur Metadaten, keine Binärdaten
|
||||
# Get all files generically - only metadata, no binary data
|
||||
files = context.interface_data.get_all_files()
|
||||
return files
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Abrufen der Dateien: {str(e)}")
|
||||
logger.error(f"Error retrieving files: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Fehler beim Abrufen der Dateien: {str(e)}"
|
||||
detail=f"Error retrieving files: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -93,45 +93,45 @@ async def upload_file(
|
|||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""
|
||||
Upload einer Datei
|
||||
Upload a file
|
||||
"""
|
||||
try:
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Datei einlesen
|
||||
# Read file
|
||||
file_content = await file.read()
|
||||
|
||||
# Größenbeschränkung prüfen
|
||||
max_size = int(APP_CONFIG.get("File_Management_MAX_UPLOAD_SIZE_MB")) * 1024 * 1024 # in Bytes
|
||||
# Check size limits
|
||||
max_size = int(APP_CONFIG.get("File_Management_MAX_UPLOAD_SIZE_MB")) * 1024 * 1024 # in bytes
|
||||
if len(file_content) > max_size:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
|
||||
detail=f"Datei zu groß. Maximale Größe: {APP_CONFIG.get('File_Management_MAX_UPLOAD_SIZE_MB')}MB"
|
||||
detail=f"File too large. Maximum size: {APP_CONFIG.get('File_Management_MAX_UPLOAD_SIZE_MB')}MB"
|
||||
)
|
||||
|
||||
# Datei über das LucyDOM-Interface in der Datenbank speichern
|
||||
# Save file via LucyDOM interface in the database
|
||||
file_meta = context.interface_data.save_uploaded_file(file_content, file.filename)
|
||||
|
||||
# Wenn workflow_id angegeben, aktualisiere die Dateiinformationen
|
||||
# If workflow_id is provided, update the file information
|
||||
if workflow_id:
|
||||
update_data = {"workflow_id": workflow_id}
|
||||
context.interface_data.update_file(file_meta["id"], update_data)
|
||||
file_meta["workflow_id"] = workflow_id
|
||||
|
||||
# Erfolgreiche Antwort
|
||||
# Successful response
|
||||
return file_meta
|
||||
|
||||
except FileStorageError as e:
|
||||
logger.error(f"Fehler beim Datei-Upload (Speichern): {str(e)}")
|
||||
logger.error(f"Error during file upload (storage): {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=str(e)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Datei-Upload: {str(e)}")
|
||||
logger.error(f"Error during file upload: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Fehler beim Datei-Upload: {str(e)}"
|
||||
detail=f"Error during file upload: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -141,17 +141,17 @@ async def get_file(
|
|||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""
|
||||
Gibt eine Datei anhand ihrer ID zum Download zurück.
|
||||
Ruft sowohl Metadaten als auch Binärdaten ab.
|
||||
Returns a file by its ID for download.
|
||||
Retrieves both metadata and binary data.
|
||||
"""
|
||||
try:
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Datei über das LucyDOM-Interface aus der Datenbank abrufen
|
||||
# Verwendet die download_file-Methode, die nun Metadaten und Binärdaten kombiniert
|
||||
# Get file via LucyDOM interface from the database
|
||||
# Uses the download_file method, which now combines metadata and binary data
|
||||
file_data = context.interface_data.download_file(file_id)
|
||||
|
||||
# Datei zurückgeben
|
||||
# Return file
|
||||
headers = {
|
||||
"Content-Disposition": f'attachment; filename="{file_data["name"]}"'
|
||||
}
|
||||
|
|
@ -162,28 +162,28 @@ async def get_file(
|
|||
)
|
||||
|
||||
except FileNotFoundError as e:
|
||||
logger.warning(f"Datei nicht gefunden: {str(e)}")
|
||||
logger.warning(f"File not found: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=str(e)
|
||||
)
|
||||
except FilePermissionError as e:
|
||||
logger.warning(f"Keine Berechtigung für Datei: {str(e)}")
|
||||
logger.warning(f"No permission for file: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=str(e)
|
||||
)
|
||||
except FileError as e:
|
||||
logger.error(f"Fehler beim Abrufen der Datei: {str(e)}")
|
||||
logger.error(f"Error retrieving file: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=str(e)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Unerwarteter Fehler beim Abrufen der Datei: {str(e)}")
|
||||
logger.error(f"Unexpected error retrieving file: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Fehler beim Abrufen der Datei: {str(e)}"
|
||||
detail=f"Error retrieving file: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -193,42 +193,42 @@ async def delete_file(
|
|||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""
|
||||
Löscht eine Datei anhand ihrer ID aus der Datenbank.
|
||||
Entfernt sowohl die Metadaten als auch die Binärdaten.
|
||||
Deletes a file by its ID from the database.
|
||||
Removes both metadata and binary data.
|
||||
"""
|
||||
try:
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Datei über das LucyDOM-Interface löschen
|
||||
# Die Methode kümmert sich nun um das Löschen beider Tabellen (files und file_data)
|
||||
# Delete file via LucyDOM interface
|
||||
# The method now handles deleting from both tables (files and file_data)
|
||||
context.interface_data.delete_file(file_id)
|
||||
|
||||
# Erfolgreiche Löschung ohne Inhalt zurückgeben (204 No Content)
|
||||
# Return successful deletion without content (204 No Content)
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
except FileNotFoundError as e:
|
||||
logger.warning(f"Datei nicht gefunden: {str(e)}")
|
||||
logger.warning(f"File not found: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=str(e)
|
||||
)
|
||||
except FilePermissionError as e:
|
||||
logger.warning(f"Keine Berechtigung zum Löschen der Datei: {str(e)}")
|
||||
logger.warning(f"No permission to delete file: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=str(e)
|
||||
)
|
||||
except FileDeletionError as e:
|
||||
logger.error(f"Fehler beim Löschen der Datei: {str(e)}")
|
||||
logger.error(f"Error deleting file: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=str(e)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Unerwarteter Fehler beim Löschen der Datei: {str(e)}")
|
||||
logger.error(f"Unexpected error deleting file: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Fehler beim Löschen der Datei: {str(e)}"
|
||||
detail=f"Error deleting file: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/stats", response_model=Dict[str, Any])
|
||||
|
|
@ -236,19 +236,19 @@ async def get_file_stats(
|
|||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""
|
||||
Gibt Statistiken über die gespeicherten Dateien zurück.
|
||||
Returns statistics about the stored files.
|
||||
"""
|
||||
try:
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Alle Dateien abrufen - nur Metadaten
|
||||
# Get all files - metadata only
|
||||
all_files = context.interface_data.get_all_files()
|
||||
|
||||
# Statistiken berechnen
|
||||
# Calculate statistics
|
||||
total_files = len(all_files)
|
||||
total_size = sum(file.get("size", 0) for file in all_files)
|
||||
|
||||
# Nach Dateityp gruppieren
|
||||
# Group by file type
|
||||
file_types = {}
|
||||
for file in all_files:
|
||||
file_type = file.get("mime_type", "unknown").split("/")[0]
|
||||
|
|
@ -263,8 +263,8 @@ async def get_file_stats(
|
|||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Abrufen der Datei-Statistiken: {str(e)}")
|
||||
logger.error(f"Error retrieving file statistics: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Fehler beim Abrufen der Datei-Statistiken: {str(e)}"
|
||||
detail=f"Error retrieving file statistics: {str(e)}"
|
||||
)
|
||||
|
|
@ -11,7 +11,7 @@ from modules.auth import get_current_active_user, get_user_context
|
|||
from modules.lucydom_interface import get_lucydom_interface
|
||||
from modules.lucydom_model import Prompt
|
||||
|
||||
# Alle Attribute des Models ermitteln (außer interne/spezielle Attribute)
|
||||
# Get all attributes of the model (except internal/special attributes)
|
||||
def get_model_attributes(model_class):
|
||||
return [attr for attr in dir(model_class)
|
||||
if not callable(getattr(model_class, attr))
|
||||
|
|
@ -22,25 +22,25 @@ def get_model_attributes(model_class):
|
|||
and attr != 'label'
|
||||
and attr != 'field_labels']
|
||||
|
||||
# Modell-Attribute für Prompt
|
||||
# Model attributes for Prompt
|
||||
prompt_attributes = get_model_attributes(Prompt)
|
||||
|
||||
@dataclass
|
||||
class AppContext:
|
||||
"""Kontext-Objekt für alle benötigten Verbindungen und Benutzerinformationen"""
|
||||
"""Context object for all required connections and user information"""
|
||||
mandate_id: int
|
||||
user_id: int
|
||||
interface_data: Any # LucyDOM Interface
|
||||
|
||||
async def get_context(current_user: Dict[str, Any]) -> AppContext:
|
||||
"""
|
||||
Erstellt ein zentrales Kontext-Objekt mit allen benötigten Interfaces
|
||||
Creates a central context object with all required interfaces
|
||||
|
||||
Args:
|
||||
current_user: Aktueller Benutzer aus der Authentifizierung
|
||||
current_user: Current user from authentication
|
||||
|
||||
Returns:
|
||||
AppContext-Objekt mit allen benötigten Verbindungen
|
||||
AppContext object with all required connections
|
||||
"""
|
||||
mandate_id, user_id = await get_user_context(current_user)
|
||||
interface_data = get_lucydom_interface(mandate_id, user_id)
|
||||
|
|
@ -51,7 +51,7 @@ async def get_context(current_user: Dict[str, Any]) -> AppContext:
|
|||
interface_data=interface_data
|
||||
)
|
||||
|
||||
# Router für Prompt-Endpunkte erstellen
|
||||
# Create router for prompt endpoints
|
||||
router = APIRouter(
|
||||
prefix="/api/prompts",
|
||||
tags=["Prompts"],
|
||||
|
|
@ -62,10 +62,10 @@ router = APIRouter(
|
|||
async def get_prompts(
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""Alle Prompts abrufen"""
|
||||
"""Get all prompts"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Prompts generisch abrufen
|
||||
# Retrieve prompts generically
|
||||
return context.interface_data.get_all_prompts()
|
||||
|
||||
|
||||
|
|
@ -74,26 +74,26 @@ async def create_prompt(
|
|||
prompt: Dict[str, Any] = Body(...),
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""Einen neuen Prompt erstellen"""
|
||||
"""Create a new prompt"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Attribute aus dem Request dynamisch setzen
|
||||
# Set attributes from the request dynamically
|
||||
prompt_data = {}
|
||||
for attr in prompt_attributes:
|
||||
if attr in prompt:
|
||||
prompt_data[attr] = prompt[attr]
|
||||
|
||||
# Pflichtfelder mit Standardwerten
|
||||
# Required fields with default values
|
||||
content = prompt.get("content", "")
|
||||
name = prompt.get("name", "Neuer Prompt")
|
||||
name = prompt.get("name", "New Prompt")
|
||||
|
||||
# Prompt erstellen
|
||||
# Create prompt
|
||||
new_prompt = context.interface_data.create_prompt(
|
||||
content=content,
|
||||
name=name
|
||||
)
|
||||
|
||||
# Aktuelle Zeit für created_at setzen, wenn es im Modell existiert
|
||||
# Set current time for created_at if it exists in the model
|
||||
if "created_at" in prompt_attributes and hasattr(new_prompt, "created_at"):
|
||||
new_prompt["created_at"] = datetime.now().isoformat()
|
||||
|
||||
|
|
@ -105,15 +105,15 @@ async def get_prompt(
|
|||
prompt_id: int,
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""Einen bestimmten Prompt abrufen"""
|
||||
"""Get a specific prompt"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Prompt generisch abrufen
|
||||
# Get prompt generically
|
||||
prompt = context.interface_data.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"
|
||||
detail=f"Prompt with ID {prompt_id} not found"
|
||||
)
|
||||
|
||||
return prompt
|
||||
|
|
@ -125,28 +125,28 @@ async def update_prompt(
|
|||
prompt_data: Dict[str, Any] = Body(...),
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""Einen bestehenden Prompt aktualisieren"""
|
||||
"""Update an existing prompt"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Prüfe, ob der Prompt existiert
|
||||
# Check if the prompt exists
|
||||
existing_prompt = context.interface_data.get_prompt(prompt_id)
|
||||
if not existing_prompt:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Prompt mit ID {prompt_id} nicht gefunden"
|
||||
detail=f"Prompt with ID {prompt_id} not found"
|
||||
)
|
||||
|
||||
# Attribute aus dem Request dynamisch filtern
|
||||
# Filter attributes from the request dynamically
|
||||
update_data = {}
|
||||
for attr in prompt_attributes:
|
||||
if attr in prompt_data:
|
||||
update_data[attr] = prompt_data[attr]
|
||||
|
||||
# Standardfelder für Update
|
||||
# Standard fields for update
|
||||
content = prompt_data.get("content")
|
||||
name = prompt_data.get("name")
|
||||
|
||||
# Prompt aktualisieren
|
||||
# Update prompt
|
||||
updated_prompt = context.interface_data.update_prompt(
|
||||
prompt_id=prompt_id,
|
||||
content=content,
|
||||
|
|
@ -156,7 +156,7 @@ async def update_prompt(
|
|||
if not updated_prompt:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Fehler beim Aktualisieren des Prompts"
|
||||
detail="Error updating the prompt"
|
||||
)
|
||||
|
||||
return updated_prompt
|
||||
|
|
@ -167,22 +167,22 @@ async def delete_prompt(
|
|||
prompt_id: int,
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""Einen Prompt löschen"""
|
||||
"""Delete a prompt"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Prüfe, ob der Prompt existiert
|
||||
# Check if the prompt exists
|
||||
existing_prompt = context.interface_data.get_prompt(prompt_id)
|
||||
if not existing_prompt:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Prompt mit ID {prompt_id} nicht gefunden"
|
||||
detail=f"Prompt with ID {prompt_id} not found"
|
||||
)
|
||||
|
||||
success = context.interface_data.delete_prompt(prompt_id)
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Fehler beim Löschen des Prompts"
|
||||
detail="Error deleting the prompt"
|
||||
)
|
||||
|
||||
return {"message": f"Prompt mit ID {prompt_id} wurde erfolgreich gelöscht"}
|
||||
return {"message": f"Prompt with ID {prompt_id} successfully deleted"}
|
||||
|
|
@ -17,10 +17,10 @@ from modules.chat import get_chat_manager
|
|||
# Import models
|
||||
import modules.lucydom_model as lucydom_model
|
||||
|
||||
# Logger konfigurieren
|
||||
# Configure logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Router für Workflow-Endpunkte erstellen
|
||||
# Create router for workflow endpoints
|
||||
router = APIRouter(
|
||||
prefix="/api/workflows",
|
||||
tags=["Workflow"],
|
||||
|
|
@ -29,7 +29,7 @@ router = APIRouter(
|
|||
|
||||
@dataclass
|
||||
class AppContext:
|
||||
"""Kontext-Objekt für alle benötigten Verbindungen und Benutzerinformationen"""
|
||||
"""Context object for all required connections and user information"""
|
||||
mandate_id: int
|
||||
user_id: int
|
||||
interface_data: Any # LucyDOM Interface
|
||||
|
|
@ -37,13 +37,13 @@ class AppContext:
|
|||
|
||||
async def get_context(current_user: Dict[str, Any]) -> AppContext:
|
||||
"""
|
||||
Erstellt ein zentrales Kontext-Objekt mit allen benötigten Interfaces
|
||||
Creates a central context object with all required interfaces
|
||||
|
||||
Args:
|
||||
current_user: Aktueller Benutzer aus der Authentifizierung
|
||||
current_user: Current user from authentication
|
||||
|
||||
Returns:
|
||||
AppContext-Objekt mit allen benötigten Verbindungen
|
||||
AppContext object with all required connections
|
||||
"""
|
||||
mandate_id, user_id = await get_user_context(current_user)
|
||||
interface_data = get_lucydom_interface(mandate_id, user_id)
|
||||
|
|
@ -59,31 +59,31 @@ async def get_context(current_user: Dict[str, Any]) -> AppContext:
|
|||
|
||||
@router.get("", response_model=List[Dict[str, Any]])
|
||||
async def list_workflows(current_user: Dict[str, Any] = Depends(get_current_active_user)):
|
||||
"""Listet alle Workflows des Benutzers auf"""
|
||||
"""Lists all workflows of the user"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Workflows für den Benutzer abrufen
|
||||
# Retrieve workflows for the user
|
||||
workflows = context.interface_data.get_workflows_by_user(context.user_id)
|
||||
|
||||
return workflows
|
||||
|
||||
@router.post("/{workflow_id}/user-input", response_model=Dict[str, Any])
|
||||
async def submit_user_input(
|
||||
workflow_id: Optional[str] = Path(None, description="ID des Workflows (optional)"),
|
||||
workflow_id: Optional[str] = Path(None, description="ID of the workflow (optional)"),
|
||||
user_input: lucydom_model.UserInputRequest = Body(...),
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""
|
||||
Ermöglicht es dem Benutzer, Eingaben für einen laufenden Workflow zu senden
|
||||
oder einen neuen Workflow zu starten.
|
||||
Allows the user to send inputs for a running workflow
|
||||
or start a new workflow.
|
||||
"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Improved logging
|
||||
logger.info(f"Benutzereingabe für Workflow {workflow_id or 'neu'} empfangen")
|
||||
logger.info(f"User input for workflow {workflow_id or 'new'} received")
|
||||
|
||||
try:
|
||||
# Workflow mit dem Chat-Manager fortsetzen oder neu starten
|
||||
# Continue or start workflow with the chat manager
|
||||
user_input_dict = {
|
||||
"prompt": user_input.prompt,
|
||||
"list_file_id": user_input.list_file_id
|
||||
|
|
@ -93,51 +93,51 @@ async def submit_user_input(
|
|||
if not workflow:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Fehler bei der Verarbeitung der Benutzereingabe"
|
||||
detail="Error processing user input"
|
||||
)
|
||||
|
||||
return {
|
||||
"workflow_id": workflow.get("id"),
|
||||
"status": "processing",
|
||||
"message": "Benutzereingabe wurde empfangen und wird verarbeitet"
|
||||
"status": "running",
|
||||
"message": "User input received and being processed"
|
||||
}
|
||||
except HTTPException:
|
||||
# HTTP-Exceptions weiterleiten
|
||||
# Forward HTTP exceptions
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bei der Verarbeitung der Benutzereingabe: {str(e)}", exc_info=True)
|
||||
logger.error(f"Error processing user input: {str(e)}", exc_info=True)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Fehler bei der Verarbeitung der Benutzereingabe: {str(e)}"
|
||||
detail=f"Error processing user input: {str(e)}"
|
||||
)
|
||||
|
||||
@router.post("/{workflow_id}/stop", response_model=Dict[str, Any])
|
||||
async def stop_workflow(
|
||||
workflow_id: str = Path(..., description="ID des zu stoppenden Workflows"),
|
||||
workflow_id: str = Path(..., description="ID of the workflow to stop"),
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""Stoppt einen laufenden Workflow"""
|
||||
"""Stops a running workflow"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Workflow laden
|
||||
# Load workflow
|
||||
workflow = context.interface_data.get_workflow(workflow_id)
|
||||
if not workflow:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Workflow mit ID {workflow_id} nicht gefunden"
|
||||
detail=f"Workflow with ID {workflow_id} not found"
|
||||
)
|
||||
|
||||
# Status auf "stopped" setzen
|
||||
workflow["status"] = "stopped"
|
||||
# Set status to "stopped"
|
||||
workflow["status"] = "completed"
|
||||
workflow["last_activity"] = datetime.now().isoformat()
|
||||
|
||||
# Workflow aktualisieren
|
||||
# Update workflow
|
||||
context.interface_data.update_workflow(workflow_id, workflow)
|
||||
|
||||
return {
|
||||
"workflow_id": workflow_id,
|
||||
"status": "stopped",
|
||||
"message": "Workflow wurde gestoppt"
|
||||
"status": "completed",
|
||||
"message": "Workflow has been stopped"
|
||||
}
|
||||
|
||||
@router.delete("/{workflow_id}", response_model=Dict[str, Any])
|
||||
|
|
@ -145,20 +145,20 @@ async def delete_workflow(
|
|||
workflow_id: str,
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""Löscht einen Workflow"""
|
||||
"""Deletes a workflow"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Workflow löschen
|
||||
# Delete workflow
|
||||
success = context.interface_data.delete_workflow(workflow_id)
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Workflow mit ID {workflow_id} nicht gefunden"
|
||||
detail=f"Workflow with ID {workflow_id} not found"
|
||||
)
|
||||
|
||||
return {
|
||||
"workflow_id": workflow_id,
|
||||
"message": "Workflow wurde gelöscht"
|
||||
"message": "Workflow has been deleted"
|
||||
}
|
||||
|
||||
@router.get("/{workflow_id}/data-statistics", response_model=Dict[str, Any])
|
||||
|
|
@ -167,19 +167,19 @@ async def get_workflow_data_statistics(
|
|||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""
|
||||
Gibt Statistiken über die übertragenen Datenmengen für einen Workflow zurück.
|
||||
Returns statistics about the transferred data volumes for a workflow.
|
||||
"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Workflow laden
|
||||
# Load workflow
|
||||
workflow = context.interface_data.get_workflow(workflow_id)
|
||||
if not workflow:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Workflow mit ID {workflow_id} nicht gefunden"
|
||||
detail=f"Workflow with ID {workflow_id} not found"
|
||||
)
|
||||
|
||||
# Datenstatistiken zurückgeben
|
||||
# Return data statistics
|
||||
data_stats = workflow.get("data_stats", {})
|
||||
if not data_stats:
|
||||
data_stats = {
|
||||
|
|
@ -199,18 +199,18 @@ async def get_workflow_status(
|
|||
workflow_id: str,
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""Status eines Workflows abrufen"""
|
||||
"""Get the status of a workflow"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Workflow laden
|
||||
# Load workflow
|
||||
workflow = context.interface_data.get_workflow(workflow_id)
|
||||
if not workflow:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Workflow mit ID {workflow_id} nicht gefunden"
|
||||
detail=f"Workflow with ID {workflow_id} not found"
|
||||
)
|
||||
|
||||
# Status aus dem geladenen Workflow erstellen
|
||||
# Create status from the loaded workflow
|
||||
status_info = {
|
||||
"id": workflow.get("id"),
|
||||
"name": workflow.get("name"),
|
||||
|
|
@ -227,20 +227,20 @@ async def get_workflow_logs(
|
|||
workflow_id: str,
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""Protokolle eines Workflows abrufen"""
|
||||
"""Get logs of a workflow"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Logs abrufen
|
||||
# Get logs
|
||||
logs = context.interface_data.get_workflow_logs(workflow_id)
|
||||
if not logs:
|
||||
# Prüfen, ob der Workflow existiert
|
||||
# Check if the workflow exists
|
||||
workflow = context.interface_data.get_workflow(workflow_id)
|
||||
if not workflow:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Workflow mit ID {workflow_id} nicht gefunden"
|
||||
detail=f"Workflow with ID {workflow_id} not found"
|
||||
)
|
||||
# Leere Log-Liste zurückgeben
|
||||
# Return empty log list
|
||||
logs = []
|
||||
|
||||
return logs
|
||||
|
|
@ -250,82 +250,82 @@ async def get_workflow_messages(
|
|||
workflow_id: str,
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""Nachrichten eines Workflows abrufen"""
|
||||
"""Get messages of a workflow"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
# Nachrichten abrufen
|
||||
# Get messages
|
||||
messages = context.interface_data.get_workflow_messages(workflow_id)
|
||||
if messages is None:
|
||||
# Prüfen, ob der Workflow existiert
|
||||
# Check if the workflow exists
|
||||
workflow = context.interface_data.get_workflow(workflow_id)
|
||||
if not workflow:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Workflow mit ID {workflow_id} nicht gefunden"
|
||||
detail=f"Workflow with ID {workflow_id} not found"
|
||||
)
|
||||
# Leere Nachrichtenliste zurückgeben
|
||||
# Return empty message list
|
||||
messages = []
|
||||
|
||||
return messages
|
||||
|
||||
@router.delete("/{workflow_id}/messages/{message_id}", response_model=Dict[str, Any])
|
||||
async def delete_workflow_message(
|
||||
workflow_id: str = Path(..., description="ID des Workflows"),
|
||||
message_id: str = Path(..., description="ID der zu löschenden Nachricht"),
|
||||
workflow_id: str = Path(..., description="ID of the workflow"),
|
||||
message_id: str = Path(..., description="ID of the message to delete"),
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""
|
||||
Löscht eine einzelne Nachricht aus einem Workflow.
|
||||
Deletes a single message from a workflow.
|
||||
|
||||
Diese Funktion entfernt die Nachricht aus dem Workflow und auch aus der Datenbank.
|
||||
This function removes the message from the workflow and also from the database.
|
||||
"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
try:
|
||||
# Prüfen, ob der Workflow existiert
|
||||
# Check if the workflow exists
|
||||
workflow = context.interface_data.get_workflow(workflow_id)
|
||||
if not workflow:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Workflow mit ID {workflow_id} nicht gefunden"
|
||||
detail=f"Workflow with ID {workflow_id} not found"
|
||||
)
|
||||
|
||||
# Nachricht löschen
|
||||
# Delete message
|
||||
success = context.interface_data.delete_workflow_message(workflow_id, message_id)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Nachricht mit ID {message_id} im Workflow {workflow_id} nicht gefunden"
|
||||
detail=f"Message with ID {message_id} in workflow {workflow_id} not found"
|
||||
)
|
||||
|
||||
return {
|
||||
"workflow_id": workflow_id,
|
||||
"message_id": message_id,
|
||||
"success": True,
|
||||
"message": "Nachricht erfolgreich gelöscht"
|
||||
"message": "Message successfully deleted"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
# Bekannte HTTP-Exceptions weiterleiten
|
||||
# Forward known HTTP exceptions
|
||||
raise
|
||||
except Exception as e:
|
||||
# Sonstige Fehler abfangen
|
||||
# Catch other errors
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Fehler beim Löschen der Nachricht: {str(e)}"
|
||||
detail=f"Error deleting the message: {str(e)}"
|
||||
)
|
||||
|
||||
@router.delete("/{workflow_id}/messages/{message_id}/files/{file_id}", response_model=Dict[str, Any])
|
||||
async def delete_file_from_message(
|
||||
workflow_id: str = Path(..., description="ID des Workflows"),
|
||||
message_id: str = Path(..., description="ID der Nachricht"),
|
||||
file_id: str = Path(..., description="ID der zu löschenden Datei"),
|
||||
workflow_id: str = Path(..., description="ID of the workflow"),
|
||||
message_id: str = Path(..., description="ID of the message"),
|
||||
file_id: str = Path(..., description="ID of the file to delete"),
|
||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||
):
|
||||
"""
|
||||
Löscht eine einzelne Dateireferenz aus einer Nachricht im Workflow.
|
||||
Die Datei selbst wird nicht aus der Datenbank gelöscht, nur die Referenz in der Nachricht.
|
||||
Deletes a single file reference from a message in the workflow.
|
||||
The file itself is not deleted from the database, only the reference in the message.
|
||||
"""
|
||||
context = await get_context(current_user)
|
||||
|
||||
|
|
@ -333,13 +333,13 @@ async def delete_file_from_message(
|
|||
logger.debug(f"DELETE request: Remove file {file_id} from message {message_id} in workflow {workflow_id}")
|
||||
|
||||
try:
|
||||
# Datei aus der Nachricht entfernen
|
||||
# Remove file from the message
|
||||
success = context.interface_data.delete_file_from_message(workflow_id, message_id, file_id)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Datei mit ID {file_id} in der Nachricht {message_id} nicht gefunden"
|
||||
detail=f"File with ID {file_id} in message {message_id} not found"
|
||||
)
|
||||
|
||||
return {
|
||||
|
|
@ -347,19 +347,19 @@ async def delete_file_from_message(
|
|||
"message_id": message_id,
|
||||
"file_id": file_id,
|
||||
"success": True,
|
||||
"message": "Datei erfolgreich aus der Nachricht gelöscht"
|
||||
"message": "File successfully deleted from message"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
# HTTP-Exceptions weiterleiten
|
||||
# Forward HTTP exceptions
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Löschen der Datei: {str(e)}")
|
||||
logger.error(f"Error deleting file: {str(e)}")
|
||||
import traceback
|
||||
traceback_str = traceback.format_exc()
|
||||
logger.error(f"Traceback: {traceback_str}")
|
||||
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Fehler beim Löschen der Datei aus der Nachricht: {str(e)}"
|
||||
detail=f"Error deleting file from message: {str(e)}"
|
||||
)
|
||||
Loading…
Reference in a new issue