StatePact¶
Transaction management for autonomous AI agents.
StatePact gives a multi-agent system one thing most frameworks leave out: a correct boundary around shared mutable state. Agents read a value, reason about it, and write it back. Without conflict detection, concurrent agents lose updates, oversell inventory, double-apply side effects and violate invariants.
async with manager.session(agent_id="planner", resources=[inventory]) as session:
stock = await session.read("sku-1")
if stock <= 0:
return "OUT_OF_STOCK"
await session.write("sku-1", stock - 1)
await session.commit()
The session stages reads and writes. At commit, the backend validates the read set and write bases against the latest committed versions and applies all writes atomically, or rejects the transaction with a classified conflict. A retry re-runs the whole body on a fresh snapshot.
Try the live concurrency playground to see lost updates, overselling and write skew under each isolation mode. It is a browser simulation of the protocol; the Python engine is in this repository.
What you get¶
| Capability | Where |
|---|---|
| Snapshot reads and atomic commits | TransactionManager, AgentSession |
optimistic, snapshot, serializable, pessimistic, none isolation |
Isolation |
| Conflict retries with backoff, jitter and replanning | Retries |
| Idempotent commit replay | Idempotency |
| Leases with monotonic fencing tokens | Leases |
| In-memory, PostgreSQL and Redis backends | Backends |
| LangGraph, CrewAI and AutoGen adapters | Integrations |
| Events, transaction log, metrics, OpenTelemetry bridge | Operations |
Install¶
pip install statepact
pip install "statepact[postgres,redis]" # add the PostgreSQL and Redis backends
Python 3.12+ is required. The core dependency is Pydantic 2; backend drivers are optional extras. See the Quickstart for a first transaction.
Five-minute tour¶
import asyncio
from statepact import RetryPolicy, TransactionManager
async def main() -> None:
manager = TransactionManager() # in-memory backend
counter = manager.resource("counter", {"value": 0})
await counter.ensure(manager)
async def increment(session) -> None:
value = await session.read("value")
await asyncio.sleep(0) # concurrent agents interleave
await session.write("value", value + 1)
async def agent(index: int) -> None:
await manager.run(
f"agent-{index}",
[counter],
increment,
retry_policy=RetryPolicy(max_attempts=200),
)
await asyncio.gather(*(agent(i) for i in range(50)))
print(await counter.read("value")) # 50
await manager.close()
asyncio.run(main())
Next: Quickstart → Concepts → Isolation.
Design principles¶
- Explicit over implicit. There is no auto-commit. Exiting a session without
commit()aborts. A failed commit is never assumed applied. - Classified failures. Every exception says whether the transaction committed and whether retrying is safe.
- The backend applies state. Sessions build a commit plan; backends validate and apply atomically. The same behavioural contract runs against memory, PostgreSQL and Redis.
- Honest limits. Predicate reads, cross-database transactions and arbitrary external side effects are not magic. They are documented in Limitations.
Status¶
0.1.1 is an alpha. The engine, integration tests against real PostgreSQL/Redis, property tests and benchmarks are real and reproducible. The public API may change before 1.0.