from fastapi import FastAPI, File, UploadFile, HTTPException, Depends, Body, Query from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from fastapi.staticfiles import StaticFiles from typing import List, Dict, Any, Optional import uuid import os import json import asyncio from datetime import datetime import logging from models import ( Workspace, Agent, DataObject, Prompt, WorkflowRequest, WorkflowResponse, LogEntry, Result ) from database import get_db, Database from agent_service import AgentService, get_agent_service # Konfiguration des Loggers logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[logging.StreamHandler()] ) logger = logging.getLogger(__name__) app = FastAPI(title="Data Platform API", description="Backend-API für die Multi-Agent Datenplattform") # CORS-Konfiguration für Frontend-Anfragen app.add_middleware( CORSMiddleware, allow_origins=["*"], # In Produktion einschränken allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Statische Dateien (für Frontend) app.mount("/static", StaticFiles(directory="static"), name="static") # Verzeichnis für hochgeladene Dateien erstellen UPLOAD_DIR = os.path.join(os.getcwd(), "uploads") os.makedirs(UPLOAD_DIR, exist_ok=True) @app.get("/", tags=["General"]) async def root(): """API-Statusendpunkt""" return {"status": "online", "message": "Data Platform API ist aktiv"} # Workspace-Endpunkte @app.get("/workspaces", tags=["Workspaces"], response_model=List[Workspace]) async def get_workspaces(db: Database = Depends(get_db)): """Alle verfügbaren Workspaces abrufen""" return db.get_all_workspaces() @app.get("/workspaces/{workspace_id}", tags=["Workspaces"], response_model=Workspace) async def get_workspace(workspace_id: str, db: Database = Depends(get_db)): """Einen bestimmten Workspace mit allen Details abrufen""" workspace = db.get_workspace(workspace_id) if not workspace: raise HTTPException(status_code=404, detail=f"Workspace mit ID {workspace_id} nicht gefunden") return workspace @app.post("/workspaces", tags=["Workspaces"], response_model=Workspace) async def create_workspace(workspace: Dict[str, Any] = Body(...), db: Database = Depends(get_db)): """Einen neuen Workspace erstellen""" workspace_id = str(uuid.uuid4()) workspace_data = { "id": workspace_id, "name": workspace.get("name", "Neuer Workspace"), "created_at": datetime.now().isoformat(), "prompts": [], "agents": [], "dataObjectReferences": [] } new_workspace = db.create_workspace(workspace_data) return new_workspace # Agent-Endpunkte @app.get("/agents", tags=["Agents"], response_model=List[Agent]) async def get_agents(workspace_id: Optional[str] = Query(None), db: Database = Depends(get_db)): """Alle Agenten oder Agenten eines bestimmten Workspaces abrufen""" if workspace_id: return db.get_agents_by_workspace(workspace_id) return db.get_all_agents() @app.post("/agents", tags=["Agents"], response_model=Agent) async def create_agent( agent: Dict[str, Any] = Body(...), db: Database = Depends(get_db) ): """Einen neuen Agenten erstellen""" agent_id = str(uuid.uuid4()) workspace_id = agent.get("workspace_id") if not workspace_id: raise HTTPException(status_code=400, detail="workspace_id ist erforderlich") # Workspace existiert? workspace = db.get_workspace(workspace_id) if not workspace: raise HTTPException(status_code=404, detail=f"Workspace mit ID {workspace_id} nicht gefunden") agent_data = { "id": agent_id, "name": agent.get("name", "Neuer Agent"), "type": agent.get("type", "generic"), "capabilities": agent.get("capabilities", []), "description": agent.get("description", "") } new_agent = db.create_agent(agent_data, workspace_id) return new_agent # Datei-Endpunkte @app.get("/files", tags=["Files"], response_model=List[DataObject]) async def get_files(db: Database = Depends(get_db)): """Alle verfügbaren Dateien abrufen""" return db.get_all_files() @app.post("/files/upload", tags=["Files"]) async def upload_file( file: UploadFile = File(...), db: Database = Depends(get_db) ): """Eine Datei hochladen""" try: file_id = str(uuid.uuid4()) file_ext = os.path.splitext(file.filename)[1] file_path = os.path.join(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 file_data = { "id": file_id, "name": file.filename, "type": file_type, "path": file_path, "content_type": file.content_type, "size": os.path.getsize(file_path), "upload_date": datetime.now().isoformat() } new_file = db.create_file(file_data) 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["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)}") # Prompt-Endpunkte @app.get("/prompts", tags=["Prompts"], response_model=List[Prompt]) async def get_prompts(workspace_id: Optional[str] = Query(None), db: Database = Depends(get_db)): """Alle Prompts oder Prompts eines bestimmten Workspaces abrufen""" if workspace_id: return db.get_prompts_by_workspace(workspace_id) return db.get_all_prompts() @app.post("/prompts", tags=["Prompts"], response_model=Prompt) async def create_prompt( prompt: Dict[str, Any] = Body(...), db: Database = Depends(get_db) ): """Einen neuen Prompt erstellen""" prompt_id = str(uuid.uuid4()) workspace_id = prompt.get("workspace_id") if not workspace_id: raise HTTPException(status_code=400, detail="workspace_id ist erforderlich") # Workspace existiert? workspace = db.get_workspace(workspace_id) if not workspace: raise HTTPException(status_code=404, detail=f"Workspace mit ID {workspace_id} nicht gefunden") prompt_data = { "id": prompt_id, "content": prompt.get("content", ""), "created_at": datetime.now().isoformat() } new_prompt = db.create_prompt(prompt_data, workspace_id) return new_prompt # Workflow-Endpunkte @app.post("/workflow/run", tags=["Workflow"], response_model=WorkflowResponse) async def run_workflow( workflow_request: WorkflowRequest, db: Database = Depends(get_db), agent_service: AgentService = Depends(get_agent_service) ): """Führt einen Workflow mit den ausgewählten Agenten und Dateien aus""" workspace = db.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 = db.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 = db.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 WorkflowResponse( workflow_id=workflow_id, status="running", message="Workflow wurde gestartet" ) @app.get("/workflow/{workflow_id}/status", tags=["Workflow"]) async def get_workflow_status( workflow_id: str, agent_service: AgentService = Depends(get_agent_service) ): """Status eines laufenden Workflows abrufen""" 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("/workflow/{workflow_id}/logs", tags=["Workflow"], response_model=List[LogEntry]) async def get_workflow_logs( workflow_id: str, agent_service: AgentService = Depends(get_agent_service) ): """Protokolle eines Workflows abrufen""" 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("/workflow/{workflow_id}/results", tags=["Workflow"], response_model=List[Result]) async def get_workflow_results( workflow_id: str, agent_service: AgentService = Depends(get_agent_service) ): """Ergebnisse eines Workflows abrufen""" 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 if __name__ == "__main__": import uvicorn uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)