piotrekgrl's picture
Upload tool
85bccd0 verified
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)
# add extra indentation
code = (
f"try:\n{code}\n" + "except Exception as e:" + "\n\t" + "out=sys.exc_info()"
)
code = "out=None" + "\n" + code + "\n" + "out" # provide results in output
return code
def execute_code(self, code: str, state: dict):
from smolagents.utils import parse_code_blobs
code = parse_code_blobs(code)
# Silencing the tool_validation error: execute_code: Name '' is undefined.
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:
# Set up the pdb instance with the current frame and traceback
frame = traceback.tb_frame
while frame.f_back:
frame = frame.f_back
self.pdb_instance.reset()
self.pdb_instance.setup(frame, traceback)
# Execute the pdb command
self.pdb_instance.onecmd(command)
# Retrieve the output
output = self.output_capture.getvalue()
finally:
# Clear the output capture for future use
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}
# Silencing the tool_validation error: execute_code: Name '' is undefined.
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