Expand description
External-language function runtime for Improv (Phase 6).
This crate is the analogue of the engine’s in-process scalar registry
(improv_engine’s scalar_arity/scalar_func) for NON-builtin functions:
it evaluates a registered external function on typed
[improv_core_model::Value] arguments, deterministically and in a
subprocess sandbox. The engine calls it host-side, outside the dataflow
(improv_engine::external::refresh_external_measure), so external calls
never run on the incremental hot path.
§Invariant (see AGENT_MASTER_STEERING.md §7)
External functions must be pure, return a typed value, and declare
dimensionality, so they behave as ordinary operators and keep the engine
deterministic. This runtime is deterministic given the same inputs + body;
purity of the user’s function body is the user’s contract (the engine treats
external calls as pure). Dimensionality is declared on the descriptor
(arg_types / return_type); higher-arity dimension broadcasting is the
engine’s job at the call site, not this runtime’s.
§Language runtimes
eval dispatches on f.language to a per-language runner:
- Python / R / Julia / Pure — subprocess runners (shared plumbing in
the
runnermodule): the interpreter reads a generated program on stdin and prints one{"ok":..}/{"error":..}JSON envelope on stdout. A wall-clock timeout kills a runaway child. If the interpreter binary is absent,evalreturnsExtFnError::LanguageUnavailable— it never panics. - Wasm — an in-process
wasmiinterpreter (no subprocess); numeric f64 ABI (see thewasmmodule).
§Sandboxing and its limits
Python is invoked as python3 -I -S - (isolated, no site, program on stdin);
R as Rscript --vanilla -; Julia as julia --startup-file=no -; Pure as
pure -q. In every case the program arrives on stdin, so the function
body is never interpolated into a shell command line (no shell is spawned).
A wall-clock timeout (default 5s) kills a runaway child.
On top of that, every subprocess runner is spawned through the sandbox
module’s OS sandbox (see its docs): on Linux, a bwrap (bubblewrap)
filesystem/network sandbox plus setrlimit ceilings (CPU time, address
space, open files, process count). It fails closed: without bwrap
(always the case on macOS and Windows) a Restricted body is refused with
ExtFnError::Sandbox unless the operator sets
IMPROV_EXTFN_ALLOW_UNSANDBOXED=1, which falls back to rlimits only. The
Wasm runtime runs in-process via wasmi, which has no syscall access at
all by construction, independent of any OS mechanism.
The host, never the model, picks the sandbox strictness: every body runs
SandboxPolicy::Restricted unless the operator sets
IMPROV_EXTFN_TRUSTED=1 (see sandbox::policy_for). ExternalFn::pure
is model data and only affects caching.
Structs§
- External
Fn - A registered external function: everything needed to type-check a call and
(via the
improv_extfnruntime) invoke it deterministically. - Registry
- Name -> descriptor. The engine will resolve a
FuncId/name to a descriptor here for non-builtin calls.
Enums§
- ExtFn
Error - Errors from resolving or evaluating an external function. The engine will
convert these to a
Value::Errorat the call site (seeerror_value); this API returnsResultso the caller decides. - Language
- The runtime language an external function targets. Each variant has a runner
in the
improv_extfncrate (subprocess for the scripting languages, an in-processwasmiinterpreter forWasm). - Sandbox
Policy - How strictly a subprocess runner should be confined.
Functions§
- error_
value - Convenience: build a
Value::Error(used by callers mapping ExtFnError). - eval
- Evaluate one descriptor on typed arguments with an explicit timeout.
- value_
to_ json - Encode one
Valueas JSON for stdin.