sanctum-engine 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. sanctum_engine-0.2.0/.github/workflows/ci.yml +31 -0
  2. sanctum_engine-0.2.0/.github/workflows/docs.yml +48 -0
  3. sanctum_engine-0.2.0/.github/workflows/release.yml +58 -0
  4. sanctum_engine-0.2.0/.gitignore +12 -0
  5. sanctum_engine-0.2.0/CHANGELOG.md +82 -0
  6. sanctum_engine-0.2.0/CONTRIBUTING.md +164 -0
  7. sanctum_engine-0.2.0/LICENSE +21 -0
  8. sanctum_engine-0.2.0/PKG-INFO +175 -0
  9. sanctum_engine-0.2.0/README.md +128 -0
  10. sanctum_engine-0.2.0/benchmarks/superstep_overhead.py +149 -0
  11. sanctum_engine-0.2.0/docs/api.md +61 -0
  12. sanctum_engine-0.2.0/docs/architecture.md +253 -0
  13. sanctum_engine-0.2.0/docs/assets/trace-viewer.svg +50 -0
  14. sanctum_engine-0.2.0/docs/comparison.md +36 -0
  15. sanctum_engine-0.2.0/docs/concepts/aether.md +41 -0
  16. sanctum_engine-0.2.0/docs/concepts/codex.md +35 -0
  17. sanctum_engine-0.2.0/docs/concepts/omens.md +43 -0
  18. sanctum_engine-0.2.0/docs/concepts/oracles.md +38 -0
  19. sanctum_engine-0.2.0/docs/concepts/policies.md +51 -0
  20. sanctum_engine-0.2.0/docs/concepts/ritual.md +36 -0
  21. sanctum_engine-0.2.0/docs/concepts/sigils-and-edges.md +39 -0
  22. sanctum_engine-0.2.0/docs/concepts/spells.md +51 -0
  23. sanctum_engine-0.2.0/docs/concepts/wards.md +36 -0
  24. sanctum_engine-0.2.0/docs/getting-started.md +71 -0
  25. sanctum_engine-0.2.0/docs/guides/human-in-the-loop.md +54 -0
  26. sanctum_engine-0.2.0/docs/guides/robust-tool-calling.md +53 -0
  27. sanctum_engine-0.2.0/docs/guides/time-travel.md +43 -0
  28. sanctum_engine-0.2.0/docs/guides/tracing.md +49 -0
  29. sanctum_engine-0.2.0/docs/index.md +33 -0
  30. sanctum_engine-0.2.0/examples/human_in_the_loop/main.py +97 -0
  31. sanctum_engine-0.2.0/examples/quickstart_ollama.py +76 -0
  32. sanctum_engine-0.2.0/examples/research_ritual/main.py +105 -0
  33. sanctum_engine-0.2.0/examples/resilient_pipeline/main.py +104 -0
  34. sanctum_engine-0.2.0/examples/sse_flask.py +97 -0
  35. sanctum_engine-0.2.0/mkdocs.yml +51 -0
  36. sanctum_engine-0.2.0/pyproject.toml +109 -0
  37. sanctum_engine-0.2.0/src/sanctum/__init__.py +66 -0
  38. sanctum_engine-0.2.0/src/sanctum/aether/__init__.py +23 -0
  39. sanctum_engine-0.2.0/src/sanctum/aether/core.py +166 -0
  40. sanctum_engine-0.2.0/src/sanctum/aether/errors.py +13 -0
  41. sanctum_engine-0.2.0/src/sanctum/aether/reducers.py +43 -0
  42. sanctum_engine-0.2.0/src/sanctum/codex/__init__.py +21 -0
  43. sanctum_engine-0.2.0/src/sanctum/codex/core.py +60 -0
  44. sanctum_engine-0.2.0/src/sanctum/codex/errors.py +14 -0
  45. sanctum_engine-0.2.0/src/sanctum/codex/memory.py +33 -0
  46. sanctum_engine-0.2.0/src/sanctum/codex/postgres.py +164 -0
  47. sanctum_engine-0.2.0/src/sanctum/codex/sqlite.py +126 -0
  48. sanctum_engine-0.2.0/src/sanctum/grimoire/__init__.py +25 -0
  49. sanctum_engine-0.2.0/src/sanctum/grimoire/core.py +290 -0
  50. sanctum_engine-0.2.0/src/sanctum/grimoire/errors.py +35 -0
  51. sanctum_engine-0.2.0/src/sanctum/grimoire/repair.py +117 -0
  52. sanctum_engine-0.2.0/src/sanctum/grimoire/summon.py +211 -0
  53. sanctum_engine-0.2.0/src/sanctum/omens/__init__.py +50 -0
  54. sanctum_engine-0.2.0/src/sanctum/omens/events.py +218 -0
  55. sanctum_engine-0.2.0/src/sanctum/omens/tracing.py +455 -0
  56. sanctum_engine-0.2.0/src/sanctum/oracle/__init__.py +44 -0
  57. sanctum_engine-0.2.0/src/sanctum/oracle/_shared.py +105 -0
  58. sanctum_engine-0.2.0/src/sanctum/oracle/core.py +87 -0
  59. sanctum_engine-0.2.0/src/sanctum/oracle/errors.py +39 -0
  60. sanctum_engine-0.2.0/src/sanctum/oracle/llamacpp.py +154 -0
  61. sanctum_engine-0.2.0/src/sanctum/oracle/ollama.py +215 -0
  62. sanctum_engine-0.2.0/src/sanctum/oracle/openai_compat.py +227 -0
  63. sanctum_engine-0.2.0/src/sanctum/oracle/robust.py +239 -0
  64. sanctum_engine-0.2.0/src/sanctum/oracle/scripted.py +68 -0
  65. sanctum_engine-0.2.0/src/sanctum/oracle/transformers.py +89 -0
  66. sanctum_engine-0.2.0/src/sanctum/ritual/__init__.py +42 -0
  67. sanctum_engine-0.2.0/src/sanctum/ritual/constants.py +14 -0
  68. sanctum_engine-0.2.0/src/sanctum/ritual/core.py +624 -0
  69. sanctum_engine-0.2.0/src/sanctum/ritual/errors.py +58 -0
  70. sanctum_engine-0.2.0/src/sanctum/ritual/interrupt.py +42 -0
  71. sanctum_engine-0.2.0/src/sanctum/ritual/policies.py +80 -0
  72. sanctum_engine-0.2.0/src/sanctum/ritual/scheduler.py +573 -0
  73. sanctum_engine-0.2.0/src/sanctum/trace.py +41 -0
  74. sanctum_engine-0.2.0/src/sanctum/wards/__init__.py +24 -0
  75. sanctum_engine-0.2.0/src/sanctum/wards/audit.py +39 -0
  76. sanctum_engine-0.2.0/src/sanctum/wards/core.py +62 -0
  77. sanctum_engine-0.2.0/src/sanctum/wards/errors.py +14 -0
  78. sanctum_engine-0.2.0/src/sanctum/wards/redact.py +53 -0
  79. sanctum_engine-0.2.0/src/sanctum/wards/usage.py +63 -0
  80. sanctum_engine-0.2.0/tests/__init__.py +0 -0
  81. sanctum_engine-0.2.0/tests/aether/__init__.py +0 -0
  82. sanctum_engine-0.2.0/tests/aether/test_reducers.py +129 -0
  83. sanctum_engine-0.2.0/tests/codex/__init__.py +0 -0
  84. sanctum_engine-0.2.0/tests/codex/test_codex.py +168 -0
  85. sanctum_engine-0.2.0/tests/fixtures/ollama_chat_tools.json +26 -0
  86. sanctum_engine-0.2.0/tests/fixtures/ollama_stream.ndjson +4 -0
  87. sanctum_engine-0.2.0/tests/fixtures/openai_chat_completion_malformed_tool.json +26 -0
  88. sanctum_engine-0.2.0/tests/fixtures/openai_chat_completion_text.json +22 -0
  89. sanctum_engine-0.2.0/tests/fixtures/openai_chat_completion_tools.json +32 -0
  90. sanctum_engine-0.2.0/tests/fixtures/openai_stream.sse +12 -0
  91. sanctum_engine-0.2.0/tests/grimoire/__init__.py +0 -0
  92. sanctum_engine-0.2.0/tests/grimoire/fixtures/agentgrimoire/system/shout/spell.json +5 -0
  93. sanctum_engine-0.2.0/tests/grimoire/fixtures/agentgrimoire/system/shout/spell.py +9 -0
  94. sanctum_engine-0.2.0/tests/grimoire/fixtures/agentgrimoire/text/word_count/spell.json +12 -0
  95. sanctum_engine-0.2.0/tests/grimoire/fixtures/agentgrimoire/text/word_count/spell.py +6 -0
  96. sanctum_engine-0.2.0/tests/grimoire/test_robust.py +300 -0
  97. sanctum_engine-0.2.0/tests/grimoire/test_summon.py +144 -0
  98. sanctum_engine-0.2.0/tests/grimoire/test_tome.py +125 -0
  99. sanctum_engine-0.2.0/tests/omens/__init__.py +0 -0
  100. sanctum_engine-0.2.0/tests/omens/test_astream.py +133 -0
  101. sanctum_engine-0.2.0/tests/omens/test_tracing.py +115 -0
  102. sanctum_engine-0.2.0/tests/oracle/__init__.py +0 -0
  103. sanctum_engine-0.2.0/tests/oracle/test_integration_ollama.py +44 -0
  104. sanctum_engine-0.2.0/tests/oracle/test_integration_openai_compat.py +109 -0
  105. sanctum_engine-0.2.0/tests/oracle/test_llamacpp.py +24 -0
  106. sanctum_engine-0.2.0/tests/oracle/test_ollama.py +101 -0
  107. sanctum_engine-0.2.0/tests/oracle/test_openai_compat.py +192 -0
  108. sanctum_engine-0.2.0/tests/oracle/test_scripted.py +29 -0
  109. sanctum_engine-0.2.0/tests/ritual/__init__.py +0 -0
  110. sanctum_engine-0.2.0/tests/ritual/test_conditional.py +121 -0
  111. sanctum_engine-0.2.0/tests/ritual/test_core.py +105 -0
  112. sanctum_engine-0.2.0/tests/ritual/test_policies.py +192 -0
  113. sanctum_engine-0.2.0/tests/ritual/test_scheduler.py +107 -0
  114. sanctum_engine-0.2.0/tests/ritual/test_validation.py +140 -0
  115. sanctum_engine-0.2.0/tests/test_sanctum.py +30 -0
  116. sanctum_engine-0.2.0/tests/wards/__init__.py +0 -0
  117. sanctum_engine-0.2.0/tests/wards/test_wards.py +194 -0
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+
22
+ - name: Install (editable, with dev extras)
23
+ run: python -m pip install -e ".[dev]"
24
+
25
+ - name: Lint
26
+ run: ruff check .
27
+
28
+ # Integration tests need a live Ollama server and are opt-in
29
+ # (SANCTUM_TEST_OLLAMA_URL); see CONTRIBUTING.md.
30
+ - name: Tests with coverage (integration excluded)
31
+ run: pytest -m "not integration" --cov=sanctum --cov-report=term
@@ -0,0 +1,48 @@
1
+ # Build the mkdocs-material site and publish it to GitHub Pages.
2
+ # One-time setup: repository Settings -> Pages -> Source: "GitHub Actions".
3
+
4
+ name: Docs
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+ pages: write
14
+ id-token: write
15
+
16
+ concurrency:
17
+ group: pages
18
+ cancel-in-progress: true
19
+
20
+ jobs:
21
+ build:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+
26
+ - uses: actions/setup-python@v5
27
+ with:
28
+ python-version: "3.12"
29
+
30
+ - name: Install package with docs extras
31
+ run: python -m pip install -e ".[docs]"
32
+
33
+ - name: Build site
34
+ run: mkdocs build
35
+
36
+ - uses: actions/upload-pages-artifact@v3
37
+ with:
38
+ path: site/
39
+
40
+ deploy:
41
+ needs: build
42
+ runs-on: ubuntu-latest
43
+ environment:
44
+ name: github-pages
45
+ url: ${{ steps.deployment.outputs.page_url }}
46
+ steps:
47
+ - id: deployment
48
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,58 @@
1
+ # Release to PyPI on version tags (v0.2.0, v1.0.0, ...).
2
+ #
3
+ # Uses PyPI *trusted publishing* — no API tokens stored in the repo.
4
+ # One-time manual setup required:
5
+ #
6
+ # 1. On PyPI (pypi.org), open the `sanctum-engine` project (or use
7
+ # "Add a new pending publisher" before the first release) and add a
8
+ # trusted publisher with:
9
+ # - Owner: zquintero246
10
+ # - Repository: sanctum-engine
11
+ # - Workflow: release.yml
12
+ # - Environment: pypi
13
+ # 2. On GitHub, create an environment named `pypi`
14
+ # (Settings -> Environments); optionally add required reviewers so a
15
+ # human approves every publish.
16
+ #
17
+ # After that: `git tag v0.2.0 && git push --tags` releases.
18
+
19
+ name: Release
20
+
21
+ on:
22
+ push:
23
+ tags: ["v*"]
24
+
25
+ jobs:
26
+ build:
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+
31
+ - uses: actions/setup-python@v5
32
+ with:
33
+ python-version: "3.12"
34
+
35
+ - name: Build sdist and wheel
36
+ run: |
37
+ python -m pip install build
38
+ python -m build
39
+
40
+ - uses: actions/upload-artifact@v4
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+
45
+ publish:
46
+ needs: build
47
+ runs-on: ubuntu-latest
48
+ environment: pypi
49
+ permissions:
50
+ id-token: write # required for PyPI trusted publishing
51
+ steps:
52
+ - uses: actions/download-artifact@v4
53
+ with:
54
+ name: dist
55
+ path: dist/
56
+
57
+ - name: Publish to PyPI (trusted publishing)
58
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv*/
4
+ .coverage
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ site/
11
+ *.sanctum-trace.json
12
+ *.sanctum-trace.html
@@ -0,0 +1,82 @@
1
+ # Changelog
2
+
3
+ All notable changes to `sanctum-engine` are documented here. The format
4
+ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
+ project adheres to [Semantic Versioning](https://semver.org/) (see
6
+ CONTRIBUTING.md for what counts as a breaking change pre-1.0).
7
+
8
+ ## [0.2.0] — 2026-07-14
9
+
10
+ ### Added
11
+ - Production Oracle adapters, all optional (the core stays stdlib-only):
12
+ - `OpenAICompatibleOracle` (`[openai-compat]` extra): any
13
+ `/v1/chat/completions` server — Ollama `/v1`, llama-server, vLLM,
14
+ LM Studio — with OpenAI-format tools and SSE streaming.
15
+ - `LlamaCppOracle` (`[llamacpp]` extra): in-process GGUF inference, the
16
+ serverless option; grammar-backed tool calling where the chat format
17
+ supports it.
18
+ - `OracleError` family (`OracleConnectionError`, `OracleTimeoutError`,
19
+ `OracleResponseError`) with actionable messages.
20
+ - Robust spell-calling for local 7-14B models:
21
+ - `PromptedSpellCalling` wrapper (schemas in the system prompt, calls
22
+ parsed from delimited blocks) with `"auto"` fallback when the
23
+ endpoint rejects tools.
24
+ - Repair layer applied to every spell call: tolerant JSON extraction
25
+ (fenced blocks, unbalanced braces, single quotes), correction
26
+ messages back to the Oracle for unknown Spells / invalid arguments,
27
+ bounded by `max_repair_rounds` before `SpellCallParseError`.
28
+ - `SpellCallRepaired` / `SpellCallRejected` Omens.
29
+ - Resilience policies: `SigilPolicy` (per-attempt timeout, retries with
30
+ exponential backoff and jitter, selective `retry_on`, `on_error`
31
+ fallback Sigil), `SigilTimeoutError`, `SigilRetried` Omen, and the
32
+ reserved `__errors__` Conduit.
33
+ - Wards middleware: `Ward` hooks (`before_sigil`, `after_sigil`,
34
+ `on_omen`, `on_compile`), `WardRejection` + `DeltaRejected` Omen, and
35
+ built-ins `AuditWard` (JSONL trail), `UsageWard` (token/call tally),
36
+ `RedactWard` (pattern masking before Seals and logs).
37
+ - Local-first tracing: `TraceRecorder` (full `.sanctum-trace.json` per
38
+ Invocation), `render_trace()` self-contained HTML viewer, and the
39
+ `python -m sanctum.trace render` CLI; tracing overhead case added to
40
+ the benchmark.
41
+ - Two-level test strategy: unit tests replay recorded fixtures through
42
+ `httpx.MockTransport`; opt-in integration tests against a live Ollama
43
+ (`SANCTUM_TEST_OLLAMA_URL`, `pytest -m integration`).
44
+ - Packaging: extras `openai-compat`, `ollama`, `llamacpp`, `all`;
45
+ CONTRIBUTING.md; MIT LICENSE; CI and trusted-publishing release
46
+ workflows.
47
+
48
+ ### Changed
49
+ - `OllamaOracle` rewritten on the native `/api/chat` endpoint via httpx
50
+ (`[ollama]` extra; previously stdlib urllib): native tool calling,
51
+ `keep_alive`, and `options` (temperature, num_ctx, ...).
52
+ - `summon()` gained `spell_calling` ("native" / "prompted" / "auto"),
53
+ `max_repair_rounds`, and `wards`; Oracle `usage` counters are attached
54
+ to assistant messages.
55
+ - The `all` extra excludes `llama-cpp-python` (needs a C toolchain);
56
+ install `[llamacpp]` explicitly for the in-process backend.
57
+
58
+ ## [0.1.0] — 2026-07-14
59
+
60
+ ### Added
61
+ - Cyclic state-graph engine: `Ritual` builder and `Rite` executable plan,
62
+ compile-time validation, static fan-out edges, conditional edges with
63
+ `path_map`, cycles bounded by `recursion_limit`.
64
+ - BSP superstep scheduler: concurrent frontier execution
65
+ (`asyncio.TaskGroup`), deterministic delta merging in Sigil insertion
66
+ order, fan-in "any" semantics, `SigilExecutionError` with sibling
67
+ cancellation.
68
+ - State system: `AetherSchema` / `Conduit` with reducers (`overwrite`,
69
+ `append`, `add`, `merge_dict`, custom), `Annotated` sugar, runtime
70
+ schema validation naming the offending Sigil.
71
+ - Persistence: `Seal` checkpoints per superstep, `Codex` stores
72
+ (`MemoryCodex`, `SqliteCodex`, optional `PostgresCodex`), resumption,
73
+ `interrupt()` human-in-the-loop, and time-travel from any Seal.
74
+ - Streaming: `astream` with combinable modes (`updates`, `values`,
75
+ `omens`, `tokens`), typed timestamped Omens, Sigil `writer` injection
76
+ for live tokens, Flask/SSE example.
77
+ - LLM layer: abstract `Oracle` + deterministic `ScriptedOracle`;
78
+ Grimoire: `Spell` / `@spell` (schema from type hints), `Tome` with the
79
+ AgentGrimoire directory convention; `summon()` ReAct loop built on the
80
+ public API.
81
+ - Docs (`README`, `docs/architecture.md`), superstep-overhead benchmark,
82
+ 98% core test coverage.
@@ -0,0 +1,164 @@
1
+ # Contributing to Sanctum
2
+
3
+ The design rationale, execution model, trade-offs, and the full glossary
4
+ live in [`docs/architecture.md`](docs/architecture.md) — read it before
5
+ changing the core.
6
+
7
+ ## Engineering principles
8
+
9
+ 1. **Zero-dependency core**: the engine is Python stdlib only; extras
10
+ (HTTP, Postgres, in-process inference, docs) are optional modules with
11
+ lazy imports.
12
+ 2. **Local-first**: never assume a proprietary LLM API in the core.
13
+ 3. Everything public is **typed** and documented with dual docstrings
14
+ (metaphor first line, precise technical explanation below).
15
+ 4. **Every feature ships with tests**; tests script LLM behavior with
16
+ `ScriptedOracle` and never touch real models or live services.
17
+ 5. The **public API stays small and stable** (see Versioning below).
18
+ 6. **Async-first**: the engine is native asyncio; synchronous entry
19
+ points are thin wrappers.
20
+ 7. Spells load from directory trees by convention
21
+ (`Tome.load_from_directory`), keeping the tool library decoupled.
22
+
23
+ ## Setup
24
+
25
+ ```sh
26
+ python -m venv .venv
27
+ .venv/Scripts/activate # Windows; use source .venv/bin/activate elsewhere
28
+ pip install -e ".[dev]"
29
+ ```
30
+
31
+ ## Running the tests
32
+
33
+ ```sh
34
+ pytest # unit suite — always green, no services needed
35
+ pytest --cov=sanctum # with core coverage
36
+ python benchmarks/superstep_overhead.py
37
+ ```
38
+
39
+ Unit tests never touch real models or servers: LLM behavior is scripted
40
+ with `ScriptedOracle`, and the HTTP Oracle adapters are tested by
41
+ replaying recorded responses (in `tests/fixtures/`) through
42
+ `httpx.MockTransport`.
43
+
44
+ ## Integration tests (opt-in)
45
+
46
+ The suite includes smoke tests against **live local model servers**,
47
+ marked `@pytest.mark.integration` and skipped unless the matching
48
+ environment variable is set:
49
+
50
+ - `SANCTUM_TEST_OPENAI_COMPAT_URL` — exercises `OpenAICompatibleOracle`
51
+ and the robust tool-calling loop end-to-end against **any** server
52
+ exposing `/v1/chat/completions` (llama.cpp's `llama-server`, Ollama's
53
+ `/v1`, vLLM, LM Studio). Set it to the exact base URL, version prefix
54
+ included.
55
+ - `SANCTUM_TEST_OLLAMA_URL` — exercises the native `OllamaOracle`
56
+ (`/api/chat`) against an Ollama daemon. Base URL without `/v1`.
57
+
58
+ With llama.cpp's `llama-server` (any small instruct GGUF works):
59
+
60
+ ```sh
61
+ llama-server -m qwen2.5-0.5b-instruct-q4_k_m.gguf --port 8080 -c 4096 --jinja
62
+
63
+ # bash
64
+ SANCTUM_TEST_OPENAI_COMPAT_URL=http://127.0.0.1:8080/v1 pytest -m integration
65
+
66
+ # PowerShell
67
+ $env:SANCTUM_TEST_OPENAI_COMPAT_URL = "http://127.0.0.1:8080/v1"
68
+ pytest -m integration
69
+ ```
70
+
71
+ With Ollama (`ollama serve` + `ollama pull qwen2.5:0.5b`), both adapters
72
+ can run in one pass:
73
+
74
+ ```sh
75
+ SANCTUM_TEST_OLLAMA_URL=http://127.0.0.1:11434 \
76
+ SANCTUM_TEST_OPENAI_COMPAT_URL=http://127.0.0.1:11434/v1 \
77
+ pytest -m integration
78
+ ```
79
+
80
+ Model overrides: `SANCTUM_TEST_OLLAMA_MODEL` and
81
+ `SANCTUM_TEST_OPENAI_COMPAT_MODEL` (both default `qwen2.5:0.5b`;
82
+ `llama-server` ignores the name and serves whatever model it loaded).
83
+ These tests assert transport, parsing, and loop survival against a real
84
+ server — never model quality — so any chat-capable model works.
85
+
86
+ ## Style
87
+
88
+ - `ruff check .` must pass (config in `pyproject.toml`; black-compatible
89
+ formatting, line length 88). CI runs it on every push and PR.
90
+ - Code and docstrings in English. The metaphor appears in names and in
91
+ the first line of each docstring, followed by the precise technical
92
+ explanation — never instead of it.
93
+ - Tone of the metaphor: classical occultism and alchemy — mystery and
94
+ precision. Avoid jokey copy, RPG/gamer aesthetics, pop-fantasy
95
+ references, and cartoonish names. Documentation is technically precise
96
+ first; the metaphor is identity, never a substitute for clarity.
97
+ - Error messages must be actionable: name the endpoint/file involved and
98
+ state the most likely fix.
99
+
100
+ ### The two-layer naming policy
101
+
102
+ Domain concepts use the glossary vocabulary (Ritual, Sigil, Aether,
103
+ Conduit, Seal, Codex, Omen, Oracle, Spell, Tome, Ward, summon — full
104
+ glossary with technical equivalences in
105
+ [`docs/architecture.md`](docs/architecture.md) §8). Universal infrastructure stays
106
+ technical and is never themed: `compile`, `invoke`, `ainvoke`, `astream`,
107
+ `add_edge`, `add_conditional_edge`, `reducer`, `recursion_limit`,
108
+ `interrupt`, START, END, superstep. Exceptions combine a domain name with
109
+ a technical suffix (`RitualValidationError`, `SigilTimeoutError`,
110
+ `SpellCallParseError`, ...). Don't invent new glossary terms or mystical
111
+ synonyms for technical ones.
112
+
113
+ ## Commit convention
114
+
115
+ Conventional Commits: `<type>(<scope>): <imperative summary>` where type
116
+ is one of `feat`, `fix`, `docs`, `test`, `refactor`, `perf`, `chore` and
117
+ scope is the subsystem (`ritual`, `aether`, `codex`, `omens`, `oracle`,
118
+ `grimoire`, `wards`, `ci`). Examples:
119
+
120
+ ```
121
+ feat(oracle): add OpenAI-compatible adapter with SSE streaming
122
+ fix(ritual): count fallback supersteps toward recursion_limit
123
+ docs(architecture): document fan-in trade-offs
124
+ ```
125
+
126
+ Breaking changes add a `!` (`feat(ritual)!: ...`) and a `BREAKING CHANGE:`
127
+ footer explaining the migration.
128
+
129
+ ## Versioning (SemVer)
130
+
131
+ The **public API** is the contract (engineering principle 5):
132
+ `Ritual`, `Rite`, `add_sigil`, `add_edge`, `add_conditional_edge`,
133
+ `compile`, `invoke`/`ainvoke`, `astream`, `Codex` (and the `Seal` shape),
134
+ `Tome`, `Oracle` (and `OracleResponse`/`SpellCall`), `summon` — plus the
135
+ documented Omen types, `SigilPolicy`, `Ward` hooks, and the exception
136
+ hierarchy exported from `sanctum`.
137
+
138
+ A **breaking change** is anything that makes existing correct code fail
139
+ or change meaning: removing/renaming those names, changing signatures or
140
+ return shapes, changing documented semantics (delta merge order, Seal
141
+ format, stream mode contents), or raising different exception types.
142
+
143
+ While the project is pre-1.0: breaking changes bump the **minor** version
144
+ (0.2.0 → 0.3.0) and are listed under a "Changed"/"Removed" heading in
145
+ CHANGELOG.md with migration notes; everything else bumps the patch. From
146
+ 1.0.0 on, standard SemVer applies (breaking = major).
147
+
148
+ ## Releasing
149
+
150
+ 1. Update `CHANGELOG.md` (move Unreleased to the new version) and the
151
+ version in `pyproject.toml` + `sanctum/__init__.py`.
152
+ 2. Tag: `git tag vX.Y.Z && git push --tags`.
153
+ 3. `release.yml` builds with `python -m build` and publishes to PyPI via
154
+ **trusted publishing** — the one-time PyPI/GitHub setup is documented
155
+ at the top of `.github/workflows/release.yml`.
156
+
157
+ ## Scope guardrails
158
+
159
+ Sanctum competes in the local-first niche, not on feature parity with
160
+ larger frameworks. Priorities: reliability with local 7-14B models
161
+ (including their tool-calling failure modes), developer experience, and
162
+ observability without external services. Don't add advanced graph
163
+ features without a concrete use case that demands them. Use-case code
164
+ belongs in `examples/`, never in the core or the main docs.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zabdiel Quintero
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,175 @@
1
+ Metadata-Version: 2.4
2
+ Name: sanctum-engine
3
+ Version: 0.2.0
4
+ Summary: A minimal, local-first orchestration engine for AI agents: cyclic state graphs executed by supersteps (Pregel/BSP).
5
+ Project-URL: Repository, https://github.com/zquintero246/sanctum-engine
6
+ Project-URL: Documentation, https://github.com/zquintero246/sanctum-engine/blob/main/docs/architecture.md
7
+ Project-URL: Changelog, https://github.com/zquintero246/sanctum-engine/blob/main/CHANGELOG.md
8
+ Author: Zabdiel Quintero
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,llama-cpp,llm,local-first,ollama,orchestration,pregel,state-graph
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Provides-Extra: all
24
+ Requires-Dist: httpx>=0.27; extra == 'all'
25
+ Requires-Dist: psycopg>=3.1; extra == 'all'
26
+ Requires-Dist: transformers>=4.40; extra == 'all'
27
+ Provides-Extra: dev
28
+ Requires-Dist: httpx>=0.27; extra == 'dev'
29
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.4; extra == 'dev'
33
+ Provides-Extra: docs
34
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
35
+ Requires-Dist: mkdocstrings[python]>=0.25; extra == 'docs'
36
+ Provides-Extra: llamacpp
37
+ Requires-Dist: llama-cpp-python>=0.3; extra == 'llamacpp'
38
+ Provides-Extra: ollama
39
+ Requires-Dist: httpx>=0.27; extra == 'ollama'
40
+ Provides-Extra: openai-compat
41
+ Requires-Dist: httpx>=0.27; extra == 'openai-compat'
42
+ Provides-Extra: postgres
43
+ Requires-Dist: psycopg>=3.1; extra == 'postgres'
44
+ Provides-Extra: transformers
45
+ Requires-Dist: transformers>=4.40; extra == 'transformers'
46
+ Description-Content-Type: text/markdown
47
+
48
+ # Sanctum
49
+
50
+ [![CI](https://github.com/zquintero246/sanctum-engine/actions/workflows/ci.yml/badge.svg)](https://github.com/zquintero246/sanctum-engine/actions/workflows/ci.yml)
51
+ [![PyPI](https://img.shields.io/pypi/v/sanctum-engine)](https://pypi.org/project/sanctum-engine/)
52
+ [![coverage](https://img.shields.io/badge/coverage-94%25-6d8f6a)](https://github.com/zquintero246/sanctum-engine/actions/workflows/ci.yml)
53
+ [![license: MIT](https://img.shields.io/badge/license-MIT-b49b5e)](LICENSE)
54
+ [![python](https://img.shields.io/pypi/pyversions/sanctum-engine)](pyproject.toml)
55
+
56
+ *Where agents are summoned, bound, and set to work — a minimal, local-first
57
+ orchestration engine for cyclic state graphs.*
58
+
59
+ Sanctum models multi-agent orchestration as a ritual of invocation:
60
+ knowledge lives in the Grimoire (tools), the Sanctum prepares and controls
61
+ the ritual (the engine), entities are summoned (agents), and all of them
62
+ cooperate over a shared energy — the Aether (state) — until a result is
63
+ manifested. Beneath the metaphor sits a precise execution model: a **cyclic
64
+ state graph** run by supersteps (Pregel/BSP), where nodes execute in
65
+ parallel, return partial state deltas merged through per-channel reducers,
66
+ and conditional edges close the loops that make agentic behavior
67
+ (think → act → observe → …) possible. The core is pure Python standard
68
+ library — no proprietary APIs, no mandatory dependencies — designed to run
69
+ entirely on local models.
70
+
71
+ **[Documentation](https://zquintero246.github.io/sanctum-engine/)** ·
72
+ [Getting started](https://zquintero246.github.io/sanctum-engine/getting-started/) ·
73
+ [Comparison with LangGraph / n8n / ADK](https://zquintero246.github.io/sanctum-engine/comparison/) ·
74
+ [Design document](docs/architecture.md)
75
+
76
+ ![The self-contained trace viewer (illustration of render_trace output)](docs/assets/trace-viewer.svg)
77
+
78
+ ## Features
79
+
80
+ | | |
81
+ |---|---|
82
+ | **Cyclic state graphs** | BSP supersteps, static fan-out, conditional edges, cycles bounded by `recursion_limit` — not a DAG |
83
+ | **Deterministic state** | Per-Conduit reducers applied in Sigil insertion order; identical runs, even under parallelism |
84
+ | **Local-first Oracles** | Ollama (native & `/v1`), llama-server, vLLM, LM Studio, in-process GGUF — never a proprietary API in the core |
85
+ | **Robust tool-calling** | Malformed JSON repaired, unknown Spells corrected conversationally, prompted fallback for models without tools |
86
+ | **Seals & time-travel** | JSON checkpoints per superstep (memory/SQLite/Postgres), resume, `interrupt()` human-in-the-loop, replay from any Seal |
87
+ | **Streaming Omens** | Typed, timestamped events; combinable modes; live tokens from inside a Sigil |
88
+ | **Resilience policies** | Per-Sigil timeout, retries with backoff+jitter, fallback Sigils reading `__errors__` |
89
+ | **Wards middleware** | Transform or veto deltas, observe every event: audit JSONL, usage tally, redaction |
90
+ | **Local tracing** | One-file HTML viewer, zero external requests: `python -m sanctum.trace render run.sanctum-trace.json` |
91
+ | **Zero-dependency core** | Python stdlib only; everything else is an optional extra |
92
+
93
+ ## Quickstart
94
+
95
+ ```sh
96
+ pip install sanctum-engine
97
+ ```
98
+
99
+ ```python
100
+ from sanctum import END, Ritual
101
+
102
+ ritual = Ritual()
103
+ ritual.add_sigil("cleanse", lambda aether: {"text": aether["text"].strip()})
104
+ ritual.add_sigil("transmute", lambda aether: {"text": aether["text"].upper()})
105
+ ritual.set_entry_point("cleanse")
106
+ ritual.add_edge("cleanse", "transmute")
107
+ ritual.add_edge("transmute", END)
108
+
109
+ rite = ritual.compile()
110
+ print(rite.invoke({"text": " fiat lux "}))
111
+ # {'text': 'FIAT LUX'}
112
+ ```
113
+
114
+ ## Summoning an Entity
115
+
116
+ `summon()` builds the canonical ReAct loop (oracle → spells → oracle → …
117
+ → END) entirely on the public primitives:
118
+
119
+ ```python
120
+ import asyncio
121
+ from sanctum import Tome, spell, summon
122
+ from sanctum.oracle.ollama import OllamaOracle # pip install "sanctum-engine[ollama]"
123
+
124
+ @spell
125
+ def word_count(text: str) -> int:
126
+ """Count the words in a text."""
127
+ return len(text.split())
128
+
129
+ entity = summon(
130
+ OllamaOracle(arcana="qwen2.5:7b"),
131
+ Tome([word_count]),
132
+ role="You are a scribe.",
133
+ spell_calling="auto", # prompted fallback if the model lacks native tools
134
+ )
135
+ result = asyncio.run(entity.ainvoke(
136
+ {"messages": [{"role": "user", "content": "How many words in 'fiat lux'?"}]}
137
+ ))
138
+ print(result["messages"][-1]["content"])
139
+ ```
140
+
141
+ ## Examples
142
+
143
+ The [`examples/`](examples/) gallery runs on scripted oracles by default —
144
+ no model required — and every script takes `--oracle ollama`:
145
+
146
+ - [`quickstart_ollama.py`](examples/quickstart_ollama.py) — chat with a
147
+ tool in thirty lines.
148
+ - [`research_ritual/`](examples/research_ritual/) — two entities scout in
149
+ parallel (fan-out), a third synthesizes (fan-in, append reducer).
150
+ - [`human_in_the_loop/`](examples/human_in_the_loop/) — `interrupt()` +
151
+ Codex: pause for approval, resume where it stopped.
152
+ - [`resilient_pipeline/`](examples/resilient_pipeline/) — retries,
153
+ timeouts, and a fallback Sigil in one observable run.
154
+ - [`sse_flask.py`](examples/sse_flask.py) — bridge `astream` to
155
+ Server-Sent Events.
156
+
157
+ ## Ecosystem
158
+
159
+ Sanctum owns execution; [AgentGrimoire](https://github.com/zquintero246/AgentGrimoire)
160
+ owns capability — a folder-per-Spell tool library loadable by convention
161
+ with `Tome.load_from_directory(path)`. Either side evolves without
162
+ touching the other.
163
+
164
+ ## Development
165
+
166
+ ```sh
167
+ pip install -e ".[dev]"
168
+ ruff check . && pytest --cov=sanctum # unit suite: no models, no services
169
+ python benchmarks/superstep_overhead.py # engine overhead: tens of µs/superstep
170
+ ```
171
+
172
+ Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) (includes
173
+ how to run the opt-in integration tests against a local Ollama) and the
174
+ [design document](docs/architecture.md) for the rationale behind every
175
+ trade-off. Licensed [MIT](LICENSE).