Metadata-Version: 2.4
Name: persql
Version: 0.4.0
Summary: PerSQL Python SDK — SQLite databases on the edge for AI agents.
Project-URL: Homepage, https://persql.com
Project-URL: Documentation, https://docs.persql.com
Project-URL: Console, https://console.persql.com
Project-URL: Issues, https://persql.com/support
Author-email: Premsan <support@persql.com>
License: MIT
License-File: LICENSE
Keywords: agents,cloudflare,database,edge,llm,sqlite
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Requires-Dist: typing-extensions>=4.7; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Provides-Extra: subscribe
Requires-Dist: websockets>=11; extra == 'subscribe'
Provides-Extra: test
Requires-Dist: anyio>=3; extra == 'test'
Requires-Dist: pytest-asyncio>=0.21; extra == 'test'
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# persql

Python SDK for **[PerSQL](https://persql.com)** — 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.

```bash
pip install persql
```

## Quick start

```python
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:

```python
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)

```python
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:

```python
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 to `anthropic.messages.create`
- `tools["openai"]` — pass to `openai.chat.completions.create`
- `tools["langchain"]` — convert with `DynamicStructuredTool`
- `tools["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:

```python
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:

```python
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:

```python
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:

```bash
pip install 'persql[subscribe]'
```

```python
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

```python
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](https://console.persql.com) | Manage tokens, databases, branches, billing |
| [Docs](https://docs.persql.com) | Concepts, API reference, examples |
| [TS SDK](https://www.npmjs.com/package/@persql/sdk) | TypeScript equivalent of this package |

## License

MIT
