|
|
import multiprocessing |
|
|
import sys |
|
|
from io import StringIO |
|
|
from typing import Dict |
|
|
|
|
|
from app.tool.base import BaseTool |
|
|
|
|
|
|
|
|
class PythonExecute(BaseTool): |
|
|
"""A tool for executing Python code with timeout and safety restrictions.""" |
|
|
|
|
|
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." |
|
|
parameters: dict = { |
|
|
"type": "object", |
|
|
"properties": { |
|
|
"code": { |
|
|
"type": "string", |
|
|
"description": "The Python code to execute.", |
|
|
}, |
|
|
}, |
|
|
"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 = 5, |
|
|
) -> Dict: |
|
|
""" |
|
|
Executes the provided Python code with a timeout. |
|
|
|
|
|
Args: |
|
|
code (str): The Python code to execute. |
|
|
timeout (int): Execution timeout in seconds. |
|
|
|
|
|
Returns: |
|
|
Dict: Contains 'output' with execution output or error message and 'success' status. |
|
|
""" |
|
|
|
|
|
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(timeout) |
|
|
|
|
|
|
|
|
if proc.is_alive(): |
|
|
proc.terminate() |
|
|
proc.join(1) |
|
|
return { |
|
|
"observation": f"Execution timeout after {timeout} seconds", |
|
|
"success": False, |
|
|
} |
|
|
return dict(result) |
|
|
|