PerSQL Python SDK — SQLite databases on the edge for AI agents.
Project description
persql
Python SDK for PerSQL — SQLite databases on the edge for AI agents. One isolated database per agent, per app, per PR, per use case — backed by Cloudflare Durable Objects with embedded SQLite. Prepaid billing, four meters, no plans.
pip install persql
Quick start
import os
from persql import PerSQL
persql = PerSQL(token=os.environ["PERSQL_TOKEN"])
db = persql.database("acme/orders")
result = db.query("SELECT id, email FROM customers WHERE id = ?", [42])
for row in result["data"]:
print(row["email"])
Async:
import asyncio
from persql import AsyncPerSQL
async def main():
async with AsyncPerSQL(token=os.environ["PERSQL_TOKEN"]) as persql:
db = persql.database("acme/orders")
result = await db.query("SELECT 1 AS one")
print(result["data"])
asyncio.run(main())
Local mode (tests, no network)
persql = PerSQL(local=":memory:")
db = persql.database("test/db")
db.query("CREATE TABLE t (id INTEGER)")
db.query("INSERT INTO t (id) VALUES (?)", [1])
print(db.query("SELECT * FROM t")["data"]) # [{"id": 1}]
Local mode uses stdlib sqlite3 — no extra dependencies. Branches,
approvals, and subscribe require a server-mode token (psql_live_…
or psql_test_…) and raise in local mode.
Agent tools
Generate a typed tool bundle for any LLM — one tool per table plus discovery, safety, and branch-management tools — and let the model drive the database:
import anthropic
from persql import PerSQL
persql = PerSQL(token=os.environ["PERSQL_TOKEN"])
db = persql.database("acme/orders")
tools = db.as_tools()
client = anthropic.Anthropic()
reply = client.messages.create(
model="claude-opus-4-7",
max_tokens=4096,
tools=tools["anthropic"],
messages=[{"role": "user", "content": "How many customers signed up last week?"}],
)
for block in reply.content:
if block.type == "tool_use":
result = tools["run"](block.name, block.input)
print(block.name, "→", result)
The same bundle exposes:
tools["anthropic"]— pass directly toanthropic.messages.createtools["openai"]— pass toopenai.chat.completions.createtools["langchain"]— convert withDynamicStructuredTooltools["run"](name, input)— sync or async dispatcher
Async clients return an awaitable bundle; await db.as_tools() then
use await tools["run"](...).
Branches
Each branch is its own database, forked from the parent at create time. Idempotent by ref — call from CI with the PR number and the same ref re-runs as a reset:
branch = db.branches.upsert("pr-42", ttl_days=7)
preview = db.branches.preview("pr-42")
print(preview["plan"]) # added / changed / removed objects
Safety primitives
Pre-flight a write, get a single-use token, redeem only if the plan looks right:
plan = db.proposals.propose(
"UPDATE orders SET status='shipped' WHERE created_at < ?",
params=["2026-01-01"],
)
print(plan["estimated_affected_rows"])
db.proposals.apply(plan["execution_token"])
Approvals
When a write hits a require_approval rule, the SDK raises
ApprovalRequiredError. Either halt and surface the URL to a human,
or wait for the decision and redeem:
from persql import ApprovalRequiredError
try:
db.query("DELETE FROM customers WHERE id = ?", [42])
except ApprovalRequiredError as e:
print(f"Needs approval: {e.approval_url}")
# …after a member approves in the console:
db.approvals.redeem(e.approval_token)
Subscribe (async, optional)
Row-change events over WebSocket. Install the optional dep:
pip install 'persql[subscribe]'
async with AsyncPerSQL(token=...) as persql:
db = persql.database("acme/orders")
async for change in await db.subscribe(tables=["orders"]):
print(change["table"], change["kind"])
Errors
from persql import ApprovalRequiredError, PerSQLError, RateLimitError
try:
db.query("UPDATE orders SET status='shipped'")
except ApprovalRequiredError as e:
pass # human approval required — see e.approval_url
except RateLimitError as e:
time.sleep(e.retry_after_seconds)
except PerSQLError as e:
# `.detail["kind"]` is set on /v1/query SQL errors
print(e.status, e.args[0], getattr(e, "detail", None))
Reference
| Component | Description |
|---|---|
| Console | Manage tokens, databases, branches, billing |
| Docs | Concepts, API reference, examples |
| TS SDK | TypeScript equivalent of this package |
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 persql-0.3.0.tar.gz.
File metadata
- Download URL: persql-0.3.0.tar.gz
- Upload date:
- Size: 30.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a12886e0e4807a3ac73d711d3edc89d8c11d476fe728affee0ee64cd2121216e
|
|
| MD5 |
0b5b51d46f4c053e9451a0b6aa69f82b
|
|
| BLAKE2b-256 |
b3142292b1e634ade13e73f9328a5d1582312ce9f64412bff834ae7d8c11ab80
|
Provenance
The following attestation bundles were made for persql-0.3.0.tar.gz:
Publisher:
python-release.yml on premsan/persql
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
persql-0.3.0.tar.gz -
Subject digest:
a12886e0e4807a3ac73d711d3edc89d8c11d476fe728affee0ee64cd2121216e - Sigstore transparency entry: 1559429583
- Sigstore integration time:
-
Permalink:
premsan/persql@8c428e4371e6c8f68cc8d99f78df1477999daa37 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/premsan
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-release.yml@8c428e4371e6c8f68cc8d99f78df1477999daa37 -
Trigger Event:
push
-
Statement type:
File details
Details for the file persql-0.3.0-py3-none-any.whl.
File metadata
- Download URL: persql-0.3.0-py3-none-any.whl
- Upload date:
- Size: 35.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2fde658d4ce9c64f20c96b7ee87cdf52fda036688a6417f443913de0e287f73
|
|
| MD5 |
0758244de96ae64e04d1978073f6424b
|
|
| BLAKE2b-256 |
abf6c31ffa3cf3f4c46fb359ffae3657b60f3b1db2575f5e9f0da19402ddf22b
|
Provenance
The following attestation bundles were made for persql-0.3.0-py3-none-any.whl:
Publisher:
python-release.yml on premsan/persql
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
persql-0.3.0-py3-none-any.whl -
Subject digest:
c2fde658d4ce9c64f20c96b7ee87cdf52fda036688a6417f443913de0e287f73 - Sigstore transparency entry: 1559429668
- Sigstore integration time:
-
Permalink:
premsan/persql@8c428e4371e6c8f68cc8d99f78df1477999daa37 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/premsan
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-release.yml@8c428e4371e6c8f68cc8d99f78df1477999daa37 -
Trigger Event:
push
-
Statement type: