1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
10 small wins to finish your pathNext lesson →
A code tool for an LLM, tested without a sandbox
An agent's code tool runs code and describes the Execution. That description, with errors and output limits, is testable with recorded sandbox messages.
from e2b_code_interpreter.models import Execution
MAX_CHARS = 2000
def describe(execution: Execution) -> str:
"""The text a model reads back after its code ran."""
parts = []
if execution.logs.stdout:
parts.append("stdout:\n" + "".join(execution.logs.stdout))
if execution.logs.stderr:
parts.append("stderr:\n" + "".join(execution.logs.stderr))
if execution.error:
parts.append(f"error: {execution.error.name}: {execution.error.value}")
elif execution.text is not None:
parts.append("result: " + execution.text)
return "\n".join(parts)[:MAX_CHARS] or "The code ran and printed nothing."
def run_python(sandbox, code: str) -> str:
"""The tool: run model-written code in the sandbox and describe what happened."""
return describe(sandbox.run_code(code, timeout=30))describeturns anExecutioninto short text: output, the error if any, otherwise the result.- Errors are described, not raised, so the model can read them and fix its code.
MAX_CHARSkeeps a runawayprintfrom filling the model's context.run_pythonsets a 30 second limit on every call.
import json
from e2b_code_interpreter.models import Execution, parse_output
class RecordedSandbox:
"""Answers run_code from recorded sandbox messages.
It runs nothing and isolates nothing: it replays what a sandbox sent, so code
that handles an Execution can be tested without an E2B key.
"""
def __init__(self, messages):
self.messages = messages
self.code = []
def run_code(self, code, **kwargs):
self.code.append(code)
execution = Execution()
for message in self.messages:
parse_output(execution, json.dumps(message))
return executionRecordedSandbox has the one method the tool uses. It replays messages through the SDK's real parse_output, so the tool gets the same Execution objects a sandbox would produce. It is a test double, not a sandbox: it executes nothing.
from recorded import RecordedSandbox
from tool import MAX_CHARS, run_python
def test_result_and_output_are_reported():
sandbox = RecordedSandbox([
{"type": "stdout", "text": "rows: 3\n", "timestamp": 1},
{"type": "result", "text": "120.5", "is_main_result": True},
])
assert run_python(sandbox, "df.total.sum()") == "stdout:\nrows: 3\n\nresult: 120.5"
assert sandbox.code == ["df.total.sum()"]
def test_errors_are_reported_not_raised():
sandbox = RecordedSandbox([{"type": "error", "name": "KeyError", "value": "'total'", "traceback": "..."}])
assert run_python(sandbox, "df['total']") == "error: KeyError: 'total'"
def test_huge_output_is_cut():
sandbox = RecordedSandbox([{"type": "stdout", "text": "x" * 50_000, "timestamp": 1}])
assert len(run_python(sandbox, "print('x' * 50_000)")) == MAX_CHARSpytest -qThe tests fix three behaviours a model depends on: output and result are reported together, an error is described rather than raised, and huge output is cut. None of them needed a key.
Try it yourself
- Add a test for a cell that prints nothing and returns nothing.
- Report
stderrafterstdoutand test it. - Add the traceback's last line to the error description.
Slow is fine. Stopping is the only problem.