373 lines
No EOL
14 KiB
Python
373 lines
No EOL
14 KiB
Python
"""
|
|
Erweitertes Test-Skript für den ChatManager-Workflow mit simulierten Datei-Uploads.
|
|
Bietet zusätzliche Konfigurationsmöglichkeiten und detailliertere Tests.
|
|
"""
|
|
|
|
import asyncio
|
|
import logging
|
|
import os
|
|
import sys
|
|
import argparse
|
|
import json
|
|
from typing import Dict, Any, List, Tuple, Optional
|
|
from datetime import datetime
|
|
|
|
# Logging konfigurieren
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
handlers=[logging.StreamHandler()]
|
|
)
|
|
logger = logging.getLogger("test_workflow")
|
|
|
|
# Pfad zum Projektverzeichnis hinzufügen
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
# Module importieren
|
|
from modules.lucydom_interface import get_lucydom_interface
|
|
from modules.chat import get_chat_manager
|
|
|
|
class TestConfig:
|
|
"""Konfigurationsklasse für Testparameter"""
|
|
def __init__(self):
|
|
self.mandate_id = 1
|
|
self.user_id = 1
|
|
self.cleanup = True
|
|
self.save_results = True
|
|
self.results_dir = "test_results"
|
|
self.test_message = "Analysiere bitte die hochgeladenen Dateien und erkläre mir deren Inhalt."
|
|
self.text_file_content = """
|
|
Dies ist eine Test-Textdatei für den ChatManager-Workflow.
|
|
Sie enthält einige Informationen zum Testen der Dokumentverarbeitung.
|
|
|
|
Der ChatManager sollte in der Lage sein, diese Datei zu verarbeiten
|
|
und daraus relevante Informationen zu extrahieren.
|
|
|
|
Diese Datei dient als Beispiel für Text-basierte Dokumente, die in einem
|
|
Chat-Workflow verwendet werden können.
|
|
"""
|
|
|
|
def parse_args() -> TestConfig:
|
|
"""Parst Kommandozeilenargumente"""
|
|
parser = argparse.ArgumentParser(description="Test für ChatManager-Workflow")
|
|
parser.add_argument("--mandate-id", type=int, default=1, help="ID des Mandanten")
|
|
parser.add_argument("--user-id", type=int, default=1, help="ID des Benutzers")
|
|
parser.add_argument("--no-cleanup", action="store_true", help="Testdateien nicht löschen")
|
|
parser.add_argument("--no-save", action="store_true", help="Ergebnisse nicht speichern")
|
|
parser.add_argument("--results-dir", type=str, default="test_results", help="Verzeichnis für Ergebnisse")
|
|
parser.add_argument("--message", type=str, help="Benutzernachricht für den Test")
|
|
|
|
args = parser.parse_args()
|
|
|
|
config = TestConfig()
|
|
config.mandate_id = args.mandate_id
|
|
config.user_id = args.user_id
|
|
config.cleanup = not args.no_cleanup
|
|
config.save_results = not args.no_save
|
|
config.results_dir = args.results_dir
|
|
if args.message:
|
|
config.test_message = args.message
|
|
|
|
return config
|
|
|
|
async def create_test_files(config: TestConfig) -> Tuple[int, int]:
|
|
"""
|
|
Erstellt eine Textdatei und ein Bild für Tests und lädt sie in die Datenbank hoch.
|
|
|
|
Args:
|
|
config: Testkonfiguration
|
|
|
|
Returns:
|
|
Tuple mit (text_file_id, image_file_id)
|
|
"""
|
|
logger.info("Erstelle Test-Dateien...")
|
|
|
|
lucy_interface = get_lucydom_interface(config.mandate_id, config.user_id)
|
|
|
|
# Textdatei erstellen
|
|
text_content = config.text_file_content
|
|
text_file_bytes = text_content.encode('utf-8')
|
|
text_file = lucy_interface.save_uploaded_file(text_file_bytes, "test_document.txt")
|
|
text_file_id = text_file["id"]
|
|
logger.info(f"Textdatei erstellt mit ID: {text_file_id}")
|
|
|
|
# Bilddatei erstellen (einfaches 1x1 PNG)
|
|
# Base64-kodiertes 1x1 PNG
|
|
png_data = bytes.fromhex(
|
|
"89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c4"
|
|
"89000000017352474200aece1ce90000000467414d410000b18f0bfc61050000"
|
|
"000970485973000016250000162501495224f00000001974455874536f667477"
|
|
"617265007777772e696e6b73636170652e6f72679bee3c1a0000000c49444154"
|
|
"08d763f8ffff3f0005fe02fec1cd59830000000049454e44ae426082"
|
|
)
|
|
image_file = lucy_interface.save_uploaded_file(png_data, "test_image.png")
|
|
image_file_id = image_file["id"]
|
|
logger.info(f"Bilddatei erstellt mit ID: {image_file_id}")
|
|
|
|
return text_file_id, image_file_id
|
|
|
|
async def verify_uploaded_files(mandate_id: int, user_id: int, file_ids: List[int]) -> bool:
|
|
"""
|
|
Überprüft, ob die hochgeladenen Dateien korrekt in der Datenbank gespeichert wurden
|
|
|
|
Args:
|
|
mandate_id: ID des Mandanten
|
|
user_id: ID des Benutzers
|
|
file_ids: Liste der Datei-IDs
|
|
|
|
Returns:
|
|
True, wenn alle Dateien verfügbar sind
|
|
"""
|
|
logger.info("Überprüfe hochgeladene Dateien...")
|
|
|
|
lucy_interface = get_lucydom_interface(mandate_id, user_id)
|
|
all_files_available = True
|
|
|
|
for file_id in file_ids:
|
|
file = lucy_interface.get_file(file_id)
|
|
if file:
|
|
file_data = lucy_interface.get_file_data(file_id)
|
|
if file_data:
|
|
logger.info(f"Datei {file_id} ({file.get('name', 'Unbekannt')}, {file.get('mime_type', 'Unbekannt')}) ist verfügbar")
|
|
logger.info(f" Größe: {len(file_data)} Bytes")
|
|
else:
|
|
logger.error(f"Datei {file_id} hat keine Binärdaten")
|
|
all_files_available = False
|
|
else:
|
|
logger.error(f"Datei mit ID {file_id} nicht in der Datenbank gefunden")
|
|
all_files_available = False
|
|
|
|
return all_files_available
|
|
|
|
async def run_chat_workflow(config: TestConfig, file_ids: List[int]) -> Dict[str, Any]:
|
|
"""
|
|
Führt einen Chat-Workflow mit gegebenen Datei-IDs aus.
|
|
|
|
Args:
|
|
config: Testkonfiguration
|
|
file_ids: Liste der Datei-IDs
|
|
|
|
Returns:
|
|
Das Workflow-Ergebnis
|
|
"""
|
|
logger.info(f"Starte Chat-Workflow mit Dateien: {file_ids}")
|
|
|
|
# ChatManager initialisieren
|
|
chat_manager = get_chat_manager(config.mandate_id, config.user_id)
|
|
|
|
# Benutzeranfrage erstellen
|
|
user_input = {
|
|
"message": config.test_message,
|
|
"additional_fileids": file_ids
|
|
}
|
|
|
|
# Start-Zeit erfassen
|
|
start_time = datetime.now()
|
|
|
|
# Chat-Workflow ausführen
|
|
workflow_result = await chat_manager.chat_run(user_input)
|
|
|
|
# Ende-Zeit und Dauer berechnen
|
|
end_time = datetime.now()
|
|
duration = (end_time - start_time).total_seconds()
|
|
|
|
logger.info(f"Workflow abgeschlossen mit ID: {workflow_result['id']}")
|
|
logger.info(f"Dauer: {duration:.2f} Sekunden")
|
|
|
|
return workflow_result
|
|
|
|
def analyze_workflow_result(workflow: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
Analysiert das Workflow-Ergebnis und gibt Statistiken zurück.
|
|
|
|
Args:
|
|
workflow: Das Workflow-Ergebnis
|
|
|
|
Returns:
|
|
Dictionary mit Analyseergebnissen
|
|
"""
|
|
logger.info("Analysiere Workflow-Ergebnis:")
|
|
|
|
# Basis-Informationen
|
|
analysis = {
|
|
"workflow_id": workflow.get("id"),
|
|
"status": workflow.get("status"),
|
|
"message_count": len(workflow.get("messages", [])),
|
|
"log_count": len(workflow.get("logs", [])),
|
|
"document_count": 0,
|
|
"roles": {},
|
|
"document_types": {},
|
|
"response_sizes": []
|
|
}
|
|
|
|
# Nachrichten analysieren
|
|
for message in workflow.get("messages", []):
|
|
# Rollen zählen
|
|
role = message.get("role", "unknown")
|
|
if role not in analysis["roles"]:
|
|
analysis["roles"][role] = 0
|
|
analysis["roles"][role] += 1
|
|
|
|
# Content-Größe bei Antworten
|
|
if role == "assistant":
|
|
content = message.get("content", "")
|
|
analysis["response_sizes"].append(len(content))
|
|
|
|
# Dokumente zählen und analysieren
|
|
documents = message.get("documents", [])
|
|
analysis["document_count"] += len(documents)
|
|
|
|
for doc in documents:
|
|
contents = doc.get("contents", [])
|
|
for content in contents:
|
|
content_type = content.get("content_type", "unknown")
|
|
if content_type not in analysis["document_types"]:
|
|
analysis["document_types"][content_type] = 0
|
|
analysis["document_types"][content_type] += 1
|
|
|
|
# Ausgabe für Log
|
|
logger.info(f"Workflow-ID: {analysis['workflow_id']}")
|
|
logger.info(f"Status: {analysis['status']}")
|
|
logger.info(f"Anzahl Nachrichten: {analysis['message_count']}")
|
|
logger.info(f"Anzahl Dokumente: {analysis['document_count']}")
|
|
logger.info(f"Rollen-Verteilung: {analysis['roles']}")
|
|
logger.info(f"Dokumenttypen: {analysis['document_types']}")
|
|
|
|
if analysis["response_sizes"]:
|
|
avg_size = sum(analysis["response_sizes"]) / len(analysis["response_sizes"])
|
|
logger.info(f"Durchschnittliche Antwortgröße: {avg_size:.2f} Zeichen")
|
|
|
|
# Detaillierte Nachrichteninformationen
|
|
for i, message in enumerate(workflow.get("messages", [])[:5]): # Begrenzung auf 5 Nachrichten
|
|
logger.info(f"Nachricht {i+1}:")
|
|
logger.info(f" Rolle: {message.get('role', 'unbekannt')}")
|
|
|
|
# Nur die ersten 100 Zeichen des Inhalts anzeigen
|
|
content = message.get("content", "")
|
|
content_preview = content[:100] + "..." if len(content) > 100 else content
|
|
logger.info(f" Inhalt: {content_preview}")
|
|
|
|
# Dokumente in der Nachricht anzeigen
|
|
documents = message.get("documents", [])
|
|
if documents:
|
|
logger.info(f" Dokumente: {len(documents)}")
|
|
for j, doc in enumerate(documents):
|
|
file_id = doc.get("file_id", "keine file_id")
|
|
logger.info(f" Dokument {j+1}: File-ID={file_id}")
|
|
|
|
return analysis
|
|
|
|
def save_test_results(config: TestConfig, workflow: Dict[str, Any], analysis: Dict[str, Any]) -> None:
|
|
"""
|
|
Speichert die Testergebnisse in einer Datei.
|
|
|
|
Args:
|
|
config: Testkonfiguration
|
|
workflow: Das vollständige Workflow-Ergebnis
|
|
analysis: Die Analyseergebnisse
|
|
"""
|
|
if not config.save_results:
|
|
return
|
|
|
|
# Ergebnisverzeichnis erstellen, falls es nicht existiert
|
|
os.makedirs(config.results_dir, exist_ok=True)
|
|
|
|
# Zeitstempel für eindeutige Dateinamen
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
|
|
# Speichere die Analyse
|
|
analysis_file = os.path.join(config.results_dir, f"analysis_{timestamp}.json")
|
|
with open(analysis_file, "w", encoding="utf-8") as f:
|
|
json.dump(analysis, f, indent=2, ensure_ascii=False)
|
|
logger.info(f"Analyse gespeichert in: {analysis_file}")
|
|
|
|
# Speichere den vollständigen Workflow (ohne große Binärdaten)
|
|
workflow_copy = workflow.copy()
|
|
|
|
# Entferne Binärdaten aus dem Export, um die Dateigröße zu reduzieren
|
|
for message in workflow_copy.get("messages", []):
|
|
if "documents" in message:
|
|
for doc in message.get("documents", []):
|
|
if "contents" in doc:
|
|
for content in doc.get("contents", []):
|
|
if "data" in content and isinstance(content["data"], bytes) and len(content["data"]) > 1000:
|
|
content["data"] = f"[{len(content['data'])} Bytes]"
|
|
|
|
workflow_file = os.path.join(config.results_dir, f"workflow_{timestamp}.json")
|
|
with open(workflow_file, "w", encoding="utf-8") as f:
|
|
# Konvertiere Bytes zu Strings für JSON-Serialisierung
|
|
json.dump(workflow_copy, f, indent=2, ensure_ascii=False, default=lambda o:
|
|
o.decode("utf-8") if isinstance(o, bytes) else str(o))
|
|
logger.info(f"Workflow gespeichert in: {workflow_file}")
|
|
|
|
async def cleanup_test_files(config: TestConfig, file_ids: List[int]) -> None:
|
|
"""
|
|
Bereinigt die erstellten Testdateien.
|
|
|
|
Args:
|
|
config: Testkonfiguration
|
|
file_ids: Liste der zu löschenden Datei-IDs
|
|
"""
|
|
if not config.cleanup:
|
|
logger.info("Bereinigung übersprungen (--no-cleanup)")
|
|
return
|
|
|
|
logger.info("Beginne Bereinigung der Testdateien...")
|
|
|
|
lucy_interface = get_lucydom_interface(config.mandate_id, config.user_id)
|
|
|
|
for file_id in file_ids:
|
|
try:
|
|
success = lucy_interface.delete_file(file_id)
|
|
if success:
|
|
logger.info(f"Datei mit ID {file_id} erfolgreich gelöscht")
|
|
else:
|
|
logger.warning(f"Fehler beim Löschen der Datei mit ID {file_id}")
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Löschen der Datei mit ID {file_id}: {str(e)}")
|
|
|
|
logger.info("Bereinigung abgeschlossen")
|
|
|
|
async def main():
|
|
"""
|
|
Hauptfunktion, die den gesamten Testprozess steuert.
|
|
"""
|
|
# Konfiguration laden
|
|
config = parse_args()
|
|
|
|
try:
|
|
logger.info("=== Test-Workflow für ChatManager gestartet ===")
|
|
logger.info(f"Mandate-ID: {config.mandate_id}, User-ID: {config.user_id}")
|
|
|
|
# Schritt 1: Testdateien erstellen
|
|
text_file_id, image_file_id = await create_test_files(config)
|
|
file_ids = [text_file_id, image_file_id]
|
|
|
|
# Schritt 2: Hochgeladene Dateien überprüfen
|
|
files_ok = await verify_uploaded_files(config.mandate_id, config.user_id, file_ids)
|
|
if not files_ok:
|
|
logger.error("Fehler bei den hochgeladenen Dateien, Test wird abgebrochen")
|
|
return
|
|
|
|
# Schritt 3: Chat-Workflow ausführen
|
|
workflow_result = await run_chat_workflow(config, file_ids)
|
|
|
|
# Schritt 4: Ergebnis analysieren
|
|
analysis = analyze_workflow_result(workflow_result)
|
|
|
|
# Schritt 5: Ergebnisse speichern
|
|
save_test_results(config, workflow_result, analysis)
|
|
|
|
# Schritt 6: Bereinigen
|
|
await cleanup_test_files(config, file_ids)
|
|
|
|
logger.info("=== Test-Workflow erfolgreich abgeschlossen ===")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler im Test-Workflow: {str(e)}", exc_info=True)
|
|
logger.info("=== Test-Workflow mit Fehler beendet ===")
|
|
|
|
if __name__ == "__main__":
|
|
# Event-Loop für asyncio erstellen und Hauptfunktion ausführen
|
|
loop = asyncio.get_event_loop()
|
|
loop.run_until_complete(main()) |