|
from smolagents.tools import Tool |
|
import pdb |
|
import smolagents |
|
import io |
|
import sys |
|
|
|
class LocalPythonDebuggerTool(Tool): |
|
name = "local-python-debugger" |
|
description = """ |
|
The Local Python Debugger Tool is designed to assist in debugging InterpreterErrors—specifically, errors related to "Code execution failed at line". |
|
It is not intended for other types of InterpreterErrors, such as: |
|
- "Reached the max number of operations" |
|
- "Code parsing failed on line" |
|
|
|
The tool debugs code executed in the following manner: |
|
```python |
|
expression = ast.parse(code) |
|
for node in expression.body: |
|
result = evaluate_ast(node, ...) |
|
``` |
|
where evaluate_ast processes the Abstract Syntax Tree (AST) by evaluating AST functions like: ast.Assign, ast.AugAssign, ast.Call, ast.Constant, ast.Name, ast.BinOp, etc. |
|
As a result, the debugger output reflects the evaluate_ast execution context rather than a direct Python runtime environment. |
|
|
|
Possible debugger commands: |
|
- bt - Shows the full stack trace, helping to understand where the error occurred in the call stack. |
|
- args - Shows the arguments passed to the function. |
|
- l - Lists the source code of the current file. |
|
- p {variable} - Prints the value of the variable. NOTE: variable names are different than in the code. They should be inspected first with `l` command. |
|
|
|
""" |
|
inputs = {'code': {'type': 'string', 'description': 'code to debug'}, 'command': {'type': 'string', 'description': 'pdb command to execute'}} |
|
output_type = "string" |
|
|
|
def setup(self): |
|
from smolagents import LocalPythonInterpreter |
|
from smolagents.default_tools import FinalAnswerTool |
|
|
|
import io |
|
import pdb |
|
|
|
self.python_executor = LocalPythonInterpreter( |
|
[], |
|
{"final_answer": FinalAnswerTool()}, |
|
max_print_outputs_length=None, |
|
) |
|
|
|
self.output_capture = io.StringIO() |
|
|
|
self.pdb_instance = pdb.Pdb(stdout=self.output_capture) |
|
|
|
def wrap_code_with_trycatch(self, code: str) -> str: |
|
from smolagents.utils import parse_code_blobs |
|
|
|
try: |
|
code = parse_code_blobs(code) |
|
except Exception as e: |
|
pass |
|
|
|
code_lines = code.split("\n") |
|
for i, line in enumerate(code_lines): |
|
code_lines[i] = f"\t{line}" |
|
code = "\n".join(code_lines) |
|
|
|
|
|
code = ( |
|
f"try:\n{code}\n" + "except Exception as e:" + "\n\t" + "out=sys.exc_info()" |
|
) |
|
code = "out=None" + "\n" + code + "\n" + "out" |
|
|
|
return code |
|
|
|
def execute_code(self, code: str, state: dict): |
|
from smolagents.utils import parse_code_blobs |
|
|
|
code = parse_code_blobs(code) |
|
|
|
|
|
py_executor_output = None |
|
py_executor_logs = None |
|
py_executor_is_final_answer = None |
|
|
|
py_executor_output, py_executor_logs, py_executor_is_final_answer = ( |
|
self.python_executor(code, state) |
|
) |
|
|
|
return py_executor_output, py_executor_logs, py_executor_is_final_answer |
|
|
|
def execude_pdb_command( |
|
self, command: str, traceback |
|
): |
|
output = None |
|
try: |
|
|
|
frame = traceback.tb_frame |
|
while frame.f_back: |
|
frame = frame.f_back |
|
self.pdb_instance.reset() |
|
self.pdb_instance.setup(frame, traceback) |
|
|
|
self.pdb_instance.onecmd(command) |
|
|
|
output = self.output_capture.getvalue() |
|
finally: |
|
|
|
self.output_capture.truncate(0) |
|
self.output_capture.seek(0) |
|
|
|
return output |
|
|
|
def forward(self, code: str, command: str): |
|
import sys |
|
|
|
code = self.wrap_code_with_trycatch(code) |
|
|
|
state = {"sys": sys} |
|
|
|
|
|
py_executor_output = None |
|
tb = None |
|
|
|
py_executor_output, py_executor_logs, py_executor_is_final_answer = ( |
|
self.execute_code(code, state) |
|
) |
|
|
|
if py_executor_output is not None: |
|
try: |
|
exc_type, exc_value, tb = py_executor_output |
|
except Exception as e: |
|
return "No traceback found" |
|
pdb_result = self.execude_pdb_command(command, tb) |
|
return pdb_result |
|
|
|
return "No traceback found" |
|
|
|
def __init__(self, *args, **kwargs): |
|
self.is_initialized = False |
|
|