piotrekgrl/smolagents-local-python-debugger-tool
3
1from smolagents.tools import Tool2import pdb3import smolagents4import io5import sys6 7class LocalPythonDebuggerTool(Tool):8 name = "local-python-debugger"9 description = """10 The Local Python Debugger Tool is designed to assist in debugging InterpreterErrors—specifically, errors related to "Code execution failed at line". 11 It is not intended for other types of InterpreterErrors, such as:12 - "Reached the max number of operations"13 - "Code parsing failed on line"14 15 The tool debugs code executed in the following manner:16 ```python17 expression = ast.parse(code)18 for node in expression.body:19 result = evaluate_ast(node, ...)20 ```21 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.22 As a result, the debugger output reflects the evaluate_ast execution context rather than a direct Python runtime environment.23 24 Possible debugger commands:25 - bt - Shows the full stack trace, helping to understand where the error occurred in the call stack.26 - args - Shows the arguments passed to the function.27 - l - Lists the source code of the current file.28 - 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.29 30 """31 inputs = {'code': {'type': 'string', 'description': 'code to debug'}, 'command': {'type': 'string', 'description': 'pdb command to execute'}}32 output_type = "string"33 34 def setup(self):35 from smolagents import LocalPythonInterpreter36 from smolagents.default_tools import FinalAnswerTool37 38 import io39 import pdb40 41 self.python_executor = LocalPythonInterpreter(42 [],43 {"final_answer": FinalAnswerTool()},44 max_print_outputs_length=None,45 )46 47 self.output_capture = io.StringIO()48 49 self.pdb_instance = pdb.Pdb(stdout=self.output_capture)50 51 def wrap_code_with_trycatch(self, code: str) -> str:52 from smolagents.utils import parse_code_blobs53 54 try:55 code = parse_code_blobs(code)56 except Exception as e:57 pass58 59 code_lines = code.split("\n")60 for i, line in enumerate(code_lines):61 code_lines[i] = f"\t{line}"62 code = "\n".join(code_lines)63 64 # add extra indentation65 code = (66 f"try:\n{code}\n" + "except Exception as e:" + "\n\t" + "out=sys.exc_info()"67 )68 code = "out=None" + "\n" + code + "\n" + "out" # provide results in output69 70 return code71 72 def execute_code(self, code: str, state: dict):73 from smolagents.utils import parse_code_blobs74 75 code = parse_code_blobs(code)76 77 # Silencing the tool_validation error: execute_code: Name '' is undefined.78 py_executor_output = None79 py_executor_logs = None80 py_executor_is_final_answer = None81 82 py_executor_output, py_executor_logs, py_executor_is_final_answer = (83 self.python_executor(code, state)84 )85 86 return py_executor_output, py_executor_logs, py_executor_is_final_answer87 88 def execude_pdb_command(89 self, command: str, traceback90 ):91 output = None92 try:93 # Set up the pdb instance with the current frame and traceback94 frame = traceback.tb_frame95 while frame.f_back:96 frame = frame.f_back97 self.pdb_instance.reset()98 self.pdb_instance.setup(frame, traceback)99 # Execute the pdb command100 self.pdb_instance.onecmd(command)101 # Retrieve the output102 output = self.output_capture.getvalue()103 finally:104 # Clear the output capture for future use105 self.output_capture.truncate(0)106 self.output_capture.seek(0)107 108 return output109 110 def forward(self, code: str, command: str):111 import sys112 113 code = self.wrap_code_with_trycatch(code)114 115 state = {"sys": sys}116 117 # Silencing the tool_validation error: execute_code: Name '' is undefined.118 py_executor_output = None119 tb = None120 121 py_executor_output, py_executor_logs, py_executor_is_final_answer = (122 self.execute_code(code, state)123 )124 125 if py_executor_output is not None:126 try:127 exc_type, exc_value, tb = py_executor_output128 except Exception as e:129 return "No traceback found"130 pdb_result = self.execude_pdb_command(command, tb)131 return pdb_result132 133 return "No traceback found"134 135 def __init__(self, *args, **kwargs):136 self.is_initialized = False137 