|
import os |
|
import tempfile |
|
import subprocess |
|
import logging |
|
from typing import Optional, Dict, Any |
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
logger = logging.getLogger(__name__) |
|
|
|
class CodeInterpreter: |
|
"""Simple code interpreter for executing Python code safely.""" |
|
|
|
def __init__(self, working_dir: Optional[str] = None): |
|
"""Initialize the code interpreter with a working directory.""" |
|
if working_dir is None: |
|
self.working_dir = tempfile.mkdtemp() |
|
else: |
|
self.working_dir = working_dir |
|
os.makedirs(working_dir, exist_ok=True) |
|
|
|
logger.info(f"Initialized CodeInterpreter with working directory: {self.working_dir}") |
|
|
|
def execute(self, code: str, language: str = "python") -> Dict[str, Any]: |
|
"""Execute code and return results.""" |
|
try: |
|
if language.lower() != "python": |
|
return { |
|
"status": "error", |
|
"stdout": "", |
|
"stderr": f"Language '{language}' not supported. Only Python is supported.", |
|
"plots": [], |
|
"dataframes": [] |
|
} |
|
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, dir=self.working_dir) as f: |
|
f.write(code) |
|
temp_file = f.name |
|
|
|
try: |
|
|
|
result = subprocess.run( |
|
["python", temp_file], |
|
capture_output=True, |
|
text=True, |
|
timeout=30, |
|
cwd=self.working_dir |
|
) |
|
|
|
status = "success" if result.returncode == 0 else "error" |
|
|
|
return { |
|
"status": status, |
|
"stdout": result.stdout, |
|
"stderr": result.stderr, |
|
"plots": [], |
|
"dataframes": [] |
|
} |
|
|
|
finally: |
|
|
|
try: |
|
os.unlink(temp_file) |
|
except OSError: |
|
pass |
|
|
|
except subprocess.TimeoutExpired: |
|
return { |
|
"status": "error", |
|
"stdout": "", |
|
"stderr": "Code execution timed out (30 seconds)", |
|
"plots": [], |
|
"dataframes": [] |
|
} |
|
except Exception as e: |
|
logger.error(f"Error executing code: {str(e)}") |
|
return { |
|
"status": "error", |
|
"stdout": "", |
|
"stderr": str(e), |
|
"plots": [], |
|
"dataframes": [] |
|
} |
|
|
|
class CodeInterpreterTool: |
|
"""Tool wrapper for the code interpreter.""" |
|
|
|
def __init__(self, working_directory: Optional[str] = None): |
|
"""Initialize the code interpreter tool.""" |
|
|
|
default_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "code_outputs")) |
|
self.interpreter = CodeInterpreter( |
|
working_dir=working_directory or default_dir |
|
) |
|
|
|
def execute(self, code: str, language: str = "python") -> Dict[str, Any]: |
|
"""Execute code and return results.""" |
|
try: |
|
logger.info(f"Executing {language} code") |
|
result = self.interpreter.execute(code, language) |
|
|
|
|
|
response = { |
|
"status": result["status"], |
|
"output": result["stdout"], |
|
"error": result["stderr"] if result["status"] == "error" else None, |
|
"plots": result.get("plots", []), |
|
"dataframes": result.get("dataframes", []) |
|
} |
|
|
|
return response |
|
except Exception as e: |
|
logger.error(f"Error executing code: {str(e)}") |
|
return { |
|
"status": "error", |
|
"error": str(e), |
|
"output": None, |
|
"plots": [], |
|
"dataframes": [] |
|
} |