""" Erweiterter Coder-Agent für die Entwicklung und Ausführung von Python-Code. Integriert direkten Code-Executor zur Vereinfachung des Ablaufs. """ import logging import json import os import asyncio import re import uuid import subprocess import tempfile import traceback import sys import importlib.util import inspect from datetime import datetime from typing import List, Dict, Any, Optional, Tuple, Union from modules.agentservice_base import BaseAgent from modules.lucydom_interface import get_lucydom_interface from modules.agentservice_utils import FileUtils, WorkflowUtils, MessageUtils, LoggingUtils from connectors.connector_aichat_openai import ChatService from modules import agentservice_code_helpers logger = logging.getLogger(__name__) class CodeExecutor: """ Führt generierten Code in einer isolierten virtuellen Umgebung aus, während Zugriff auf spezifische App-Module gewährt wird und automatisch erforderliche Pakete installiert werden. """ def __init__(self, app_modules: List[str] = None, venv_path: Optional[str] = None, timeout: int = 30, max_memory_mb: int = 512, allowed_packages: List[str] = None, blocked_packages: List[str] = None): """ Initialisiert den CodeExecutor. Args: app_modules: Liste von Modulnamen, die dem generierten Code zur Verfügung stehen sollen venv_path: Pfad zur virtuellen Umgebung. Falls None, wird eine temporäre erstellt timeout: Maximale Ausführungszeit in Sekunden max_memory_mb: Maximaler Arbeitsspeicher in MB allowed_packages: Liste erlaubter Pakete (wenn None, werden alle erlaubt, außer blockierte) blocked_packages: Liste blockierter Pakete (z.B. gefährliche oder ressourcenintensive) """ self.app_modules = app_modules or [] self.venv_path = venv_path self.timeout = timeout self.max_memory_mb = max_memory_mb self.temp_dir = None self.allowed_packages = allowed_packages self.blocked_packages = blocked_packages or ["cryptography", "flask", "django", "tornado", "requests"] def _create_venv(self) -> str: """Erstellt eine virtuelle Umgebung und gibt den Pfad zurück.""" if self.venv_path and os.path.exists(self.venv_path): return self.venv_path # Temporäres Verzeichnis für die virtuelle Umgebung erstellen self.temp_dir = tempfile.mkdtemp(prefix="ai_code_exec_") venv_path = os.path.join(self.temp_dir, "venv") try: # Virtuelle Umgebung erstellen logger.info(f"Erstelle virtuelle Umgebung in {venv_path}") subprocess.run([sys.executable, "-m", "venv", venv_path], check=True, capture_output=True) return venv_path except subprocess.CalledProcessError as e: logger.error(f"Fehler beim Erstellen der virtuellen Umgebung: {e}") raise RuntimeError(f"Konnte venv nicht erstellen: {e}") def _get_pip_executable(self, venv_path: str) -> str: """Ermittelt den Pfad zum pip-Executable in der virtuellen Umgebung.""" if os.name == 'nt': # Windows return os.path.join(venv_path, "Scripts", "pip.exe") else: # Unix/Linux return os.path.join(venv_path, "bin", "pip") def _get_python_executable(self, venv_path: str) -> str: """Ermittelt den Pfad zum Python-Executable in der virtuellen Umgebung.""" if os.name == 'nt': # Windows return os.path.join(venv_path, "Scripts", "python.exe") else: # Unix/Linux return os.path.join(venv_path, "bin", "python") def _install_packages(self, packages: List[str], venv_path: str) -> Tuple[bool, str]: """ Installiert Pakete in der virtuellen Umgebung. Args: packages: Liste der zu installierenden Pakete venv_path: Pfad zur virtuellen Umgebung Returns: Tuple aus (Erfolg, Fehlermeldung) """ if not packages: return True, "" # Überprüfen, ob Pakete erlaubt sind blocked = [] for package in packages: # Paketname ohne Version extrahieren pkg_name = re.split('[=<>]', package)[0].strip() if self.blocked_packages and pkg_name.lower() in [p.lower() for p in self.blocked_packages]: blocked.append(pkg_name) if self.allowed_packages and pkg_name.lower() not in [p.lower() for p in self.allowed_packages]: blocked.append(pkg_name) if blocked: return False, f"Die folgenden Pakete sind nicht erlaubt: {', '.join(blocked)}" # Pakete installieren pip_executable = self._get_pip_executable(venv_path) logger.info(f"Installiere Pakete in virtueller Umgebung: {', '.join(packages)}") try: # pip aktualisieren - mache diesen Schritt optional try: subprocess.run( [pip_executable, "install", "--upgrade", "pip"], check=False, # Changed from True to False to make it optional capture_output=True, timeout=60 ) except Exception as pip_error: # Log the error but continue logger.warning(f"Pip-Upgrade fehlgeschlagen, fahre mit Paketinstallation fort: {pip_error}") # Pakete installieren process = subprocess.run( [pip_executable, "install"] + packages, check=True, capture_output=True, text=True, timeout=120 # 2 Minuten Timeout für Paketinstallation ) return True, process.stdout except subprocess.CalledProcessError as e: error_msg = f"Fehler bei der Paketinstallation: {e.stderr}" logger.error(error_msg) return False, error_msg except subprocess.TimeoutExpired: return False, "Zeitüberschreitung bei der Paketinstallation." except Exception as e: return False, f"Unerwarteter Fehler bei der Paketinstallation: {str(e)}" def _extract_required_packages(self, code: str) -> List[str]: """ Extrahiert benötigte Pakete aus dem Code durch Analyse von Import-Statements und Pip-Installationsanweisungen. Args: code: Der Python-Code Returns: Liste der erkannten Paketnamen """ packages = set() # Paketkommentare erkennen (# pip install package) pip_comments = re.findall(r'#\s*pip\s+install\s+([^#\n]+)', code) for comment in pip_comments: for pkg in comment.split(): if pkg and not pkg.startswith('-'): packages.add(pkg.strip()) # Import-Statements analysieren import_lines = re.findall(r'^(?:import|from)\s+([^\s.]+)(?:\s+import|\s*$|\.)', code, re.MULTILINE) # Standardmodule, die nicht installiert werden müssen std_modules = { 'os', 'sys', 'time', 'datetime', 'math', 're', 'random', 'json', 'collections', 'itertools', 'functools', 'pathlib', 'shutil', 'tempfile', 'uuid', 'subprocess', 'threading', 'logging', 'traceback', 'io', 'copy' } # Module der App, die nicht installiert werden müssen app_modules_prefixes = set(m.split('.')[0] for m in self.app_modules) for module in import_lines: if module not in std_modules and module not in app_modules_prefixes: packages.add(module) return list(packages) def _create_module_loader(self) -> str: """ Erstellt ein Hilfsskript, das App-Module in die venv importiert. Gibt den Pfad zum Hilfsskript zurück. """ if not self.app_modules: return "" # Temporäre Datei für den Module-Loader erstellen module_loader_path = os.path.join(self.temp_dir or tempfile.mkdtemp(prefix="ai_code_exec_"), "module_loader.py") # Pfad zu den App-Modulen bestimmen app_path = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) # Modul-Loader-Code generieren loader_code = f""" import sys import importlib.util import os # App-Pfad zum Suchpfad hinzufügen sys.path.insert(0, "{app_path}") # Module importieren modules = {{}} """ # Code zum Importieren der Module hinzufügen for module_name in self.app_modules: loader_code += f""" try: modules["{module_name}"] = __import__("{module_name}", fromlist=["*"]) print(f"Modul '{module_name}' erfolgreich importiert") except ImportError as e: print(f"Fehler beim Importieren von '{module_name}': {{e}}") """ # Loader-Datei schreiben with open(module_loader_path, "w") as f: f.write(loader_code) return module_loader_path def execute_code(self, code: str, input_data: Dict[str, Any] = None) -> Dict[str, Any]: """ Führt den generierten Code in einer isolierten Umgebung aus. Args: code: Der auszuführende Python-Code input_data: Eingabedaten für den Code (werden als JSON serialisiert) Returns: Dict mit Ausführungsergebnissen, Ausgabe und Fehlern """ # Virtuelle Umgebung erstellen oder bestehende verwenden venv_path = self._create_venv() # Erforderliche Pakete aus dem Code extrahieren required_packages = self._extract_required_packages(code) # Pakete installieren, falls erforderlich install_success = True install_log = "" if required_packages: install_success, install_log = self._install_packages(required_packages, venv_path) if not install_success: return { "success": False, "output": "", "error": f"Fehler bei der Installation der erforderlichen Pakete: {install_log}", "result": None, "installed_packages": required_packages } # Temporäre Datei für den Code erstellen code_id = str(uuid.uuid4())[:8] code_file_path = os.path.join(self.temp_dir or tempfile.mkdtemp(prefix="ai_code_exec_"), f"ai_code_{code_id}.py") # Module-Loader erstellen module_loader_path = self._create_module_loader() # Eingabedaten als JSON speichern, wenn vorhanden input_path = "" if input_data: import json input_path = os.path.join(self.temp_dir or tempfile.mkdtemp(prefix="ai_code_exec_"), f"input_{code_id}.json") with open(input_path, "w") as f: json.dump(input_data, f) # Outputpfad für Ergebnisse output_path = os.path.join(self.temp_dir or tempfile.mkdtemp(prefix="ai_code_exec_"), f"output_{code_id}.json") # Prepare all paths using forward slashes for consistency across platforms safe_module_loader_path = module_loader_path.replace('\\', '/') if module_loader_path else "" safe_input_path = input_path.replace('\\', '/') if input_path else "" safe_output_path = output_path.replace('\\', '/') wrapped_code = f""" # -*- coding: utf-8 -*- # coding: utf-8 import sys import json import traceback import os # Ergebnisstruktur result = {{ "success": False, "output": "", "error": "", "result": None, "installed_packages": {required_packages} }} try: # Module laden, falls erforderlich if "{safe_module_loader_path}": module_loader = __import__("module_loader") globals().update({{k: v for k, v in module_loader.modules.items()}}) # Eingabedaten laden, falls vorhanden input_data = None if "{safe_input_path}": with open("{safe_input_path}", "r") as f: input_data = json.load(f) # Ausgabeumleitung from io import StringIO original_stdout = sys.stdout original_stderr = sys.stderr captured_stdout = StringIO() captured_stderr = StringIO() sys.stdout = captured_stdout sys.stderr = captured_stderr # Benutzercode ausführen try: # Den Code in einem lokalen Namespace ausführen local_vars = {{"input_data": input_data}} exec('''{code}''', globals(), local_vars) # Ergebnis speichern, falls eine Variable 'result' definiert wurde if "result" in local_vars: result["result"] = local_vars["result"] result["success"] = True except Exception as e: result["error"] = str(e) result["error"] += "\\n" + traceback.format_exc() finally: # Ausgabe erfassen result["output"] = captured_stdout.getvalue() result["error"] += captured_stderr.getvalue() # Ausgabeumleitung zurücksetzen sys.stdout = original_stdout sys.stderr = original_stderr except Exception as outer_e: result["error"] = f"Fehler beim Ausführen des Setups: {{outer_e}}\\n{{traceback.format_exc()}}" # Ergebnis speichern with open("{safe_output_path}", "w") as f: json.dump(result, f, default=str) """ # Code in temporäre Datei schreiben with UTF-8 encoding with open(code_file_path, "w", encoding="utf-8") as f: f.write(wrapped_code) # Python-Interpreter aus der virtuellen Umgebung bestimmen python_executable = self._get_python_executable(venv_path) # Code ausführen logger.info(f"Führe Code in virtueller Umgebung aus: {python_executable}") try: # Prozess mit Ressourcenbeschränkungen ausführen cmd = [python_executable, code_file_path] # Umgebungsvariablen setzen, um Speicherlimit zu erzwingen env = os.environ.copy() if self.max_memory_mb: if os.name == 'posix': # Unix/Linux # Auf Unix-Systemen können wir ulimit verwenden cmd = ["bash", "-c", f"ulimit -v {self.max_memory_mb * 1024} && {python_executable} {code_file_path}"] elif os.name == 'nt': # Windows # Auf Windows können wir keine harten Speichergrenzen setzen, aber Job Objects verwenden # Hier müsste eine komplexere Lösung implementiert werden pass # Prozess starten und mit Timeout ausführen process = subprocess.run( cmd, timeout=self.timeout, env=env, capture_output=True, text=True ) # Ergebnis aus der Ausgabedatei lesen if os.path.exists(output_path): with open(output_path, "r") as f: import json execution_result = json.load(f) else: execution_result = { "success": False, "output": process.stdout, "error": f"Keine Ergebnisdatei gefunden. Stderr: {process.stderr}", "result": None, "installed_packages": required_packages } except subprocess.TimeoutExpired: execution_result = { "success": False, "output": "", "error": f"Zeitüberschreitung bei der Ausführung (Timeout nach {self.timeout} Sekunden)", "result": None, "installed_packages": required_packages } except Exception as e: execution_result = { "success": False, "output": "", "error": f"Fehler bei der Ausführung: {str(e)}", "result": None, "installed_packages": required_packages } # Informationen zur Paketinstallation hinzufügen if install_log: execution_result["package_install_log"] = install_log # Temporäre Dateien aufräumen self._cleanup_temp_files([code_file_path, input_path, output_path]) return execution_result def _cleanup_temp_files(self, file_paths: List[str]): """Räumt temporäre Dateien auf.""" for path in file_paths: if path and os.path.exists(path): try: os.remove(path) except Exception as e: logger.warning(f"Konnte temporäre Datei nicht löschen {path}: {e}") def cleanup(self): """Räumt alle temporären Ressourcen auf.""" if self.temp_dir and os.path.exists(self.temp_dir): import shutil try: shutil.rmtree(self.temp_dir) logger.info(f"Temporäres Verzeichnis gelöscht: {self.temp_dir}") except Exception as e: logger.warning(f"Konnte temporäres Verzeichnis nicht löschen {self.temp_dir}: {e}") def __del__(self): """Aufräumen beim Garbage Collection.""" self.cleanup() class CoderAgent(BaseAgent): """Erweiterter Agent für die Entwicklung und Ausführung von Python-Code""" def __init__(self): """Initialize the coder agent with proper type and capabilities""" super().__init__() # Agent metadata self.id = "coder" self.type = "coder" self.name = "Python Code Agent" self.description = "Entwickelt und führt Python-Code aus" self.capabilities = "code_development,data_processing,file_processing,automation" self.result_format = "python_code" # Init utilities self.file_utils = FileUtils() self.message_utils = MessageUtils() # Executor settings self.executor_timeout = 60 # seconds self.executor_memory_limit = 512 # MB # AI service settings self.ai_temperature = 0.2 # Lower temperature for more deterministic code generation self.ai_max_tokens = 2000 # Enough tokens for complex code def get_agent_info(self) -> Dict[str, Any]: """Get agent information for agent registry""" return { "id": self.id, "type": self.type, "name": self.name, "description": self.description, "capabilities": self.capabilities, "result_format": self.result_format, "metadata": { "timeout": self.executor_timeout, "memory_limit": self.executor_memory_limit } } async def process_message(self, message: Dict[str, Any], workflow: Dict[str, Any], context: Dict[str, Any] = None, log_func=None) -> Dict[str, Any]: """ Processes a message to develop and execute Python code. Args: message: The message to process workflow: The current workflow context: Additional context information log_func: Function for workflow logging Returns: Response message """ # Initialize logging workflow_id = workflow.get("id") logging_utils = LoggingUtils(workflow_id, log_func) logging_utils.info(f"CoderAgent startet Verarbeitung", "agents") # Initialize utilities workflow_utils = WorkflowUtils(workflow_id) # Create response message response = self.message_utils.create_message(workflow_id, role="assistant") response["agent_type"] = self.type response["agent_name"] = self.name response["parent_message_id"] = message.get("id") try: # Check if user directly provided code content = message.get("content", "") documents = message.get("documents", []) # Extract code from message content code_blocks = re.findall(r'```(?:python)?\s*([\s\S]*?)```', content) code_to_execute = None if code_blocks: # Use the first code block found code_to_execute = code_blocks[0] logging_utils.info(f"Code aus Nachricht extrahiert ({len(code_to_execute)} Zeichen)", "agents") else: # Generate code based on the message content using OpenAI logging_utils.info("Kein Code in der Nachricht gefunden, generiere neuen Code mit AI", "agents") # Generate code using AI code_to_execute = await self._generate_code_from_prompt(content, documents, context) if not code_to_execute: logging_utils.warning("AI konnte keinen Code generieren", "agents") response["content"] = "Ich konnte basierend auf Ihrer Anfrage keinen ausführbaren Code generieren. Bitte geben Sie detailliertere Anweisungen an." self.message_utils.finalize_message(response) return response logging_utils.info(f"Code mit AI generiert ({len(code_to_execute)} Zeichen)", "agents") # Get database interface for code execution mandate_id = workflow.get("mandate_id", 0) user_id = workflow.get("user_id", 0) lucydom_interface = get_lucydom_interface(mandate_id, user_id) # Execute the code if code_to_execute: logging_utils.info("Führe Code aus", "execution") # Prepare execution context execution_context = { "workflow_id": workflow_id, "documents": documents, "message": message, "mandate_id": mandate_id, "user_id": user_id } # Execute code result = await self._execute_code(code_to_execute, lucydom_interface, execution_context) # Prepare response if result.get("success", False): # Code execution successful output = result.get("output", "") execution_result = result.get("result") logging_utils.info("Code erfolgreich ausgeführt", "execution") # Format response content response_content = f"## Code erfolgreich ausgeführt\n\n" # Include the executed code response_content += f"### Ausgeführter Code\n\n```python\n{code_to_execute}\n```\n\n" # Include the output if available if output: response_content += f"### Ausgabe\n\n```\n{output}\n```\n\n" # Include the execution result if available if execution_result: result_str = json.dumps(execution_result, indent=2) if isinstance(execution_result, (dict, list)) else str(execution_result) response_content += f"### Ergebnis\n\n```\n{result_str}\n```\n\n" response["content"] = response_content # Process any files created by the code if isinstance(execution_result, dict) and "created_files" in execution_result: created_files = execution_result.get("created_files", []) for file_info in created_files: file_id = file_info.get("id") if file_id: logging_utils.info(f"Füge erstellte Datei {file_info.get('name', file_id)} zu Dokumenten hinzu", "files") file_meta = lucydom_interface.get_file(file_id) if file_meta: # Add file document to the response doc = { "id": f"doc_{uuid.uuid4()}", "source": file_meta, "type": "file" } response["documents"].append(doc) else: # Code execution failed error = result.get("error", "Unbekannter Fehler") logging_utils.error(f"Fehler bei der Codeausführung: {error}", "execution") # Format error response response_content = f"## Fehler bei der Codeausführung\n\n" response_content += f"### Ausgeführter Code\n\n```python\n{code_to_execute}\n```\n\n" response_content += f"### Fehler\n\n```\n{error}\n```\n\n" # Add recommendation based on error response_content += self._get_error_recommendation(error) response["content"] = response_content else: # No code to execute response["content"] = "Ich konnte keinen ausführbaren Code finden oder generieren. Bitte geben Sie Python-Code an oder erläutern Sie Ihre Anforderungen genauer." # Finalize response self.message_utils.finalize_message(response) # Log success logging_utils.info("CoderAgent hat die Anfrage erfolgreich verarbeitet", "agents") return response except Exception as e: error_msg = f"Fehler bei der Verarbeitung durch den CoderAgent: {str(e)}" logging_utils.error(error_msg, "error") # Create error response response["content"] = f"## Fehler bei der Verarbeitung\n\n```\n{error_msg}\n\n{traceback.format_exc()}\n```" self.message_utils.finalize_message(response) return response async def _generate_code_from_prompt(self, prompt: str, documents: List[Dict[str, Any]], context: Dict[str, Any] = None) -> str: """ Generate Python code from a prompt using OpenAI service. Args: prompt: The prompt to generate code from documents: Documents associated with the prompt context: Additional context information Returns: Generated Python code """ try: # Initialize AI service chat_service = ChatService() # Prepare a detailed prompt for code generation ai_prompt = self._prepare_code_prompt(prompt, documents) # Create messages for the OpenAI API messages = [ {"role": "system", "content": "You are a Python code generator. Generate only executable Python code without explanations. The code should be well-commented, handle errors appropriately, and follow best practices."}, {"role": "user", "content": ai_prompt} ] # Call the OpenAI API logging.info(f"Calling OpenAI API to generate code") generated_content = await chat_service.call_api(messages, temperature=self.ai_temperature, max_tokens=self.ai_max_tokens) # Extract code from the response (the AI might wrap it in markdown) code_blocks = re.findall(r'```(?:python)?\s*([\s\S]*?)```', generated_content) if code_blocks: # Use the first code block found return code_blocks[0].strip() else: # If no code block is found, return the raw response return generated_content.strip() except Exception as e: logging.error(f"Error generating code with AI: {str(e)}", exc_info=True) # Return a basic error-handling code error_msg = str(e).replace('"', '\\"') return f""" # Error during code generation print("An error occurred during code generation: {error_msg}") # Return an error result result = {{"error": "Code generation failed", "message": "{error_msg}"}} """ def _prepare_code_prompt(self, user_prompt: str, documents: List[Dict[str, Any]]) -> str: """ Prepares a detailed prompt for the AI to generate Python code. Args: user_prompt: The original user request documents: Available documents Returns: A detailed prompt for code generation """ # Start with the user's request prompt = f"""Generate Python code to solve the following task: {user_prompt} """ # Add information about available documents if documents: prompt += "\nAvailable documents:\n" for i, doc in enumerate(documents): source = doc.get("source", {}) doc_name = source.get("name", f"Document {i+1}") doc_type = source.get("content_type", "unknown") doc_id = source.get("id", "") prompt += f"- {doc_name} (type: {doc_type}, id: {doc_id})\n" # Add information about how to access documents prompt += """ To access these documents, use: - await load_file(file_id, encoding='utf-8') for text files - await load_file(file_id) for binary files """ # Add information about available helper functions prompt += """ Available helper functions: 1. load_file(file_id, encoding=None): - Asynchronous function to load file content - Returns string if encoding is provided, otherwise bytes 2. save_file(content, file_name, content_type=None): - Saves content as a file and returns metadata 3. update_file(file_id, content, update_metadata=None): - Updates an existing file with new content 4. get_file_metadata(file_id): - Returns metadata for a file 5. process_csv(content, operations=None): - Processes CSV data with pandas 6. extract_text_from_pdf(pdf_data): - Extracts text from PDF documents Requirements: - The code should be fully functional and handle errors - Use async/await for asynchronous operations - Return results in the 'result' variable as a dictionary - For file operations, use the provided helper functions - Always import necessary libraries at the top """ return prompt async def _execute_code(self, code: str, lucydom_interface, context: Dict[str, Any] = None) -> Dict[str, Any]: """ Führt Python-Code mit dem eingebauten CodeExecutor aus. Args: code: Der auszuführende Python-Code lucydom_interface: Interface für Datenbankzugriffe context: Zusätzlicher Kontext Returns: Ergebnis der Codeausführung """ try: # Systemfunktionen für den Code vorbereiten system_functions_code = self._prepare_system_functions(lucydom_interface) # Code mit Systemfunktionen erweitern enhanced_code = system_functions_code + "\n\n" + code # Liste verfügbarer Module available_modules = [ "modules.lucydom_interface", "modules.lucydom_model", "modules.agentservice_utils" ] # Liste erlaubter Pakete allowed_packages = None # None bedeutet alle erlaubt außer explizit blockierte # Liste blockierter Pakete blocked_packages = [ "cryptography", "flask", "django", "tornado", # Sicherheitsrisiken "tensorflow", "pytorch", "scikit-learn" # Ressourcenintensiv ] # CodeExecutor initialisieren executor = CodeExecutor( app_modules=available_modules, timeout=self.executor_timeout, max_memory_mb=self.executor_memory_limit, allowed_packages=allowed_packages, blocked_packages=blocked_packages ) try: # Eingabedaten vorbereiten input_data = { "context": context, "workflow_id": context.get("workflow_id", "") if context else "", } # Dateireferenzen hinzufügen if context and "documents" in context: file_refs = [] for doc in context.get("documents", []): source = doc.get("source", {}) if source.get("type") == "file": file_refs.append({ "id": source.get("id", ""), "name": source.get("name", ""), "type": source.get("content_type", "") }) input_data["files"] = file_refs # Code ausführen result = executor.execute_code(enhanced_code, input_data) # Log für die Ausführung if result.get("success", False): logger.info(f"Code erfolgreich ausgeführt") output = result.get("output", "") if output: logger.debug(f"Ausgabe: {output[:200]}..." if len(output) > 200 else output) else: logger.error(f"Fehler bei der Codeausführung: {result.get('error', 'Unbekannter Fehler')}") return result finally: # Ressourcen freigeben executor.cleanup() except Exception as e: logger.error(f"Fehler bei der Codeausführung: {str(e)}", exc_info=True) return { "success": False, "output": "", "error": f"Fehler bei der Ausführung: {str(e)}\n{traceback.format_exc()}", "result": None } def _prepare_system_functions(self, lucydom_interface) -> str: """ Bereitet die Systemfunktionen für den auszuführenden Code vor. Args: lucydom_interface: Interface für Datenbankzugriffe Returns: Python-Code für die Systemfunktionen """ # Get all helper functions from the module helper_functions = [] for name, func in inspect.getmembers(agentservice_code_helpers, inspect.isfunction): # Get the source code of the function source = inspect.getsource(func) helper_functions.append(source) # Combine all functions into a single string functions_code = "\n\n".join(helper_functions) # Add code to make lucydom_interface available setup_code = """ # lucydom_interface global verfügbar machen import asyncio """ return functions_code + "\n\n" + setup_code def _get_error_recommendation(self, error_message: str) -> str: """ Generate recommendations based on error message Args: error_message: The error message Returns: Recommendation text """ # Common error patterns and recommendations if "ImportError" in error_message or "ModuleNotFoundError" in error_message: return """ ### Empfehlung Der Fehler deutet auf ein fehlendes Python-Modul hin. Einige Gründe könnten sein: 1. Das Modul ist in der Ausführungsumgebung nicht installiert 2. Das Modul ist aus Sicherheitsgründen blockiert (z.B. tensorflow, pytorch) 3. Es gibt einen Tippfehler im Modulnamen Versuchen Sie, nur Standardbibliotheken oder gängige Datenanalyse-Module wie pandas, numpy, matplotlib zu verwenden. """ elif "PermissionError" in error_message: return """ ### Empfehlung Der Code hat nicht die nötigen Berechtigungen für den Zugriff auf Dateien oder Verzeichnisse. Bitte nutzen Sie die bereitgestellten Funktionen `load_file()` und `save_file()` für Dateizugriffe. """ elif "SyntaxError" in error_message: return """ ### Empfehlung Im Code gibt es einen Syntaxfehler. Häufige Ursachen sind: 1. Fehlende oder überzählige Klammern, Anführungszeichen oder Doppelpunkte 2. Einrückungsfehler 3. Ungültige Python-Syntax Überprüfen Sie den Code auf solche Fehler und korrigieren Sie ihn. """ elif "FileNotFoundError" in error_message: return """ ### Empfehlung Eine Datei konnte nicht gefunden werden. Wenn Sie auf Dateien zugreifen möchten: 1. Nutzen Sie die bereitgestellte `load_file(file_id)` Funktion 2. Stellen Sie sicher, dass die Datei-ID korrekt ist 3. Prüfen Sie, ob die Datei im Workflow verfügbar ist """ elif "TypeError" in error_message: return """ ### Empfehlung Es gibt einen Typfehler im Code. Überprüfen Sie: 1. Ob die richtigen Datentypen verwendet werden 2. Ob Konvertierungen zwischen Typen (z.B. str zu int) korrekt durchgeführt werden 3. Ob die Parameter für Funktionen den erwarteten Typen entsprechen """ else: return """ ### Empfehlung Um den Fehler zu beheben: 1. Überprüfen Sie die genaue Fehlermeldung 2. Vereinfachen Sie den Code und testen Sie schrittweise 3. Stellen Sie sicher, dass alle benötigten Daten korrekt geladen werden 4. Verwenden Sie try/except-Blöcke für fehleranfällige Operationen """ # Singleton-Instanz _coder_agent = None def get_coder_agent(): """Gibt eine Singleton-Instanz des Coder-Agenten zurück""" global _coder_agent if _coder_agent is None: _coder_agent = CoderAgent() return _coder_agent