zonix 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zonix-0.2.1/.gitignore +18 -0
- zonix-0.2.1/CHANGELOG.md +27 -0
- zonix-0.2.1/CONTRIBUTING.md +21 -0
- zonix-0.2.1/LICENSE +21 -0
- zonix-0.2.1/PKG-INFO +314 -0
- zonix-0.2.1/README.md +285 -0
- zonix-0.2.1/SECURITY.md +12 -0
- zonix-0.2.1/docs/tutorial.zh-CN.md +341 -0
- zonix-0.2.1/examples/real_provider_case.py +71 -0
- zonix-0.2.1/examples/single_agent.py +35 -0
- zonix-0.2.1/examples/workflow_team.py +38 -0
- zonix-0.2.1/logo.png +0 -0
- zonix-0.2.1/pyproject.toml +50 -0
- zonix-0.2.1/scripts/smoke_real_provider.py +419 -0
- zonix-0.2.1/src/zonix/__init__.py +44 -0
- zonix-0.2.1/src/zonix/engine.py +335 -0
- zonix-0.2.1/src/zonix/events.py +106 -0
- zonix-0.2.1/src/zonix/exceptions.py +43 -0
- zonix-0.2.1/src/zonix/hitl.py +47 -0
- zonix-0.2.1/src/zonix/memory/__init__.py +118 -0
- zonix-0.2.1/src/zonix/models/__init__.py +14 -0
- zonix-0.2.1/src/zonix/models/anthropic.py +154 -0
- zonix-0.2.1/src/zonix/models/base.py +77 -0
- zonix-0.2.1/src/zonix/models/fake.py +34 -0
- zonix-0.2.1/src/zonix/models/openai.py +222 -0
- zonix-0.2.1/src/zonix/multi/__init__.py +10 -0
- zonix-0.2.1/src/zonix/multi/team.py +96 -0
- zonix-0.2.1/src/zonix/multi/workflow.py +127 -0
- zonix-0.2.1/src/zonix/obs.py +18 -0
- zonix-0.2.1/src/zonix/py.typed +1 -0
- zonix-0.2.1/src/zonix/runtime.py +151 -0
- zonix-0.2.1/src/zonix/serialization.py +34 -0
- zonix-0.2.1/src/zonix/spec.py +171 -0
- zonix-0.2.1/src/zonix/tools.py +109 -0
- zonix-0.2.1/src/zonix/types.py +162 -0
- zonix-0.2.1/src/zonix/wire/__init__.py +3 -0
- zonix-0.2.1/src/zonix/wire/ai_sdk.py +86 -0
zonix-0.2.1/.gitignore
ADDED
zonix-0.2.1/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
- Fixed the README logo URL so PyPI can render it from GitHub.
|
|
6
|
+
- Prepared the first PyPI distribution.
|
|
7
|
+
|
|
8
|
+
## 0.2.0
|
|
9
|
+
|
|
10
|
+
- Added provider-level structured output control:
|
|
11
|
+
- OpenAI-compatible models now prefer strict JSON Schema response formats.
|
|
12
|
+
- Anthropic-compatible models now use a final-output tool with `input_schema`.
|
|
13
|
+
- Pydantic validation can request one output repair pass by default.
|
|
14
|
+
- Added Anthropic-compatible provider support.
|
|
15
|
+
- Fixed OpenAI-compatible streaming providers that send empty terminal chunks.
|
|
16
|
+
- Preserved assistant tool-call messages so tool results round-trip correctly.
|
|
17
|
+
- Added real provider examples and a full smoke script covering agents, streams,
|
|
18
|
+
tools, HITL, resume, retry, timeout, fallback, workflow, team, router, memory,
|
|
19
|
+
and wire adapters.
|
|
20
|
+
- Added a Chinese multi-chapter tutorial.
|
|
21
|
+
|
|
22
|
+
## 0.1.0
|
|
23
|
+
|
|
24
|
+
- Initial standalone repository.
|
|
25
|
+
- Added explicit `agent`, `workflow`, `team`, and `router` primitives.
|
|
26
|
+
- Added serializable run results, spans, usage, typed stream events, tool schemas,
|
|
27
|
+
memory strategies, HITL checkpoints, and an AI SDK data stream adapter.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Zonix is intentionally small and explicit. Contributions should keep these
|
|
4
|
+
constraints intact:
|
|
5
|
+
|
|
6
|
+
- Prefer typed objects over magic strings.
|
|
7
|
+
- Keep run state serializable at node boundaries.
|
|
8
|
+
- Keep `__call__`, `.run()`, and `.stream()` on the same execution path.
|
|
9
|
+
- Add new model providers through `zonix.models.BaseChatModel`.
|
|
10
|
+
- Add new frontend protocols under `zonix.wire`.
|
|
11
|
+
|
|
12
|
+
Local development:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
python -m venv .venv
|
|
16
|
+
. .venv/bin/activate
|
|
17
|
+
pip install -e ".[dev]"
|
|
18
|
+
python -m compileall src
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Do not commit generated build artifacts.
|
zonix-0.2.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zonix contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
zonix-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zonix
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Explicit, serializable AI workflow primitives inspired by pydantic-ai.
|
|
5
|
+
Project-URL: Homepage, https://github.com/zongxi1115/zonix
|
|
6
|
+
Project-URL: Repository, https://github.com/zongxi1115/zonix
|
|
7
|
+
Project-URL: Issues, https://github.com/zongxi1115/zonix/issues
|
|
8
|
+
Author-email: zongxi1115 <zxwang1234321@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,ai,llm,pydantic,workflow
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: pydantic<3,>=2.8
|
|
21
|
+
Provides-Extra: anthropic
|
|
22
|
+
Requires-Dist: anthropic>=0.45; extra == 'anthropic'
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: mypy>=1.13; extra == 'dev'
|
|
25
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
26
|
+
Provides-Extra: openai
|
|
27
|
+
Requires-Dist: openai>=1.68; extra == 'openai'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# Zonix
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
Zonix is a Python AI workflow framework with explicit agents and a serializable
|
|
35
|
+
run engine. It borrows the clarity of pydantic-ai's `Agent`, then adds first
|
|
36
|
+
class `workflow`, `team`, and `router` primitives on top of one execution model.
|
|
37
|
+
|
|
38
|
+
The core idea:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
plan = await planner("add captcha to the login page", ctx=ctx)
|
|
42
|
+
result = await planner.run("add captcha to the login page", ctx=ctx)
|
|
43
|
+
|
|
44
|
+
async for event in planner.stream("add captcha to the login page", ctx=ctx):
|
|
45
|
+
print(event)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`__call__` returns the structured output. `.run()` returns the full trace, usage,
|
|
49
|
+
messages, and checkpoint metadata. `.stream()` returns typed events that can be
|
|
50
|
+
mapped to frontend protocols such as the Vercel AI SDK data stream.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install zonix
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For local development from this repository:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install -e .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Optional model providers:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install "zonix[openai]"
|
|
68
|
+
pip install "zonix[anthropic]"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## OpenAI-compatible and Anthropic-compatible endpoints
|
|
72
|
+
|
|
73
|
+
Provider objects accept `base_url`, so OpenAI-compatible gateways and
|
|
74
|
+
Anthropic-compatible gateways stay explicit and typed:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import os
|
|
78
|
+
|
|
79
|
+
from zonix.models import Anthropic, OpenAI
|
|
80
|
+
|
|
81
|
+
openai_model = OpenAI(
|
|
82
|
+
model=os.environ["ZONIX_MODEL"],
|
|
83
|
+
api_key=os.environ["ZONIX_API_KEY"],
|
|
84
|
+
base_url=os.environ["ZONIX_BASE_URL"],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
anthropic_model = Anthropic(
|
|
88
|
+
model=os.environ["ZONIX_MODEL"],
|
|
89
|
+
api_key=os.environ["ZONIX_API_KEY"],
|
|
90
|
+
base_url=os.environ["ZONIX_BASE_URL"],
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Run the real provider example:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
export ZONIX_API_KEY="..."
|
|
98
|
+
export ZONIX_PROVIDER="openai"
|
|
99
|
+
export ZONIX_BASE_URL="https://your-openai-compatible-host/v1"
|
|
100
|
+
export ZONIX_MODEL="your-model"
|
|
101
|
+
python examples/real_provider_case.py
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Single agent
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from pydantic import BaseModel
|
|
108
|
+
|
|
109
|
+
from zonix import agent
|
|
110
|
+
from zonix.models import OpenAI
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class Plan(BaseModel):
|
|
114
|
+
goal: str
|
|
115
|
+
files: list[str]
|
|
116
|
+
steps: list[str]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
planner = (
|
|
120
|
+
agent(
|
|
121
|
+
"planner",
|
|
122
|
+
role="Plan code work",
|
|
123
|
+
model=OpenAI("gpt-5.2", temperature=0.2),
|
|
124
|
+
output=Plan,
|
|
125
|
+
)
|
|
126
|
+
.use(read_tree, search_code)
|
|
127
|
+
.prompt(
|
|
128
|
+
"Split the user request into a code plan. "
|
|
129
|
+
"Return only JSON that matches the Plan schema."
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
plan = await planner("add captcha to the login page", ctx=project_ctx)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
An agent definition keeps the important pieces in one place:
|
|
137
|
+
|
|
138
|
+
- `name` and `role` for trace readability.
|
|
139
|
+
- `model` as a typed object, not a provider string.
|
|
140
|
+
- `output` as a Pydantic model or Python type.
|
|
141
|
+
- `deps` through `ctx`.
|
|
142
|
+
- tools via `.use(...)` or `@agent.tool`.
|
|
143
|
+
- static or dynamic prompts via `.prompt(...)`.
|
|
144
|
+
|
|
145
|
+
## Tools
|
|
146
|
+
|
|
147
|
+
Tool schemas are generated from type hints and docstrings.
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
coder = agent("coder", output=Patch, deps=ProjectCtx).use(read_file)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@coder.tool(approval=True)
|
|
154
|
+
async def write_file(ctx, path: str, content: str) -> bool:
|
|
155
|
+
"""Write content to a repository file."""
|
|
156
|
+
return ctx.deps.repo.write(path, content)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
If a tool takes `ctx` as its first parameter, Zonix passes a `ToolContext` with
|
|
160
|
+
`deps`, shared usage, the current run state, and the owning agent.
|
|
161
|
+
|
|
162
|
+
## Three call levels
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
output = await planner(task, ctx=ctx)
|
|
166
|
+
run = await planner.run(task, ctx=ctx)
|
|
167
|
+
|
|
168
|
+
async for event in planner.stream(task, ctx=ctx):
|
|
169
|
+
...
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
All three calls use the same run engine. The engine owns prompt assembly, model
|
|
173
|
+
calls, tool execution, output validation, usage aggregation, spans, checkpoints,
|
|
174
|
+
and event emission.
|
|
175
|
+
|
|
176
|
+
## Workflow
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from zonix import workflow
|
|
180
|
+
|
|
181
|
+
code_flow = (
|
|
182
|
+
workflow("code_team")
|
|
183
|
+
.start(planner)
|
|
184
|
+
.then(coder)
|
|
185
|
+
.then(reviewer)
|
|
186
|
+
.build()
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
review = await code_flow.solve("add captcha to login", ctx=ctx)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`workflow` compiles ordered steps into a node. The output of one node becomes
|
|
193
|
+
the input of the next node, while `ctx`, usage, trace, scratch, and stream events
|
|
194
|
+
are automatically carried through the run.
|
|
195
|
+
|
|
196
|
+
The builder also supports `parallel`, `join`, `branch`, and `loop`:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
flow = (
|
|
200
|
+
workflow("review")
|
|
201
|
+
.start(planner)
|
|
202
|
+
.parallel(security_review, perf_review)
|
|
203
|
+
.join(merge_reviews)
|
|
204
|
+
.branch(lambda review: review.risk == "high", then=human_gate, else_=auto_apply)
|
|
205
|
+
.loop(coder, until=lambda patch: patch.tests_pass, max_iters=3)
|
|
206
|
+
.build()
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Team and router
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from zonix import router, team
|
|
214
|
+
from zonix.types import Route
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def choose(task, state) -> Route:
|
|
218
|
+
if "review" in str(task).lower():
|
|
219
|
+
return Route(next="reviewer")
|
|
220
|
+
return Route(next="coder")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
code_team = (
|
|
224
|
+
team("code_team")
|
|
225
|
+
.add(planner, coder, reviewer)
|
|
226
|
+
.route(router("rule_router", choose))
|
|
227
|
+
.build(max_steps=6)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
answer = await code_team.solve("review the auth changes", ctx=ctx)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
A router can be a rule function, another agent, or any node that returns
|
|
234
|
+
`Route(next=..., done=..., input=...)`.
|
|
235
|
+
|
|
236
|
+
## Memory
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
from zonix.memory import Session, Summarize, Vector, Window
|
|
240
|
+
|
|
241
|
+
session = Session(memory=[Window(size=20), Vector(store=my_store)])
|
|
242
|
+
assistant = agent("assistant", memory=[Summarize(over=170_000, keep=20)])
|
|
243
|
+
|
|
244
|
+
answer = await assistant("continue from last time", ctx=ctx, session=session)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Memory strategies are typed and composable. They transform prior session history
|
|
248
|
+
before the current run is assembled.
|
|
249
|
+
|
|
250
|
+
## Streaming events
|
|
251
|
+
|
|
252
|
+
Zonix streams typed Python events:
|
|
253
|
+
|
|
254
|
+
- `TextStart`, `TextDelta`, `TextEnd`
|
|
255
|
+
- `ReasoningDelta`
|
|
256
|
+
- `ToolInputStart`, `ToolInputDelta`, `ToolInputAvailable`
|
|
257
|
+
- `ToolOutputAvailable`
|
|
258
|
+
- `ApprovalRequired`
|
|
259
|
+
- `ErrorEvent`, `Finish`
|
|
260
|
+
|
|
261
|
+
Frontend protocols are adapters. For Vercel AI SDK data streams:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
from zonix.wire.ai_sdk import to_ai_sdk
|
|
265
|
+
|
|
266
|
+
async for chunk in to_ai_sdk(agent.stream(task, ctx=ctx)):
|
|
267
|
+
yield chunk
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
HTTP responses should include:
|
|
271
|
+
|
|
272
|
+
```text
|
|
273
|
+
x-vercel-ai-ui-message-stream: v1
|
|
274
|
+
content-type: text/event-stream
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Human approval and resume
|
|
278
|
+
|
|
279
|
+
Tools can pause the run before execution:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
run = await coder.run("edit the login page", ctx=ctx)
|
|
283
|
+
|
|
284
|
+
if run.paused:
|
|
285
|
+
print(run.pending)
|
|
286
|
+
run = await run.resume(approve=True)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
`run.dump()` returns a JSON-safe snapshot with output, usage, trace, messages,
|
|
290
|
+
scratch, and pending approval metadata. `CheckpointStore` can persist snapshots
|
|
291
|
+
to disk.
|
|
292
|
+
|
|
293
|
+
## Architecture
|
|
294
|
+
|
|
295
|
+
```text
|
|
296
|
+
zonix/
|
|
297
|
+
spec.py agent()/team()/workflow()/router() factories
|
|
298
|
+
engine.py serializable Run engine and Agent execution
|
|
299
|
+
runtime.py __call__/run/stream driver shared by every node
|
|
300
|
+
memory/ Window, Summarize, Vector, Session
|
|
301
|
+
multi/ Workflow, Team, Router nodes
|
|
302
|
+
hitl.py checkpoint save/load and approval keys
|
|
303
|
+
models/ complete/stream model adapters
|
|
304
|
+
wire/ event-to-wire protocol adapters
|
|
305
|
+
obs.py lightweight observability hooks
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Zonix is intentionally explicit: business code should say what it means, and
|
|
309
|
+
the run engine should make every step inspectable.
|
|
310
|
+
|
|
311
|
+
## Tutorials
|
|
312
|
+
|
|
313
|
+
- [中文多章节教程](https://github.com/zongxi1115/zonix/blob/main/docs/tutorial.zh-CN.md)
|
|
314
|
+
- [Real provider smoke script](https://github.com/zongxi1115/zonix/blob/main/scripts/smoke_real_provider.py)
|
zonix-0.2.1/README.md
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Zonix
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Zonix is a Python AI workflow framework with explicit agents and a serializable
|
|
6
|
+
run engine. It borrows the clarity of pydantic-ai's `Agent`, then adds first
|
|
7
|
+
class `workflow`, `team`, and `router` primitives on top of one execution model.
|
|
8
|
+
|
|
9
|
+
The core idea:
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
plan = await planner("add captcha to the login page", ctx=ctx)
|
|
13
|
+
result = await planner.run("add captcha to the login page", ctx=ctx)
|
|
14
|
+
|
|
15
|
+
async for event in planner.stream("add captcha to the login page", ctx=ctx):
|
|
16
|
+
print(event)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`__call__` returns the structured output. `.run()` returns the full trace, usage,
|
|
20
|
+
messages, and checkpoint metadata. `.stream()` returns typed events that can be
|
|
21
|
+
mapped to frontend protocols such as the Vercel AI SDK data stream.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install zonix
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
For local development from this repository:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install -e .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Optional model providers:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install "zonix[openai]"
|
|
39
|
+
pip install "zonix[anthropic]"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## OpenAI-compatible and Anthropic-compatible endpoints
|
|
43
|
+
|
|
44
|
+
Provider objects accept `base_url`, so OpenAI-compatible gateways and
|
|
45
|
+
Anthropic-compatible gateways stay explicit and typed:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import os
|
|
49
|
+
|
|
50
|
+
from zonix.models import Anthropic, OpenAI
|
|
51
|
+
|
|
52
|
+
openai_model = OpenAI(
|
|
53
|
+
model=os.environ["ZONIX_MODEL"],
|
|
54
|
+
api_key=os.environ["ZONIX_API_KEY"],
|
|
55
|
+
base_url=os.environ["ZONIX_BASE_URL"],
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
anthropic_model = Anthropic(
|
|
59
|
+
model=os.environ["ZONIX_MODEL"],
|
|
60
|
+
api_key=os.environ["ZONIX_API_KEY"],
|
|
61
|
+
base_url=os.environ["ZONIX_BASE_URL"],
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Run the real provider example:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
export ZONIX_API_KEY="..."
|
|
69
|
+
export ZONIX_PROVIDER="openai"
|
|
70
|
+
export ZONIX_BASE_URL="https://your-openai-compatible-host/v1"
|
|
71
|
+
export ZONIX_MODEL="your-model"
|
|
72
|
+
python examples/real_provider_case.py
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Single agent
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from pydantic import BaseModel
|
|
79
|
+
|
|
80
|
+
from zonix import agent
|
|
81
|
+
from zonix.models import OpenAI
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class Plan(BaseModel):
|
|
85
|
+
goal: str
|
|
86
|
+
files: list[str]
|
|
87
|
+
steps: list[str]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
planner = (
|
|
91
|
+
agent(
|
|
92
|
+
"planner",
|
|
93
|
+
role="Plan code work",
|
|
94
|
+
model=OpenAI("gpt-5.2", temperature=0.2),
|
|
95
|
+
output=Plan,
|
|
96
|
+
)
|
|
97
|
+
.use(read_tree, search_code)
|
|
98
|
+
.prompt(
|
|
99
|
+
"Split the user request into a code plan. "
|
|
100
|
+
"Return only JSON that matches the Plan schema."
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
plan = await planner("add captcha to the login page", ctx=project_ctx)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
An agent definition keeps the important pieces in one place:
|
|
108
|
+
|
|
109
|
+
- `name` and `role` for trace readability.
|
|
110
|
+
- `model` as a typed object, not a provider string.
|
|
111
|
+
- `output` as a Pydantic model or Python type.
|
|
112
|
+
- `deps` through `ctx`.
|
|
113
|
+
- tools via `.use(...)` or `@agent.tool`.
|
|
114
|
+
- static or dynamic prompts via `.prompt(...)`.
|
|
115
|
+
|
|
116
|
+
## Tools
|
|
117
|
+
|
|
118
|
+
Tool schemas are generated from type hints and docstrings.
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
coder = agent("coder", output=Patch, deps=ProjectCtx).use(read_file)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@coder.tool(approval=True)
|
|
125
|
+
async def write_file(ctx, path: str, content: str) -> bool:
|
|
126
|
+
"""Write content to a repository file."""
|
|
127
|
+
return ctx.deps.repo.write(path, content)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
If a tool takes `ctx` as its first parameter, Zonix passes a `ToolContext` with
|
|
131
|
+
`deps`, shared usage, the current run state, and the owning agent.
|
|
132
|
+
|
|
133
|
+
## Three call levels
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
output = await planner(task, ctx=ctx)
|
|
137
|
+
run = await planner.run(task, ctx=ctx)
|
|
138
|
+
|
|
139
|
+
async for event in planner.stream(task, ctx=ctx):
|
|
140
|
+
...
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
All three calls use the same run engine. The engine owns prompt assembly, model
|
|
144
|
+
calls, tool execution, output validation, usage aggregation, spans, checkpoints,
|
|
145
|
+
and event emission.
|
|
146
|
+
|
|
147
|
+
## Workflow
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from zonix import workflow
|
|
151
|
+
|
|
152
|
+
code_flow = (
|
|
153
|
+
workflow("code_team")
|
|
154
|
+
.start(planner)
|
|
155
|
+
.then(coder)
|
|
156
|
+
.then(reviewer)
|
|
157
|
+
.build()
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
review = await code_flow.solve("add captcha to login", ctx=ctx)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`workflow` compiles ordered steps into a node. The output of one node becomes
|
|
164
|
+
the input of the next node, while `ctx`, usage, trace, scratch, and stream events
|
|
165
|
+
are automatically carried through the run.
|
|
166
|
+
|
|
167
|
+
The builder also supports `parallel`, `join`, `branch`, and `loop`:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
flow = (
|
|
171
|
+
workflow("review")
|
|
172
|
+
.start(planner)
|
|
173
|
+
.parallel(security_review, perf_review)
|
|
174
|
+
.join(merge_reviews)
|
|
175
|
+
.branch(lambda review: review.risk == "high", then=human_gate, else_=auto_apply)
|
|
176
|
+
.loop(coder, until=lambda patch: patch.tests_pass, max_iters=3)
|
|
177
|
+
.build()
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Team and router
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from zonix import router, team
|
|
185
|
+
from zonix.types import Route
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def choose(task, state) -> Route:
|
|
189
|
+
if "review" in str(task).lower():
|
|
190
|
+
return Route(next="reviewer")
|
|
191
|
+
return Route(next="coder")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
code_team = (
|
|
195
|
+
team("code_team")
|
|
196
|
+
.add(planner, coder, reviewer)
|
|
197
|
+
.route(router("rule_router", choose))
|
|
198
|
+
.build(max_steps=6)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
answer = await code_team.solve("review the auth changes", ctx=ctx)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
A router can be a rule function, another agent, or any node that returns
|
|
205
|
+
`Route(next=..., done=..., input=...)`.
|
|
206
|
+
|
|
207
|
+
## Memory
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from zonix.memory import Session, Summarize, Vector, Window
|
|
211
|
+
|
|
212
|
+
session = Session(memory=[Window(size=20), Vector(store=my_store)])
|
|
213
|
+
assistant = agent("assistant", memory=[Summarize(over=170_000, keep=20)])
|
|
214
|
+
|
|
215
|
+
answer = await assistant("continue from last time", ctx=ctx, session=session)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Memory strategies are typed and composable. They transform prior session history
|
|
219
|
+
before the current run is assembled.
|
|
220
|
+
|
|
221
|
+
## Streaming events
|
|
222
|
+
|
|
223
|
+
Zonix streams typed Python events:
|
|
224
|
+
|
|
225
|
+
- `TextStart`, `TextDelta`, `TextEnd`
|
|
226
|
+
- `ReasoningDelta`
|
|
227
|
+
- `ToolInputStart`, `ToolInputDelta`, `ToolInputAvailable`
|
|
228
|
+
- `ToolOutputAvailable`
|
|
229
|
+
- `ApprovalRequired`
|
|
230
|
+
- `ErrorEvent`, `Finish`
|
|
231
|
+
|
|
232
|
+
Frontend protocols are adapters. For Vercel AI SDK data streams:
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
from zonix.wire.ai_sdk import to_ai_sdk
|
|
236
|
+
|
|
237
|
+
async for chunk in to_ai_sdk(agent.stream(task, ctx=ctx)):
|
|
238
|
+
yield chunk
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
HTTP responses should include:
|
|
242
|
+
|
|
243
|
+
```text
|
|
244
|
+
x-vercel-ai-ui-message-stream: v1
|
|
245
|
+
content-type: text/event-stream
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Human approval and resume
|
|
249
|
+
|
|
250
|
+
Tools can pause the run before execution:
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
run = await coder.run("edit the login page", ctx=ctx)
|
|
254
|
+
|
|
255
|
+
if run.paused:
|
|
256
|
+
print(run.pending)
|
|
257
|
+
run = await run.resume(approve=True)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
`run.dump()` returns a JSON-safe snapshot with output, usage, trace, messages,
|
|
261
|
+
scratch, and pending approval metadata. `CheckpointStore` can persist snapshots
|
|
262
|
+
to disk.
|
|
263
|
+
|
|
264
|
+
## Architecture
|
|
265
|
+
|
|
266
|
+
```text
|
|
267
|
+
zonix/
|
|
268
|
+
spec.py agent()/team()/workflow()/router() factories
|
|
269
|
+
engine.py serializable Run engine and Agent execution
|
|
270
|
+
runtime.py __call__/run/stream driver shared by every node
|
|
271
|
+
memory/ Window, Summarize, Vector, Session
|
|
272
|
+
multi/ Workflow, Team, Router nodes
|
|
273
|
+
hitl.py checkpoint save/load and approval keys
|
|
274
|
+
models/ complete/stream model adapters
|
|
275
|
+
wire/ event-to-wire protocol adapters
|
|
276
|
+
obs.py lightweight observability hooks
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Zonix is intentionally explicit: business code should say what it means, and
|
|
280
|
+
the run engine should make every step inspectable.
|
|
281
|
+
|
|
282
|
+
## Tutorials
|
|
283
|
+
|
|
284
|
+
- [中文多章节教程](https://github.com/zongxi1115/zonix/blob/main/docs/tutorial.zh-CN.md)
|
|
285
|
+
- [Real provider smoke script](https://github.com/zongxi1115/zonix/blob/main/scripts/smoke_real_provider.py)
|
zonix-0.2.1/SECURITY.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
Zonix can execute user-defined tools. Treat tool registration as code execution:
|
|
4
|
+
|
|
5
|
+
- Never register untrusted callables.
|
|
6
|
+
- Use `approval=True` for tools that mutate files, databases, cloud resources,
|
|
7
|
+
or external systems.
|
|
8
|
+
- Persist checkpoints outside public directories.
|
|
9
|
+
- Redact secrets from prompts, trace attributes, and tool outputs before sharing
|
|
10
|
+
run dumps.
|
|
11
|
+
|
|
12
|
+
Please report security issues privately to the repository owner.
|