foundationagents--openmanus
d0cf79b6f4
Pre-commit checks / pre-commit-check (push) Failing after 1s
Add E2B sandbox for code execution, and basic Sandbox for computer use.
119 行
5.0 KiB
Python
119 行
5.0 KiB
Python
from dotenv import load_dotenv
|
|
load_dotenv()
|
|
from typing import Dict
|
|
from app.tool.base import BaseTool
|
|
import asyncio
|
|
import multiprocessing
|
|
import sys
|
|
from io import StringIO
|
|
from app.config import config
|
|
|
|
class SandboxPythonExecute(BaseTool):
|
|
"""A tool for executing Python code either locally (with timeout) or in a sandboxed environment using e2b_code_interpreter."""
|
|
|
|
name: str = "python_execute"
|
|
description: str = (
|
|
"Executes Python code string. Note: Only print outputs are visible, function return values are not captured. Use print statements to see results. "
|
|
"Set mode='sandbox' to run in a secure sandbox (e2b), otherwise runs locally."
|
|
)
|
|
parameters: dict = {
|
|
"type": "object",
|
|
"properties": {
|
|
"code": {
|
|
"type": "string",
|
|
"description": "The Python code to execute.",
|
|
},
|
|
"mode": {
|
|
"type": "string",
|
|
"enum": ["local", "sandbox"],
|
|
"description": "Execution mode: 'local' (default) or 'sandbox' (e2b sandbox)",
|
|
},
|
|
"timeout": {
|
|
"type": "integer",
|
|
"description": "Execution timeout in seconds (default: 5 for local, 10 for sandbox)",
|
|
},
|
|
},
|
|
"required": ["code"],
|
|
}
|
|
|
|
def _run_code(self, code: str, result_dict: dict, safe_globals: dict) -> None:
|
|
original_stdout = sys.stdout
|
|
try:
|
|
output_buffer = StringIO()
|
|
sys.stdout = output_buffer
|
|
exec(code, safe_globals, safe_globals)
|
|
result_dict["observation"] = output_buffer.getvalue()
|
|
result_dict["success"] = True
|
|
except Exception as e:
|
|
result_dict["observation"] = str(e)
|
|
result_dict["success"] = False
|
|
finally:
|
|
sys.stdout = original_stdout
|
|
|
|
async def execute(
|
|
self,
|
|
code: str,
|
|
timeout: int = None,
|
|
mode: str = "local",
|
|
) -> Dict:
|
|
"""
|
|
Executes the provided Python code in the selected environment.
|
|
Args:
|
|
code (str): The Python code to execute.
|
|
timeout (int): Execution timeout in seconds.
|
|
mode (str): 'local' or 'sandbox'.
|
|
Returns:
|
|
Dict: Contains 'observation' with execution output or error message and 'success' status.
|
|
"""
|
|
if mode == "sandbox":
|
|
# Use e2b_code_interpreter Sandbox
|
|
try:
|
|
from e2b_code_interpreter import Sandbox
|
|
except ImportError:
|
|
return {"observation": "e2b_code_interpreter not installed.", "success": False}
|
|
# Get sandbox config from app.config.config
|
|
sandbox_config = getattr(config, "cloud_sandbox", {})
|
|
# You can pass config to Sandbox() if needed, e.g. Sandbox(api_key=sandbox_config.get("api_key"))
|
|
sbx = Sandbox(**sandbox_config) if sandbox_config else Sandbox()
|
|
try:
|
|
# Default timeout for sandbox is 10s if not set
|
|
effective_timeout = timeout if timeout is not None else 10
|
|
async def run():
|
|
execution = sbx.run_code(code)
|
|
logs = execution.logs if hasattr(execution, 'logs') else str(execution)
|
|
success = execution.error is None if hasattr(execution, 'error') else True
|
|
observation = logs
|
|
if not success:
|
|
observation = f"Error: {getattr(execution, 'error', 'Unknown error')}\n{logs}"
|
|
return {"observation": observation, "success": success}
|
|
result = await asyncio.wait_for(run(), timeout=effective_timeout)
|
|
return result
|
|
except asyncio.TimeoutError:
|
|
return {"observation": f"Execution timeout after {effective_timeout} seconds", "success": False}
|
|
except Exception as e:
|
|
return {"observation": str(e), "success": False}
|
|
finally:
|
|
sbx.kill()
|
|
else:
|
|
# Local execution (default)
|
|
effective_timeout = timeout if timeout is not None else 5
|
|
with multiprocessing.Manager() as manager:
|
|
result = manager.dict({"observation": "", "success": False})
|
|
if isinstance(__builtins__, dict):
|
|
safe_globals = {"__builtins__": __builtins__}
|
|
else:
|
|
safe_globals = {"__builtins__": __builtins__.__dict__.copy()}
|
|
proc = multiprocessing.Process(
|
|
target=self._run_code, args=(code, result, safe_globals)
|
|
)
|
|
proc.start()
|
|
proc.join(effective_timeout)
|
|
if proc.is_alive():
|
|
proc.terminate()
|
|
proc.join(1)
|
|
return {
|
|
"observation": f"Execution timeout after {effective_timeout} seconds",
|
|
"success": False,
|
|
}
|
|
return dict(result)
|