Zonix
Call simply. Chain deeply. Trace everything.
Zonix is a Python agent and workflow framework. It borrows the clarity of
pydantic-ai's Agent, then adds workflow, team, and router primitives on
top of one shared execution model. Agents, workflows, and teams are all nodes
with the same __call__ / run / stream surface, and they share trace, usage,
messages, and approval state.
The design goal is that a beginner can call one object and get a useful answer, while an advanced user can turn on reasoning, usage accounting, raw provider payloads, graph export, human approval, and frontend streaming without changing the shape of their business code.
Features
- One execution model.
await node(task)returns the output,node.run(task)returns the fullRunResult,node.stream(task)yields typed events. Agents, workflows, and teams all support the same three. - Typed structured output. Set
output=SomeModeland get a validated instance back, with automatic repair rounds when the model returns bad JSON. - Tools from type hints. Schemas are generated from signatures and
docstrings. Optional
ToolContextinjection, parallel execution, error capture, and middleware interception. - Human approval built in. Mark a tool
approval=Trueandrun()returns a paused result you canresume(), or register an approver callback and keep the run going. - Approval while streaming. Pass
approval=tostream()and a pause is resolved by your handler mid-flight — the event stream keeps flowing instead of being cancelled. - Composable orchestration.
workflowgivesthen/parallel/join/branch/loop, plus plain functions as steps viamap;teamgives router-driven dispatch. Both export Mermaid, DOT, SVG, PNG, or PDF graphs, and both can share message context across members. - Resumable workflows.
workflow(...).checkpoint(store)persists each step's result; rerun with the samerun_idand finished steps are skipped.FileCheckpointStoreandMemoryCheckpointStoreship in the box, or implement theCheckpointerprotocol yourself. - Explicit cancellation. Every entry point takes
cancel=(anasyncio.Event, or anything withis_set()), checked between workflow steps, team turns, model calls, and tool calls. - Provider-neutral adapters. OpenAI (Chat and Responses), Anthropic, and
Gemini, plus offline
Echo/StaticModel/ScriptedModelfor tests. Any OpenAI-compatible endpoint works by settingbase_url. - Inspectable by default.
RunResultkeeps the span tree, usage, messages, and the raw upstream request and response for every model call. - Sync facade.
call_sync,run_sync,stream_syncfor scripts, CLIs, and notebooks — including resumable approvals.
Install
pip install zonix
Optional provider extras:
pip install "zonix[openai]"
pip install "zonix[anthropic]"
pip install "zonix[gemini]"
pip install "zonix[viz]" # image export for workflow/team graphs
Requires Python 3.11+. For local development from this repository: pip install -e .
60 seconds
import asyncio
import os
from pydantic import BaseModel
from zonix import agent
from zonix.models import OpenAI
class Plan(BaseModel):
goal: str
files: list[str]
steps: list[str]
planner = agent(
"planner",
role="Plan code work",
model=OpenAI("gpt-5.5", api_key=os.environ["OPENAI_API_KEY"]),
output=Plan,
)
@planner.tool
def read_tree(path: str) -> list[str]:
"""List files under a repository path."""
return sorted(os.listdir(path))
async def main() -> None:
plan = await planner("add captcha to the login page")
print(plan.goal, plan.files)
result = await planner.run("add captcha to the login page")
print(result.usage.total_tokens)
print(result.model_calls[-1].raw_response)
async for event in planner.stream("add captcha to the login page"):
print(event)
asyncio.run(main())
Any OpenAI-compatible endpoint works with the same adapter — only the model name
and base_url change:
from zonix.models import OpenAI
deepseek = OpenAI(
model="deepseek-chat",
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com/v1",
)
Streaming chat requests automatically send stream_options={"include_usage": true},
so token counts are complete even on streamed runs.
Multi-agent
from zonix import router, team, workflow
from zonix.types import Route
# Fixed pipeline: output of one step feeds the next.
flow = (
workflow("review")
.start(planner)
.parallel(security_review, perf_review)
.join(merge_reviews)
.branch(lambda r: r.risk == "high", then=human_gate, else_=auto_apply)
.build()
)
review = await flow("audit the auth changes", ctx=ctx)
print(flow.to_mermaid())
# Router-driven dispatch: the router picks the next node each step.
def choose(task, state) -> Route:
if isinstance(task, Review):
return Route(done=True)
return Route(next="reviewer" if "review" in str(task).lower() else "coder")
code_team = (
team("code_team")
.add(planner, coder, reviewer)
.route(router("rule_router", choose))
.build(max_steps=6)
)
answer = await code_team("review the auth changes", ctx=ctx)
A router can be a rule function, another agent, or any node that returns
Route(next=..., done=..., input=...). Workflows and teams are nodes themselves,
so they nest freely.
Long pipelines can checkpoint each step and resume where they stopped:
from zonix import FileCheckpointStore
flow = (
workflow("review")
.start(planner)
.map(lambda plan: plan.model_dump()) # plain functions are steps too
.then(coder)
.checkpoint(FileCheckpointStore("./.checkpoints"))
.share_context() # each step sees the ones before it
.build()
)
await flow("audit the auth changes", run_id="job-42") # rerun skips finished steps
Checkpointed values round-trip as JSON, so a step that returned a BaseModel
replays as a dict — keep plain jsonable values flowing between steps, or
re-validate at the start of the next one.
Human approval
result = await coder.run("edit the login page", ctx=ctx)
if result.paused:
print(result.pending.tool, result.pending.input)
result = await result.resume(approve=True)
Or skip the pause entirely by passing an approver:
result = await coder.run("edit the login page", approval=lambda pending: True)
Paused results hold a live continuation. Release them with await result.cancel()
(or result.close() for run_sync) if you will not resume.
Streaming takes the same handler, and the stream survives the pause:
async for event in coder.stream("edit the login page", approval=my_handler):
...
Without a handler a streamed run emits ApprovalRequired and then cancels.
Cancellation
cancel = asyncio.Event()
task = asyncio.create_task(flow.run("long job", cancel=cancel))
cancel.set() # cooperative: takes effect at the next checkpoint
Tracing
Zonix ships vendor-neutral tracing hooks with no collector dependency. Register a
SpanProcessor once, then override per run:
from zonix import TraceOptions, configure_tracing
configure_tracing(my_processor, defaults=TraceOptions(enabled=True, project="my-app"))
result = await planner.run(task, trace=TraceOptions(tags=["dev"], metadata={"user_id": "u1"}))
async for event in planner.stream(task, trace=TraceOptions(tags=["ui"])):
...
The span tree covers workflows, teams, agents, routers, model calls, and tool
calls. result.trace stays available even when export is disabled. The separate
zonix-observe package provides a local collector, storage, and browser UI.
Architecture
zonix/
spec.py agent()/team()/workflow()/router() factories
engine.py agent model and tool execution loop
runtime.py __call__/run/stream driver shared by every node
types.py Message, Usage, Span, RunState, RunResult, Route
tools.py tool definitions, ToolContext, middleware results
graph.py graph specs, Mermaid, DOT, and image export
checkpoint.py Checkpointer protocol, file and in-memory stores
memory/ Window, Summarize, Vector, Session
multi/ Workflow, Team, Router nodes
models/ OpenAI, Anthropic, Gemini, offline adapters
hitl.py approval keys and snapshot persistence
tracing.py vendor-neutral span lifecycle and processor hooks
wire/ event-to-wire protocol adapters (Vercel AI SDK)
Documentation
License
MIT
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 zonix-0.5.0.tar.gz.
File metadata
- Download URL: zonix-0.5.0.tar.gz
- Upload date:
- Size: 1.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f11c5bcbe1e07bbe000562c2b8086b426baf5ed12d4f0b628c71de90d0b00391
|
|
| MD5 |
b16bfee77faf39a9874eab6fcbf32b4c
|
|
| BLAKE2b-256 |
0f2f099f44ad35d6555f0695253819811bccaf7a129c5bfe05d8df7f1613e009
|
Provenance
The following attestation bundles were made for zonix-0.5.0.tar.gz:
Publisher:
publish.yml on zongxi1115/zonix
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zonix-0.5.0.tar.gz -
Subject digest:
f11c5bcbe1e07bbe000562c2b8086b426baf5ed12d4f0b628c71de90d0b00391 - Sigstore transparency entry: 2467559613
- Sigstore integration time:
-
Permalink:
zongxi1115/zonix@4f85804dcf719276b0269abcf8ad5951e2ae6f75 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/zongxi1115
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4f85804dcf719276b0269abcf8ad5951e2ae6f75 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zonix-0.5.0-py3-none-any.whl.
File metadata
- Download URL: zonix-0.5.0-py3-none-any.whl
- Upload date:
- Size: 61.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3134416bd19645c45f2996224e9a09cb662c8d5f2bdf7abd3ea3d9e87e5f7a88
|
|
| MD5 |
c612f676a16f05bdb5a27cb6702890f4
|
|
| BLAKE2b-256 |
f92d397a7411bdaa2da0145ee90356c8f8722bb19ea9e725be89d83d721310e0
|
Provenance
The following attestation bundles were made for zonix-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on zongxi1115/zonix
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zonix-0.5.0-py3-none-any.whl -
Subject digest:
3134416bd19645c45f2996224e9a09cb662c8d5f2bdf7abd3ea3d9e87e5f7a88 - Sigstore transparency entry: 2467559628
- Sigstore integration time:
-
Permalink:
zongxi1115/zonix@4f85804dcf719276b0269abcf8ad5951e2ae6f75 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/zongxi1115
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4f85804dcf719276b0269abcf8ad5951e2ae6f75 -
Trigger Event:
push
-
Statement type: