| | 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 |
| |
|