Roots (0.1.1)
An AI-native process orchestration framework — YAML-defined directed graphs executed by a crash-safe orchestrator.
On this page
#What is Roots?
Roots is an AI-native process orchestration framework. You describe a process — an agent pipeline, an approval workflow, a fan-out/fan-in computation — as a directed graph in YAML, then run it with a stateless-between-ticks orchestrator that persists state after every node. Because state is checkpointed continuously, runs survive crashes and restarts.
The process definition is decoupled from implementation: nodes reference agents by name (a local Python callable, a remote HTTP service, or an MCP tool), and the graph routes between them using deterministic conditions or AI-driven decisions.
- YAML process graphs — 10 node types and 4 decision modes
- Crash-safe orchestrator — state persists after each node; fork/join and parallel pools checkpoint per-branch
- Process composition —
subprocessnodes call other processes;iteratornodes fan out over a list - Pluggable storage — SQLite by default, PostgreSQL for production
- Agents & events — local / HTTP / MCP agents, webhooks, and
wait_forsubscriptions - Typed end-to-end — Pydantic v2 models, ships
py.typed, strictpyright
#Installation
pip install rootsflow # or: uv pip install rootsflow
The distribution is named rootsflow; the import package is roots:
from roots import Roots, SqliteBackend
Requires Python 3.12+.
#Quick Start
#1. Define a process (echo.yaml)
id: echo
name: Echo Process
version: "1.0.0"
nodes:
- id: greet
type: agent
label: Greet
config:
agent: echo_agent
output_key: greeting
- id: done
type: end
label: Done
config:
status: completed
edges:
- from: greet
to: done
entry_point: greet
#2. Run it from Python
import asyncio
from roots import Roots, SqliteBackend
async def echo_agent(work_item_state: dict) -> dict:
return {"message": f"Hello, {work_item_state.get('name', 'world')}!"}
async def main() -> None:
backend = SqliteBackend("roots.db")
await backend.initialize()
async with Roots(storage=backend) as app:
await app.register_agent("echo_agent", echo_agent)
await app.load_process("echo.yaml")
run, _event = await app.start_and_wait("echo", {"name": "Roots"})
print(run.status, run.work_item_state)
asyncio.run(main())
#3. Or use the CLI
roots validate echo.yaml # validate a process definition
roots run echo.yaml --work-item '{"name": "Roots"}'
roots serve # start the HTTP API on 127.0.0.1:8000
roots pack ./echo.yaml -o echo.root # bundle into a portable .root archive
#Node Types
| Type | Purpose |
|---|---|
agent | Invoke a single agent (local / HTTP / MCP) |
agent_pool | Run several agents in parallel or sequence, then merge or vote |
decision | Route on conditions — deterministic or AI-driven |
checkpoint | Pause for human approval |
fork / join | Split into parallel branches and recombine (crash-safe) |
emit | Emit a custom event |
iterator | Fan a subprocess out over a list (for_each) |
subprocess | Call another process as a child run |
end | Terminate the run with a status |
Decision modes: deterministic, ai_bounded, ai_checkpoint, ai_autonomous.
#How it works
A tick-based orchestrator advances each run one node at a time, persisting state to
the StorageBackend after every step. Each node declares an output_key; its result is
accumulated into the run’s state for downstream nodes to read — an implicit data pipeline
through the graph. Expression conditions are evaluated with simpleeval (no eval/exec),
and all models use Pydantic v2.
For the full guide — node reference, agent contracts, packaging, and the HTTP API — see the Integration Guide on GitHub.
#Security note
Roots is beta software. The HTTP API has no authentication by default and binds
to 127.0.0.1. Set ROOTS_API_KEY to require an X-API-Key header, and do not expose
roots serve to an untrusted network without your own authentication layer. Process
definitions, event sinks, and MCP command-agents are treated as trusted input.
#Links
#License
MIT License