Skip to main content

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 one execute() 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 monty worker 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

dspy_monty_interpreter-0.3.0.tar.gz (26.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dspy_monty_interpreter-0.3.0-py3-none-any.whl (8.9 kB view details)

Uploaded Python 3

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

Hashes for dspy_monty_interpreter-0.3.0.tar.gz
Algorithm Hash digest
SHA256 d2b5ca6bfb78069f54a94cfee0f0ca3f8dfda989078dbb6ab0d561e5de02c765
MD5 9eef22792133ca63ba3ca122de1c8c2d
BLAKE2b-256 cb9e359c58b694af2a6a6ba21671dad6004e865c2918bf76cbf0b51ca08bbf5f

See more details on using hashes here.

File details

Details for the file dspy_monty_interpreter-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for dspy_monty_interpreter-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d1727810e390df4705e6200380d8404c7548a7f8194708e2d209276cc52c4ea6
MD5 8d0a0e1cb49c1ce67b4f3c426a9b2545
BLAKE2b-256 c45eecea4d1cda66a27a504b29aa18e439d3f04a7ba0578c3cb29edbcb203519

See more details on using hashes here.

Release history Release notifications | RSS feed

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

This release

0.3.0 This release

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page