Pydantic Monty: A Secure Python Interpreter for AI Agents

February 6, 2026

Pydantic has released Monty, a minimal Python interpreter written in Rust for safely executing LLM-generated code.

AI agents that generate code face a tradeoff: trust the generated code completely, or spin up containers for isolation. Containers are slow and resource-intensive. Trusting arbitrary code is risky. Monty sidesteps both by sandboxing execution at the interpreter level, blocking dangerous operations while running Python in microseconds.

How It Works

Monty provides a straightforward API:

import pydantic_monty as monty

# Simple execution
m = monty.Monty('x * y', inputs=['x', 'y'])
result = m.run(inputs={'x': 5, 'y': 3})  # Returns 15

Monty also supports external functions. Code running inside the sandbox can call out to host functions:

m = monty.Monty(
    'fetch_data(query).upper()',
    inputs=['query'],
    external_functions=['fetch_data']
)

result = m.run(
    inputs={'query': 'users'},
    external_functions={'fetch_data': lambda q: f"data for {q}"}
)
# Returns "DATA FOR USERS"

Start/Resume Pattern

Monty can pause execution when an external call is needed, rather than requiring all external functions upfront:

code = """
data = fetch_data(query)
processed = data.upper()
save_result(processed)
len(processed)
"""

m = monty.Monty(code, inputs=['query'], external_functions=['fetch_data', 'save_result'])

# Start - pauses at first external call
state = m.start(inputs={'query': 'users'})
# state.function_name == 'fetch_data'

# Resume with result - pauses at next external call
state = state.resume(return_value="hello world")
# state.function_name == 'save_result'

# Resume again - completes
state = state.resume(return_value=None)
# state.output == 11

Execution state can be serialized and restored, enabling durable agent workflows that survive restarts.

Security Model

Monty takes a deny-by-default approach. Functions like open(), __import__(), eval(), and exec() simply don’t exist. The os and sys modules are stubs that return safe values (sys.version returns “3.14.0 (Monty)”). Subprocess, file access, and environment variables are all blocked.

For cases where controlled file access is needed, Monty provides an in-memory file system abstraction:

from pydantic_monty import OSAccess, MemoryFile

mem_file = MemoryFile(path="/data.txt", content=b"Hello!")
os_access = OSAccess(files=[mem_file])

Performance

Simple operations complete in under a microsecond. Recursive functions with list comprehensions run in single-digit microseconds. Monty instances can be reused: parse once, run many times with different inputs.

Current Limitations

At version 0.0.3, Monty doesn’t yet support class definitions, match statements, context managers, generators, or most standard library modules. It covers the core Python features needed for typical LLM-generated code but isn’t a complete Python implementation.

Installation

Monty is available via pip or uv:

uv add pydantic-monty

Bindings also exist for Rust and JavaScript/TypeScript.

Pydantic AI Integration

The Pydantic team building a secure interpreter suggests deeper integration with Pydantic AI is likely coming. Monty is lightweight enough for real-time code execution in conversational AI, where container startup time would break the interaction flow.

Last updated on

Please submit corrections and feedback...