Dependencies, isolation and runtimes
Where a module's code runs, and which libraries it can import.
Declaring dependencies#
dependencies:
python:
requires-python: ">=3.12"
packages: ["scikit-learn>=1.5"] # private to your module, installed into .venv
inherit: [pm4py, pandas, numpy] # reuse the platform's installed copies
isolation: in_process # in_process (default) | subprocess
execution: thread # thread (default) | worker
npm: ["d3-sankey@^0.12"] # bundled with your frontend
| Field | Meaning |
|---|---|
packages | Installed into modules/<folder>/.venv; other modules cannot see them. |
inherit | Names the platform already ships — avoids reinstalling hundreds of megabytes per module. A package may not appear in both lists. |
requires-python | A validation gate for in_process, an interpreter selector for subprocess. |
execution | In-process only: thread (fast, cooperative cancel) or worker (killable child process). |
Each environment is hashed: unchanged dependencies are skipped on boot, and for in_process modules the platform's major.minor Python version is part of the hash, so an interpreter upgrade rebuilds automatically. A private import namespace puts your .venv/site-packages first, so your versions win and other modules' packages stay invisible.
Choosing an isolation mode#
| Situation | Mode |
|---|---|
| Pure Python, finishes quickly | in_process + thread (default). |
| Long native call that ignores cancellation | in_process + execution: worker. |
| Another Python version, or a native-library conflict | isolation: subprocess. |
| A Java library | runtime: { kind: jvm }. |
| A method that needs its own server | A sidecar service. |
Subprocess modules run in a long-lived worker on their own interpreter, with every ctx call proxied over a Unix-socket JSON-RPC connection and DataFrames handed over as Parquet. Each context touch costs roughly 1–50 ms, so batch: one aggregated duckdb_fetch beats a thousand calls. Cancellation is a three-second soft window followed by SIGKILL of the process group; a crashed worker respawns with exponential backoff, and a crash loop becomes a terminal error rather than a hot loop.
JVM modules ship as one self-contained fat jar with a Main-Class and speak the same protocol, so the SDK surface is equivalent:
public static void main(String[] argv) {
MateModule.builder("my_jvm_module")
.onEventJob("precompute", "log.imported",
JobSpec.of().progress(true).cancellable(true).title("My JVM analysis"),
MyImpl::precompute)
.route("get_ranking", RouteSpec.get("/ranking"), MyImpl::getRanking)
.build()
.run(argv); // connects, handshakes, serves - never returns
}
The context offers eventLog() (with duckdbFetch running host-side and materialize() returning a Parquet path), cache(), bus(), registry(), progress(), logger(), config(), workdir(), and checkCancelled(). Build with make sdk-jvm; modules/performance_java is the reference. There is no server-side Maven or Gradle resolution — the jar must bundle everything but the JRE.
Sidecar services (a graph database, a search engine, a solver) follow a fixed contract: declared under an optional compose profile the operator starts, bound to loopback, addressed through a config → environment → default chain, probed with a health check that produces an actionable error, wiped before and after each run, and never treated as a store — results go to ctx.cache. Full rules: modules/SIDECAR_SERVICES.md.