// local-first orchestration for AI agents

SANCTUM

Draw the circle. Bind the sigils. Invoke — a minimal engine for cyclic state graphs run by supersteps, built to work with the models on your machine. Pure Python standard library at the core. No cloud, no accounts, no telemetry.

$ pip install sanctum-engine
0core dependencies
~60µsper superstep
140tests, no models needed
MITlicense
The model

An agent is a cycle, not a chain

Sanctum executes a cyclic state graph — not a DAG. Every superstep runs the active Sigils (nodes) in parallel over a shared Aether (state), merges their partial deltas through per-channel reducers, then lets conditional edges decide what wakes next. Think → act → observe → think again: the loop is the agent, bounded by a recursion limit and checkpointed at every step.

Deterministic by construction: deltas apply in Sigil insertion order, so parallel runs resolve identically, every time.

from sanctum import END, Ritual

ritual = Ritual()
ritual.add_sigil("think", think)
ritual.add_sigil("act", act)
ritual.set_entry_point("think")
ritual.add_edge("think", "act")
ritual.add_conditional_edge("act",
    lambda aether: END if aether["done"] else "think")

rite = ritual.compile()          # validated, sealed
rite.invoke({"task": "..."})     # the loop unfolds
The machinery

Small surface, serious machinery

One graph builder, one executable plan, and everything an agentic runtime actually needs — nothing it doesn't.

BSP supersteps

Pregel-style lockstep: parallel Sigils, deterministic reducer order, conditional edges evaluated against the merged state.

Wait-all joins

join="all" turns a Sigil into a barrier over its predecessors — uneven branches converge correctly, across supersteps, surviving checkpoints.

Circles — subgraphs

Mount a compiled Rite as a single Sigil. A summoned agent becomes one node of a larger pipeline; its inner life stays visible as echoed events.

Scatter — map-reduce

Fan out over a runtime-sized list with bounded concurrency; results land in item order. Dynamic parallelism without touching the frontier model.

summon()

The canonical ReAct loop — oracle → spells → oracle → END — assembled entirely from public primitives. Your agent in one call.

Robust tool-calling

Built for 7–14B local models: malformed JSON repaired, unknown Spells corrected conversationally, prompted fallback when the server has no native tools.

Seals & time-travel

JSON checkpoints per superstep (memory / SQLite / Postgres); resume, interrupt() for human-in-the-loop, replay from any historic Seal.

Streaming Omens

Typed, timestamped events for every lifecycle point; combinable modes; live tokens pushed from inside a running Sigil.

Resilience policies

Per-Sigil timeout, retries with backoff and jitter, fallback Sigils reading a reserved error channel. Failure is a path, not a crash.

Wards middleware

Transform or veto any delta before it touches state: audit trails, usage tallies, redaction — applied before Seals and logs ever see the data.

Local tracing

A complete execution trace rendered to one self-contained HTML file. Zero external requests — your LangSmith is a file on disk.

Zero-dependency core

The engine is pure Python stdlib. HTTP adapters, Postgres, in-process GGUF — all optional extras, all lazily imported.

Local first

Built for the models you actually run

One OpenAI-compatible adapter covers llama-server, vLLM, LM Studio and Ollama's /v1; a native Ollama adapter and in-process GGUF loading round it out. The transcript is translated to each server's tool wire format, so local models genuinely see their tool results — and small models stop re-calling the same tool forever.

Tests never require a model: a scripted oracle makes every flow deterministic, and the integration suite against a real server is strictly opt-in.

from sanctum.oracle.openai_compat import OpenAICompatibleOracle

oracle = OpenAICompatibleOracle(
    arcana="qwen2.5-7b",
    base_url="http://127.0.0.1:8080/v1",  # llama-server
)
entity = summon(oracle, tome,
    role="You are a scryer.",
    spell_calling="auto")  # prompted fallback included
The honest table

An honest comparison

SanctumLangGraphn8n
Core dependencies0 (stdlib)langchain-core + moreNode.js platform
Local models as first-classyes — the pointpossiblevia nodes
Cyclic graphs / superstepsyes (BSP)yes (Pregel)DAG-ish
Checkpoints & time-travelyes, JSONyesexecutions log
Tool-repair for small modelsbuilt-innono
Tracing without a SaaSone HTML fileLangSmith (cloud)built-in UI

Full 13-row comparison, including where LangGraph is the better choice →

The ecosystem

The engine, the spellbook, the chamber

sanctum-engine

Execution: the graph, the state, the scheduler, the Oracles, the resilience. Everything on this page.

GitHub · PyPI

AgentGrimoire

Capability: a folder-per-Spell tool library, loadable by convention with Tome.load_from_directory(). Either side evolves without touching the other.

GitHub

Sanctum Studio

The chamber, made visible: a local visual builder and dashboard — draw rites on a canvas, watch every superstep live, pause for human approval, time-travel through any run. No cloud, no build step.

GitHub