Spaces:
Build error
Build error
| """ | |
| code_kernel.py | |
| Sandboxed Python execution kernel for Aetherius. | |
| Runs arbitrary Python code in a subprocess with: | |
| - Configurable timeout (default 30s) | |
| - stdout/stderr capture | |
| - Output truncation to prevent memory flooding | |
| - Matplotlib figure auto-saving (if code generates plots) | |
| - On Kaggle T4: full numpy/scipy/torch CUDA access in the subprocess | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| import uuid | |
| import tempfile | |
| import subprocess | |
| import json | |
| TIMEOUT_DEFAULT = 30 | |
| MAX_OUTPUT = 8000 | |
| _PLOTS_DIR_ENV = "AETHERIUS_PAINTINGS_DIR" | |
| def _get_plots_dir() -> str: | |
| try: | |
| import services.config as cfg | |
| return cfg.PAINTINGS_DIR.rstrip("/") | |
| except Exception: | |
| return os.environ.get(_PLOTS_DIR_ENV, "/tmp/aetherius_plots") | |
| _PREAMBLE = """\ | |
| import os, sys, warnings | |
| warnings.filterwarnings("ignore") | |
| # Matplotlib non-interactive backend so plots save without a display | |
| import matplotlib | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| _PLOT_SAVE_DIR = {plot_dir!r} | |
| os.makedirs(_PLOT_SAVE_DIR, exist_ok=True) | |
| _PLOT_PATH = None | |
| def _save_current_figure(): | |
| global _PLOT_PATH | |
| import uuid | |
| _PLOT_PATH = os.path.join(_PLOT_SAVE_DIR, f"plot_{{uuid.uuid4().hex[:8]}}.png") | |
| plt.savefig(_PLOT_PATH, dpi=150, bbox_inches="tight") | |
| plt.close("all") | |
| print(f"[code_kernel] Plot saved: {{_PLOT_PATH}}") | |
| import atexit | |
| atexit.register(lambda: _save_current_figure() if plt.get_fignums() else None) | |
| # ── User code begins ────────────────────────────────────────────────────────── | |
| """ | |
| def execute(code: str, timeout: int = TIMEOUT_DEFAULT) -> dict: | |
| """ | |
| Execute Python code in an isolated subprocess. | |
| Returns: | |
| { | |
| "success": bool, | |
| "stdout": str, | |
| "stderr": str, | |
| "returncode": int, | |
| "plot_path": str | None, # set if matplotlib figure was saved | |
| } | |
| """ | |
| plots_dir = _get_plots_dir() | |
| os.makedirs(plots_dir, exist_ok=True) | |
| full_code = _PREAMBLE.format(plot_dir=plots_dir) + code | |
| tmp = tempfile.NamedTemporaryFile( | |
| mode="w", suffix=".py", delete=False, encoding="utf-8" | |
| ) | |
| try: | |
| tmp.write(full_code) | |
| tmp.close() | |
| proc = subprocess.run( | |
| [sys.executable, tmp.name], | |
| capture_output=True, | |
| text=True, | |
| timeout=timeout, | |
| ) | |
| stdout = proc.stdout[:MAX_OUTPUT] | |
| stderr = proc.stderr[:MAX_OUTPUT] | |
| success = proc.returncode == 0 | |
| # Check if a plot was saved (preamble prints the path) | |
| plot_path = None | |
| for line in stdout.splitlines(): | |
| if line.startswith("[code_kernel] Plot saved:"): | |
| plot_path = line.split(":", 1)[1].strip() | |
| break | |
| return { | |
| "success": success, | |
| "stdout": stdout, | |
| "stderr": stderr, | |
| "returncode": proc.returncode, | |
| "plot_path": plot_path, | |
| } | |
| except subprocess.TimeoutExpired: | |
| return { | |
| "success": False, | |
| "stdout": "", | |
| "stderr": f"[code_kernel] Timed out after {timeout}s.", | |
| "returncode": -1, | |
| "plot_path": None, | |
| } | |
| except Exception as exc: | |
| return { | |
| "success": False, | |
| "stdout": "", | |
| "stderr": f"[code_kernel] Internal error: {exc}", | |
| "returncode": -1, | |
| "plot_path": None, | |
| } | |
| finally: | |
| try: | |
| os.unlink(tmp.name) | |
| except Exception: | |
| pass | |
| def execute_sandboxed_validation(file_path: str, timeout: int = 15) -> dict: | |
| """ | |
| Runs a .py file in a subprocess for Pass-2 validation during | |
| stage_and_verify_code_patch. The file is executed with a minimal | |
| import-only simulation: the code is loaded as a module to catch | |
| runtime import errors, circular dependencies, and top-level | |
| exceptions — without triggering any side effects. | |
| Returns {"success": bool, "error": str | None, "stdout": str} | |
| """ | |
| validation_wrapper = f""" | |
| import sys, traceback | |
| try: | |
| import importlib.util | |
| spec = importlib.util.spec_from_file_location("_patch_validation", {file_path!r}) | |
| mod = importlib.util.module_from_spec(spec) | |
| # We do NOT exec the module body for safety — just check it compiles | |
| with open({file_path!r}, 'r', encoding='utf-8') as f: | |
| source = f.read() | |
| compile(source, {file_path!r}, 'exec') | |
| print("VALIDATION_OK") | |
| except SyntaxError as se: | |
| print(f"SYNTAX_ERROR: {{se}}", file=sys.stderr) | |
| sys.exit(1) | |
| except Exception as e: | |
| print(f"RUNTIME_ERROR: {{e}}", file=sys.stderr) | |
| sys.exit(2) | |
| """ | |
| tmp = tempfile.NamedTemporaryFile( | |
| mode="w", suffix=".py", delete=False, encoding="utf-8" | |
| ) | |
| try: | |
| tmp.write(validation_wrapper) | |
| tmp.close() | |
| proc = subprocess.run( | |
| [sys.executable, tmp.name], | |
| capture_output=True, text=True, timeout=timeout, | |
| ) | |
| if proc.returncode == 0: | |
| return {"success": True, "error": None, "stdout": proc.stdout.strip()} | |
| return { | |
| "success": False, | |
| "error": (proc.stderr or proc.stdout).strip(), | |
| "stdout": proc.stdout.strip(), | |
| } | |
| except subprocess.TimeoutExpired: | |
| return {"success": False, "error": "Validation timed out.", "stdout": ""} | |
| except Exception as exc: | |
| return {"success": False, "error": str(exc), "stdout": ""} | |
| finally: | |
| try: | |
| os.unlink(tmp.name) | |
| except Exception: | |
| pass | |
| def format_result(result: dict) -> str: | |
| """Format execution result as a readable string for Aetherius.""" | |
| parts = [] | |
| if result["success"]: | |
| parts.append("✓ Execution succeeded.") | |
| else: | |
| parts.append(f"✗ Execution failed (exit {result['returncode']}).") | |
| if result["stdout"]: | |
| parts.append(f"Output:\n{result['stdout']}") | |
| if result["stderr"]: | |
| label = "Warnings/Errors" if result["success"] else "Error" | |
| parts.append(f"{label}:\n{result['stderr']}") | |
| if result["plot_path"]: | |
| parts.append(f"[AETHERIUS_PAINTING]\nPATH:{result['plot_path']}\nSTATEMENT:code-generated plot") | |
| return "\n\n".join(parts) if parts else "No output." | |