# 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. ```bash no-run pip install dspy withruntime ``` ```python check import dspy from withruntime import Sandbox from withruntime.tools import sandbox_tools def 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: ```python check result = 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: ```python check import dspy from withruntime import Sandbox from withruntime.tools import sandbox_tools class 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](/docs/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](/docs/security#secrets-sandboxes-never-see) 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](/docs/security#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](/compare/pyodide-vs-server-sandbox) and [WebAssembly vs a microVM](/compare/webassembly-vs-microvm); evaluation at scale is in [agent evals and SWE-bench](/use-cases/agent-evals-and-swe-bench). New accounts get 50 free sandbox hours, no card: ```bash no-run npx withruntime sandbox run --trial -- python3 -c 'print(6 * 7)' ``` ## Sources - [DSPy: ReAct and tools](https://dspy.ai/current/getting-started/react-and-tools/), plain functions as tools, `max_iters` and `trajectory`, read 25 September 2026 - [dspy on PyPI](https://pypi.org/project/dspy/), 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.