The sqlite of durable agent execution — crash-recoverable AI agents with exactly-once semantics. Zero dependencies.
Project description
delite
The sqlite of durable agent execution. Crash-recoverable AI agents with exactly-once semantics.
Install
Binary
brew install benelser/tap/delite # macOS
cargo install delite-core # Rust toolchain
pip install delite-core # pip
npm install @bjelser/delite-core # npm
curl -sSL https://raw.githubusercontent.com/benelser/durable/main/install.sh | sh
Python SDK
pip install durable # pip
uv add durable # uv
poetry add durable # poetry
TypeScript SDK (coming soon)
npm install durable # npm
bun add durable # bun
Set your LLM provider key:
export OPENAI_API_KEY=sk-...
# or
export ANTHROPIC_API_KEY=sk-ant-...
Quick Start
from durable import Agent, tool
from durable.providers import OpenAI
@tool("get_weather", description="Get weather for a location")
def get_weather(location: str) -> dict:
return {"temp": 72, "conditions": "sunny", "location": location}
with Agent("./data") as agent:
agent.add_tool(get_weather)
agent.set_llm(OpenAI())
response = agent.run("What's the weather in San Francisco?")
print(response)
What It Does
Every LLM call and tool execution is a durable step. Results are persisted to an append-only event log before the next step begins. If the process crashes — between the payment charge and the confirmation email, between retry 3 and retry 4, between hour 1 and hour 47 — execution resumes exactly where it left off.
Without delite:
charge_payment() → crash → restart → charge_payment() again
Result: customer charged twice ($299.94)
With delite:
charge_payment() → crash → restart → cached result returned
Result: customer charged once ($149.97)
Crash Recovery
# First run — everything executes and is persisted
response = agent.run("Process order #123")
execution_id = response.execution_id
# Process crashes. On restart, pass the same execution_id:
response = agent.run("Process order #123", execution_id=execution_id)
# All completed steps return cached results. No re-execution.
Human-in-the-Loop
Mark any tool as requiring human approval before execution:
@tool("transfer_funds", description="Transfer money", requires_confirmation=True)
def transfer_funds(from_acct: str, to_acct: str, amount: float) -> dict:
return {"status": "transferred", "amount": amount}
response = agent.run("Transfer $5000 from checking to savings")
# response.is_suspended == True
# The tool has NOT executed. Human reviews and approves:
agent.approve(response.execution_id, response.suspend_reason.confirmation_id)
response = agent.resume(response.execution_id)
# Now the transfer executes
Contracts
Contracts are checks that run before a tool executes. They are code, not prompt engineering — the LLM cannot circumvent them.
@agent.contract("max-charge")
def check_charge(step_name, args):
if "charge" in step_name and args.get("amount", 0) > 1000:
raise ValueError("Charges over $1000 need VP approval")
response = agent.run("Charge $5000")
# response.is_suspended == True — the tool never executed
Budget Limits
from durable import Budget
agent.budget = Budget(max_dollars=2.00, max_llm_calls=10)
response = agent.run("Research this topic thoroughly")
if response.is_suspended:
print(f"Budget exhausted: {response.suspend_reason}")
# All completed work is preserved. Increase budget and resume.
Multi-Agent Runtime
One runtime, N agents, running as durable threads inside your process:
from durable import Runtime, Agent
rt = Runtime("./data")
researcher = Agent("./data", runtime=rt, agent_id="researcher", ...)
writer = Agent("./data", runtime=rt, agent_id="writer", ...)
# Non-blocking spawn
rt.go(researcher, "Research the topic")
rt.go(writer, "Write the report")
# Lifecycle callbacks
@rt.on_complete
def done(agent_id, exec_id, response):
print(f"{agent_id} finished")
@rt.on_suspend
def paused(agent_id, exec_id, reason):
send_slack_notification(f"Approval needed: {reason}")
# Signals trigger auto-resume
rt.signal(exec_id, confirmation_id, True)
Idempotency Keys
Every tool callback includes a unique idempotency key. Forward it to payment providers to prevent double-charges:
from durable.agent import current_idempotency_key
@tool("charge", description="Charge payment")
def charge(customer_id: str, amount: float) -> dict:
stripe.PaymentIntent.create(
amount=int(amount * 100),
customer=customer_id,
idempotency_key=current_idempotency_key(),
)
return {"status": "charged"}
LLM Providers
| Provider | Python | Env Variable |
|---|---|---|
| OpenAI | from durable.providers import OpenAI |
OPENAI_API_KEY |
| Anthropic | from durable.providers import Anthropic |
ANTHROPIC_API_KEY |
| Custom | Any callable (messages, tools, model) -> dict |
— |
Streaming
for chunk in agent.stream("Tell me a story"):
print(chunk, end="", flush=True)
CLI
Inspect any execution after it runs:
delite status --data-dir ./data # list all executions
delite inspect <execution-id> --data-dir ./data # detailed view
delite steps <execution-id> --data-dir ./data # step timeline
delite events <execution-id> --data-dir ./data # raw event log
delite export <execution-id> --data-dir ./data # JSON export
delite health --data-dir ./data # storage health
How delite Compares
vs LangGraph
LangGraph has checkpointing (save/restore graph state via sqlite, Postgres, etc). This gives you conversation resume and human-in-the-loop interrupts.
What it does NOT give you: exactly-once tool execution. LangGraph checkpoints at node boundaries, not within nodes. If a tool executes and the process crashes before the next checkpoint, the tool re-executes on resume.
delite persists every step individually before the next begins. Prompt and tool drift are detected on resume.
vs Temporal
Temporal is the gold standard: multi-machine clusters, workflow versioning, visibility UI, battle-tested in production. It requires a Temporal Server cluster, separate worker processes, and an infrastructure team.
delite is for teams that want those guarantees without operating distributed infrastructure. One process, one binary, files on disk. If you outgrow single-machine, Temporal is the right next step.
Architecture
┌─────────────────────────────────────┐
│ Your code (Python, TypeScript) │
├─────────────────────────────────────┤
│ SDK (zero dependencies) │
├─────────────────────────────────────┤
│ Rust Engine (invisible subprocess) │
├─────────────────────────────────────┤
│ Event Log (append-only files) │
└─────────────────────────────────────┘
Single-process. The Rust engine runs as an invisible subprocess. The LLM API is the bottleneck, not the runtime — a single machine handles more concurrent agents than most teams can afford in API costs.
Zero Dependencies
The Rust engine uses only the standard library. The Python SDK uses only stdlib (json, subprocess, urllib, threading). No transitive dependency hell.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file delite_core-0.2.5-py3-none-win_amd64.whl.
File metadata
- Download URL: delite_core-0.2.5-py3-none-win_amd64.whl
- Upload date:
- Size: 572.3 kB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1ec37f592a338294f211f3744a9e5cbf98aa09549084edb19e6deb0c0afbc8d
|
|
| MD5 |
daf93f2d559a994be824588db6618643
|
|
| BLAKE2b-256 |
812c2d2ac8c94b64d2373c12ba94b5f0f2a0bc13d2747b4df73ddd3b4258fe68
|
Provenance
The following attestation bundles were made for delite_core-0.2.5-py3-none-win_amd64.whl:
Publisher:
release.yml on benelser/durable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
delite_core-0.2.5-py3-none-win_amd64.whl -
Subject digest:
f1ec37f592a338294f211f3744a9e5cbf98aa09549084edb19e6deb0c0afbc8d - Sigstore transparency entry: 1250669262
- Sigstore integration time:
-
Permalink:
benelser/durable@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Branch / Tag:
refs/tags/v0.2.5 - Owner: https://github.com/benelser
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Trigger Event:
push
-
Statement type:
File details
Details for the file delite_core-0.2.5-py3-none-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: delite_core-0.2.5-py3-none-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 706.4 kB
- Tags: Python 3, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc2f29132704596cf2df0412b970cb514b0acbfe161a8649a9e811e823b5ef90
|
|
| MD5 |
bb44baaa72903ea2fd02f62eb7f78c16
|
|
| BLAKE2b-256 |
29b1fd11ee5249df35137dca0bccab38389b6c2e800909f3aee9eceaefc2aac7
|
Provenance
The following attestation bundles were made for delite_core-0.2.5-py3-none-musllinux_1_2_x86_64.whl:
Publisher:
release.yml on benelser/durable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
delite_core-0.2.5-py3-none-musllinux_1_2_x86_64.whl -
Subject digest:
dc2f29132704596cf2df0412b970cb514b0acbfe161a8649a9e811e823b5ef90 - Sigstore transparency entry: 1250669214
- Sigstore integration time:
-
Permalink:
benelser/durable@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Branch / Tag:
refs/tags/v0.2.5 - Owner: https://github.com/benelser
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Trigger Event:
push
-
Statement type:
File details
Details for the file delite_core-0.2.5-py3-none-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: delite_core-0.2.5-py3-none-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 604.5 kB
- Tags: Python 3, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1698ad146fa1ed48fb5b8b7c84cb61a6afe3b695538bcb3292576b894b390fe4
|
|
| MD5 |
c2baed05efc3edc748c252a429fe66cd
|
|
| BLAKE2b-256 |
24904f28d5b23159e97c1addefeb982b3723082bc3aef9a3a6e3df9a50bfc661
|
Provenance
The following attestation bundles were made for delite_core-0.2.5-py3-none-manylinux_2_28_aarch64.whl:
Publisher:
release.yml on benelser/durable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
delite_core-0.2.5-py3-none-manylinux_2_28_aarch64.whl -
Subject digest:
1698ad146fa1ed48fb5b8b7c84cb61a6afe3b695538bcb3292576b894b390fe4 - Sigstore transparency entry: 1250669183
- Sigstore integration time:
-
Permalink:
benelser/durable@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Branch / Tag:
refs/tags/v0.2.5 - Owner: https://github.com/benelser
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Trigger Event:
push
-
Statement type:
File details
Details for the file delite_core-0.2.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: delite_core-0.2.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 661.2 kB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da486c3ca1ca00a4ff43b4fa103ded131c74aafc5bd0c34abfdf279f701a1377
|
|
| MD5 |
abceebaf97aab291c9ffefc2cc5ac3ed
|
|
| BLAKE2b-256 |
be6e4dc7e81039f373da4f2c5f094ebc501c16b7a97c1d12c599b91efcde8e65
|
Provenance
The following attestation bundles were made for delite_core-0.2.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on benelser/durable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
delite_core-0.2.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
da486c3ca1ca00a4ff43b4fa103ded131c74aafc5bd0c34abfdf279f701a1377 - Sigstore transparency entry: 1250669233
- Sigstore integration time:
-
Permalink:
benelser/durable@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Branch / Tag:
refs/tags/v0.2.5 - Owner: https://github.com/benelser
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Trigger Event:
push
-
Statement type:
File details
Details for the file delite_core-0.2.5-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: delite_core-0.2.5-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 598.1 kB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
606454bdaf3076b14cdd32aaa3efa9912c20a9ccb32b82c547296e2554d4ed9b
|
|
| MD5 |
0e54cd22b0b82daecda0f43820bc23a4
|
|
| BLAKE2b-256 |
2abcff3d20f276d32606994fce7b94f1a3ece736924abbadd967fbfd4fd5d725
|
Provenance
The following attestation bundles were made for delite_core-0.2.5-py3-none-macosx_11_0_arm64.whl:
Publisher:
release.yml on benelser/durable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
delite_core-0.2.5-py3-none-macosx_11_0_arm64.whl -
Subject digest:
606454bdaf3076b14cdd32aaa3efa9912c20a9ccb32b82c547296e2554d4ed9b - Sigstore transparency entry: 1250669192
- Sigstore integration time:
-
Permalink:
benelser/durable@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Branch / Tag:
refs/tags/v0.2.5 - Owner: https://github.com/benelser
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Trigger Event:
push
-
Statement type:
File details
Details for the file delite_core-0.2.5-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: delite_core-0.2.5-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 640.6 kB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
632c692db42148802e9ca9de76c466e1a7dfdfe301ceca762563e78876e6bcaa
|
|
| MD5 |
09e882c6a8bba5dee00aea5757822ce8
|
|
| BLAKE2b-256 |
816df43a555787b9dc2f15bad621e0ffcd207a2d0e59f4baf47715e3b3be2693
|
Provenance
The following attestation bundles were made for delite_core-0.2.5-py3-none-macosx_10_12_x86_64.whl:
Publisher:
release.yml on benelser/durable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
delite_core-0.2.5-py3-none-macosx_10_12_x86_64.whl -
Subject digest:
632c692db42148802e9ca9de76c466e1a7dfdfe301ceca762563e78876e6bcaa - Sigstore transparency entry: 1250669167
- Sigstore integration time:
-
Permalink:
benelser/durable@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Branch / Tag:
refs/tags/v0.2.5 - Owner: https://github.com/benelser
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@53d4d7742726a874f66ef882b50ecdd3d41c5632 -
Trigger Event:
push
-
Statement type: