jdsl
Declarative behavior-tree agents as clean Python combinators, run by a tree-walking interpreter over DeepSeek / Anthropic / OpenAI — with DSPy-style signatures on the LLM leaves.
Determinism lives in the tree (seq / sel / act); the model enters only at
predict leaves, typed by a signature like "message -> category". No codegen,
no object-literal DSL — the tree structure is the call structure.
from jdsl import tool, root, seq, sel, act, check, predict, ref, store
@tool
def route_to_billing(): print("→ billing")
@tool
def route_to_support(): print("→ support")
@tool
def route_to_human(): print("→ human")
skill = (
root("Triage", system="Classify inbound messages: billing, support, other.")
.model("deepseek-chat") # provider inferred from the id
.do(seq(
predict("message -> category"), # the model classifies…
sel( # …and the tree branches on it
seq(check("category", "billing"), act(route_to_billing)),
seq(check("category", "support"), act(route_to_support)),
act(route_to_human), # fallback
),
))
)
ctx = skill.run(message="I was double charged.")
print(ctx.blackboard["category"]) # -> "billing"
…and the model can drive your tools. A react leaf hands it your @tools and
lets it pick and chain them (native function-calling):
from jdsl import tool, root, react
@tool
def distance_km(origin: str, destination: str) -> int: ... # your Python
@tool
def drive_hours(km: int) -> float: ...
skill = root("Trip").model("deepseek-chat").do(
react("request -> answer", tools=[distance_km, drive_hours])
)
skill.run(request="Driving Nairobi to Mombasa — how long?")
> distance_km(origin='Nairobi', destination='Mombasa') -> 485
> drive_hours(km=485) -> 6.1
answer: About 6.1 hours.
Triage steers the model with the tree; react lets the model steer your tools.
Full version: examples/trip.py.
Skill authoring TUI
JDSL includes a compact terminal workbench for building restricted behavior trees without writing the initial Python DSL:
uv run --extra tui jdsl tui
Add sequence, selector, action, guard, predict, react, and repeat nodes; edit
their properties; validate the tree; and save a portable .jdsl package. See
the TUI guide for the authoring workflow.
The workbench uses the Gruvbox Material palette, adapted from
sainnhe/gruvbox-material and
curbol/omarchy-gruvbox-material.
Truecolor terminals are recommended (COLORTERM=truecolor). Use Ctrl+M to
toggle reduced motion and Ctrl+G to switch between ASCII-safe and Unicode glyphs.
Install & run
Quick install (curl)
curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash
Or with options:
curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --version 0.6.0 --no-modify-path
Requires Python ≥ 3.11. The curl installer includes the harness dependency as well as the runtime CLI. After install:
jdsl about # banner + quickstart
jdsl run examples/greeter.py
Upgrade a previous curl installation with:
curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --uninstall
curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --version 0.6.0
The installed CLI also supports jdsl uninstall; captures and provider
credentials are preserved.
Development (uv)
Requires uv and Python ≥ 3.11.
uv sync
uv run jdsl run examples/greeter.py # deterministic, no key
echo 'DEEPSEEK_API_KEY=sk-...' >> .env # or ANTHROPIC_API_KEY=...
uv run jdsl run examples/triage.py # LLM-driven
uv run pytest # tests (no network)
The provider is inferred from the model id: deepseek-chat → DeepSeek,
claude-* → Anthropic, gpt-*/o* → OpenAI. A .env in the working directory
is loaded automatically.
The API in one screen
| Combinator | Meaning |
|---|---|
root(name, system=…) |
Entry point. Chain .model(id) then .do(child). |
seq(*children) |
Run in order; fail fast (behavior-tree AND). |
sel(*children) |
Try until one succeeds (behavior-tree OR). |
repeat(child, until=…, max=n) |
Loop child until until succeeds or max passes (retry/refine). |
invert(child) · optional(child) |
Decorators: flip a status · fail-soft (a failing step won't abort its seq). |
timeout(child, seconds=…) · oneshot(child) |
Decorators: bound wall-clock time · run once per run, replay the status. |
act(fn, *args) |
Leaf calling a @tool; literal args are type-checked. |
ref("key") |
An act argument resolved from the blackboard at run time. |
store(act(…), "key") |
Capture an action's return value onto the blackboard. |
check("key", value) |
Guard leaf on blackboard["key"]; string matches are case/whitespace-lenient. |
predict("a -> b") |
DSPy-style LLM leaf: reads inputs, writes outputs. |
react("q -> a", tools=[…]) |
Agentic leaf: the model calls @tools in a loop (function-calling) until it answers. |
Layout
jdsl/ the runtime core — dsl, tree (interpreter), context, provider, render, cli
trace/ canonical trace events, sinks, storage, redaction, replay
ir/ Behavior IR: schema, safe guard expressions, validation, lowering
package/ .jdsl manifest, tool contracts, provenance, export + loader
jdsl_harness/ capture + behavior compiler (separate package; optional [harness] extra)
plugins/ Claude Code plugin + Gemini CLI extension (trace capture)
examples/ runnable skills — see examples/README.md
test/ pytest suite (no network; LLM leaves use fakes)
docs/ concepts, API reference, providers, harness & compiler
Behavior harness & compiler
jdsl can also compile observed frontier-model behavior into portable policy a
smaller frozen model runs. The harness captures canonical traces, mines and
verifies reusable structure (exact dataflow, sequencing, guards, recovery), and
leaves only irreducible semantic decisions as typed signatures — exported as a
deterministic .jdsl.
jdsl harness serve # local capture daemon
jdsl capture inspect <id> # exact-lineage report
jdsl compile <id> --out retail.jdsl
jdsl package run retail.jdsl --tools tools.py --model <small-model>
See docs/harness_usage.md for the hands-on guide (all three capture tiers, compile, verify, run) and docs/harness.md for the design-to-code map.
Docs
- docs/index.md — MkDocs homepage
- docs/quickstart.md — install and first runs
- docs/examples.md — guide to the runnable examples
- docs/concepts.md — behavior trees, the blackboard, signatures, why no codegen
- docs/api.md — full combinator + node reference
- docs/providers.md — models, keys,
.env, routing - docs/tui.md — skill authoring TUI
- docs/harness_usage.md — hands-on: capture, compile, verify, run
- docs/harness.md — capture harness and behavior compiler overview
- docs/packages.md —
.jdslpackage format and binding - CONTRIBUTING.md — dev setup, tests, adding examples
Status
Work in progress. The interpreter, combinator API, signatures, and the Anthropic / OpenAI-compatible (DeepSeek, OpenAI) backends run today. Not yet built: Google provider, and the DSPy-defining optimization step (tuning prompts against a metric) — the natural next direction now the runtime exists.
License
O`Saasy · Author: Cantor Industries
Metadata
Release files for jdsl 0.6.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| jdsl-0.6.2.tar.gz | 311.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jdsl-0.6.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 467.2 kB
Release files / jdsl-0.6.2.tar.gz
| Download URL | jdsl-0.6.2.tar.gz |
|---|---|
| Size | 311.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f0f6356e582ec471d57cca492b571764843642ac8070e5383b730ab5e2019bfd
|
|
BLAKE2b-256 checksum How to use checksums |
e50c605732092bcbe98933d85cebfeec6c2c496d7c5733ac333d9750ca410be5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / jdsl-0.6.2-py3-none-any.whl
| Download URL | jdsl-0.6.2-py3-none-any.whl |
|---|---|
| Size | 155.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
20ed00fb1e1c78d5275a4ea396093c5dd65eae07480c6f1adcefa87989d2b527
|
|
BLAKE2b-256 checksum How to use checksums |
e1400b09d750389dd58b95336bdf5126306124c3dfcdc9a505c1fac6be58c727
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for jdsl 0.6.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for jdsl-0.6.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.