dspy-monty-interpreter
DSPy CodeInterpreter implementation using Monty, a secure Python interpreter written in Rust.
The Monty team points out, "This project is still in development, and not ready for the prime time." It uses a small subset of the standard library (sys, os, typing, asyncio, re, datetime, json, math, unicodedata) and can't yet use match statements. It does support classes (as of Monty 0.0.19), with/context managers, and a sandboxed open() (file access is opt-in, see Filesystem access).
That said: Monty is fast. For many RLM use cases, Monty is my daily driver.
Installation
pip install dspy-monty-interpreter
Requires pydantic-monty>=0.0.19.
Usage
import dspy
from dspy_monty_interpreter import MontyInterpreter
interpreter = MontyInterpreter()
rlm = dspy.RLM("context -> answer", interpreter=interpreter)
result = rlm(context="What is 2 + 2?")
Standalone usage
from dspy_monty_interpreter import MontyInterpreter
interp = MontyInterpreter()
# Basic execution
interp.execute("x = 42")
interp.execute("print(x + 8)") # returns "50"
# State persists across calls
interp.execute("def double(n):\n return n * 2")
interp.execute("double(21)") # returns "42"
# With tools
def lookup(key: str) -> str:
return "some value"
interp = MontyInterpreter(tools={"lookup": lookup})
interp.execute('result = lookup(key="foo")\nprint(result)')
Filesystem access
The sandbox has no filesystem access by default. You grant access per interpreter by passing one or more MountDir objects, which map a virtual path inside the sandbox to a directory on your machine. Sandboxed code then reads and writes those paths with the normal open() and pathlib.Path APIs.
A MountDir takes a virtual path, a host path, and a mode (all keyword-only):
read-only: code can read the files but cannot write anything.read-write: code can read and write the real files on your machine.overlay: reads come from your real files, but writes are kept in memory for the duration of oneexecute()call and never touch your disk.
For a typical agent, mount the files you want the model to explore as read-only, and add an overlay directory for anything it wants to write:
import dspy
from dspy_monty_interpreter import MontyInterpreter, MountDir
interpreter = MontyInterpreter(mounts=[
MountDir(virtual_path="/data", host_path="./reports", mode="read-only"),
MountDir(virtual_path="/scratch", host_path="./scratch", mode="overlay"),
])
rlm = dspy.RLM("question -> answer", interpreter=interpreter)
result = rlm(question="Which report mentions the Q3 forecast?")
The model can read every file under ./reports through /data, and it can write freely under /scratch during each execute() call, but nothing it writes ever reaches your disk.
Overlay mode also works well as pure scratch space in standalone use:
import tempfile
from dspy_monty_interpreter import MontyInterpreter, MountDir
interp = MontyInterpreter(
mounts=MountDir(virtual_path="/data", host_path=tempfile.mkdtemp(), mode="overlay")
)
interp.execute(
"from pathlib import Path\n"
"Path('/data/notes.txt').write_text('draft')\n"
"print(Path('/data/notes.txt').read_text())"
) # returns "draft"
# The host directory is still empty.
Two things to know about overlays. First, as of Monty 0.0.19 an overlay only lives for the duration of a single execute() call. Code that writes a file must read it back in the same call. Use read-write mode when files need to survive across calls. Second, overlay writes never reach the host, so the only way to get overlay data out is from inside the sandbox. Have the code print() or SUBMIT() the results, or use read-write mode when you need real files on disk.
For a fully virtual filesystem, environment variables, or control over the clock, pass an AbstractOS implementation as the os_access parameter. See the Monty documentation for details.
Timeouts
Pass request_timeout to set a hard limit, in seconds, on each execute() call:
interp = MontyInterpreter(request_timeout=10.0)
When code exceeds the limit, execute() raises CodeInterpreterError. The session state is lost, and the next execute() starts fresh.
Parallel evaluation
A single MontyInterpreter is safe to share across threads, which is exactly what dspy.Evaluate and dspy.Parallel do when they run one RLM with num_threads. Each thread gets its own isolated REPL session, and all sessions share one pool of Monty worker processes:
interpreter = MontyInterpreter(max_processes=8)
rlm = dspy.RLM("question -> answer", interpreter=interpreter)
evaluate = dspy.Evaluate(devset=devset, num_threads=8, metric=metric)
evaluate(rlm)
The pool caps live workers at max_processes, which defaults to your CPU count. A thread holds its worker between execute() calls, so when num_threads is higher than your CPU count, set max_processes to at least num_threads. Otherwise threads at the tail of a run can wait on workers that idle threads still hold.
Why Monty?
- Fast: No WASM bootstrap. Code runs against a pool of warm
montyworker processes (as of Monty 0.0.19) - Secure: No filesystem, network, or environment access unless you grant it (see Filesystem access)
- Resilient: A crashed or timed-out worker is replaced automatically without taking down your process
- Lightweight: Pure Rust, no Deno/Pyodide dependency
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dspy_monty_interpreter-0.3.0.tar.gz.
File metadata
- Download URL: dspy_monty_interpreter-0.3.0.tar.gz
- Upload date:
- Size: 26.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2b5ca6bfb78069f54a94cfee0f0ca3f8dfda989078dbb6ab0d561e5de02c765
|
|
| MD5 |
9eef22792133ca63ba3ca122de1c8c2d
|
|
| BLAKE2b-256 |
cb9e359c58b694af2a6a6ba21671dad6004e865c2918bf76cbf0b51ca08bbf5f
|
File details
Details for the file dspy_monty_interpreter-0.3.0-py3-none-any.whl.
File metadata
- Download URL: dspy_monty_interpreter-0.3.0-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1727810e390df4705e6200380d8404c7548a7f8194708e2d209276cc52c4ea6
|
|
| MD5 |
8d0a0e1cb49c1ce67b4f3c426a9b2545
|
|
| BLAKE2b-256 |
c45eecea4d1cda66a27a504b29aa18e439d3f04a7ba0578c3cb29edbcb203519
|