Runtime

DSPy code execution: give ReAct a secure Linux sandbox

Pass Runtime's four sandbox functions to dspy.ReAct as tools, and the program's shell commands run in a Firecracker microVM.

On Runtime a DSPy program can run real software, not only pure Python. DSPy's own PythonInterpreter runs code in Pyodide, a WebAssembly build of Python, under Deno on your machine. That keeps code off your filesystem, but it offers no shell, no compiler and only the packages built for Pyodide. A Runtime sandbox is an Ubuntu 24.04 microVM with its own kernel, Python 3.12, Node.js 24, gcc, pip and sudo. It starts in 351 ms at the median (24 September 2026), which matters when an optimizer runs a program hundreds of times. dspy 3.3.1 was the current PyPI release on 25 September 2026.

Where DSPy runs code

Module Default interpreter What the code can use
dspy.ProgramOfThought PythonInterpreter (Deno and Pyodide) Pyodide's Python and its packages
dspy.CodeAct PythonInterpreter Pyodide's Python, plus the tools you pass
dspy.RLM PythonInterpreter Pyodide's Python, plus the tools you pass
dspy.ReAct with Runtime tools A Runtime microVM A bash shell, any package, files, the network you allow

The PythonInterpreter docstring in dspy 3.3.1 describes it as a "local interpreter for secure Python execution using Deno and Pyodide" with "no access to the host filesystem, network, or environment by default", and it needs Deno installed (pip install "dspy[deno]"). The three code modules take an interpreter_factory argument for other interpreters.

ReAct with a sandbox

DSPy tools are plain Python functions with type hints and docstrings; ReAct takes them in a list and converts them itself. sandbox_tools(sbx) returns four such functions.

Terminalpip install dspy withruntime
Pythonimport dspyfrom withruntime import Sandboxfrom withruntime.tools import sandbox_toolsdef solve(task: str) -> dspy.Prediction:    with Sandbox.create(        network={"internet": True, "allow": ["pypi.org", "*.pythonhosted.org"]},        timeout_seconds=900,        on_lease_end="stop",    ) as sbx:        agent = dspy.ReAct("task -> answer", tools=sandbox_tools(sbx), max_iters=12)        return agent(task=task)

Configure the language model first, as in any DSPy program, with dspy.configure(lm=dspy.LM(...)). The result carries answer, reasoning and a trajectory that records every thought, tool call and observation, so you can see which commands ran:

Pythonresult = solve("Install sympy, then factor x**6 - 1 and print the factors.")for step, value in result.trajectory.items():    print(step, value)print(result.answer)

This loop ran on 25 September 2026 with dspy 3.3.1 and dspy.utils.DummyLM scripting the model: ReAct called runtime_exec, recorded the sandbox's exit code and stdout as the observation, and finished with the answer.

Optimizing a program that runs code

Optimizers such as MIPROv2 and BootstrapFewShot call your program once per training example and candidate, so give each call its own sandbox inside the module. Every trial then starts from the same clean machine, and nothing one trial installs can leak into the next score:

Pythonimport dspyfrom withruntime import Sandboxfrom withruntime.tools import sandbox_toolsclass Solver(dspy.Module):    def __init__(self):        super().__init__()        self.signature = dspy.Signature("task -> answer")    def forward(self, task: str) -> dspy.Prediction:        with Sandbox.create(network={"internet": False}, timeout_seconds=300, on_lease_end="stop") as sbx:            react = dspy.ReAct(self.signature, tools=sandbox_tools(sbx), max_iters=8)            return react(task=task)

What that costs: Runtime bills the CPU the commands use, at $0.025 per vCPU-hour, plus $0.0075 per GiB-hour of memory. A thousand one-minute runs at 2 vCPU and 4 GiB that each use 20 CPU-seconds cost $0.64, and a sandbox idling while the model thinks costs $0.03125 an hour (pricing). A paid account runs 100 sandboxes at once to start, so parallel evaluation threads each get a machine.

Keep keys and budgets safe

  • The LM key stays with DSPy. DSPy calls the model from your process. The sandbox receives only the commands ReAct chooses, never the key.
  • No network while scoring. network={"internet": False} gives the sandbox no network card at all, so model-written code cannot send your training data anywhere.
  • Tokens the code needs are stored as Runtime secrets and seen inside the sandbox only as placeholders.
  • A long optimization run cannot overspend. Put a daily spending limit on the key the optimizer uses. When a create would pass it, the create fails with spending_limit_reached, and nothing is charged (read-only keys and daily limits).

Pyodide or a microVM?

Keep PythonInterpreter when the code is short, pure Python and needs nothing outside Pyodide. Use Runtime when a step needs a package Pyodide lacks, a command-line tool, a test suite, several languages, or a file that must survive from one step to the next. The trade-off is laid out in Pyodide vs a server sandbox and WebAssembly vs a microVM; evaluation at scale is in agent evals and SWE-bench. New accounts get 50 free sandbox hours, no card:

Terminalnpx withruntime sandbox run --trial -- python3 -c 'print(6 * 7)'

Sources

  • DSPy: ReAct and tools, plain functions as tools, max_iters and trajectory, read 25 September 2026
  • dspy on PyPI, version 3.3.1; the PythonInterpreter docstring and the interpreter_factory defaults of ProgramOfThought, CodeAct and RLM were read from that release's source on 25 September 2026

Facts on this page were checked on 25 September 2026.