Skip to content

moderately-ai/interpretthis

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

545 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

interpretthis

Sandboxed Python AST interpreter for Rust, Python, and TypeScript.

cargo add interpretthis     # crates.io
pip install interpretthis   # PyPI
npm install interpretthis   # npm

Runs untrusted or LLM-generated Python by evaluating a rustpython-parser AST under resource limits and an allowlisted language surface. It is not CPython embedded via the C API: there is no filesystem, network, or process access unless the host injects those as tools.

Why

You need to execute model-written Python for structured work (transforms, scoring, tool-orchestrated agents) without giving the model a real Python process. interpretthis is the evaluator: host code owns tools, limits, and what happens with the result.

Quick start

use std::collections::HashMap;

use interpretthis::{
    Interpreter, InterpreterConfig, InterpreterDeps, KwargsExt, ToolDefinition, Tools, Value,
};

#[tokio::main]
async fn main() {
    let tools = Tools::new().with(
        "double",
        ToolDefinition::from_fn(|kwargs| async move {
            let n = kwargs.require_int("n")?;
            Ok(Value::Int(n * 2))
        }),
    );

    let interp = Interpreter::new(
        InterpreterDeps { tools },
        InterpreterConfig::default(),
    );

    // Per-call tools merge with registered tools (per-call wins on name clash).
    let resp = interp
        .execute(
            "result = double(n=x)\nprint(result)",
            &Tools::new(),
            HashMap::from([("x".to_string(), Value::Int(42))]),
        )
        .await;

    match resp.result() {
        Ok(()) => println!("{}", resp.stdout),
        Err(e) => eprintln!("{e}"),
    }
}

Requires Rust 1.88+ and Tokio.

From Python

from interpretthis import Interpreter

interp = Interpreter(tools={"double": lambda n: n * 2})
result = interp.execute("answer = double(n=x)\nprint(answer)", {"x": 21})

result.stdout                  # '42\n'
interp.get_variable("answer")  # 42

Tools may be async def; from async code use await interp.execute_async(...), and tool coroutines run on your own event loop. See crates/interpretthis-python/README.md.

From TypeScript

import { Interpreter } from 'interpretthis'

const interp = new Interpreter({ double: ({ n }) => n * 2 })
const result = await interp.execute('answer = double(n=x)\nprint(answer)', { x: 21 })

result.stdout                    // '42\n'
interp.getVariable('answer')     // 42

Tools may be async. See crates/interpretthis-node/README.md.

In every language, a failing run is data, not an exception: you get stdout and the error, because a script that prints and then breaks has told you something useful in both — and that pair is what you feed back to a model.

Limits (honest)

  • Language subset — large, but not full CPython. See CONFORMANCE.md.
  • Sandbox — dangerous names/attrs and non-allowlisted imports are rejected. See THREAT_MODEL.md.
  • Tools are trusted — a host tool with side effects extends the trust boundary. Tool failures become a generic Python Exception (catchable); uncaught they fail the host execute call.
  • State export is a versioned byte blob (4-byte version + JSON) for host-owned resume; signing/encryption is the host’s job.

Docs

docs.rs/interpretthis API reference (after crates.io publish)
CONFORMANCE.md CPython divergences
STATUS.md Parity track status
THREAT_MODEL.md Security boundary
CHANGELOG.md Release notes
CONTRIBUTING.md Contribution policy (outreach first)
RELEASING.md Tag and crates.io publish
MODULE_TEMPLATE.md Adding a stdlib module
SECURITY.md Vulnerability reporting

Development

cargo nextest run --all-targets   # preferred; cargo test also works
cargo clippy --all-targets -- -D warnings
scripts/license-headers.sh check

Parity tests compare against host python3.12 when available (crates/interpretthis/tests/integration/parity_corpus/).

License

Licensed under either of

at your option.

Contribution policy

We do not accept unsolicited outside contributions. If you are interested in working on this project, email opensource@moderately.ai and explain why — see CONTRIBUTING.md.

If a contribution is later accepted, unless you explicitly state otherwise it is dual-licensed under MIT OR Apache-2.0 as above, without additional terms.

About

Sandboxed Python AST interpreter for untrusted and LLM-generated code

Topics

Resources

License

Apache-2.0 and 2 other licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Unknown
license-header.txt

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages