zonix 0.2.2__tar.gz → 0.3.2__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.2 → zonix-0.3.2}/CHANGELOG.md +24 -0
- {zonix-0.2.2 → zonix-0.3.2}/PKG-INFO +134 -4
- {zonix-0.2.2 → zonix-0.3.2}/README.md +129 -3
- zonix-0.3.2/docs/tutorial.zh-CN.md +880 -0
- {zonix-0.2.2 → zonix-0.3.2}/pyproject.toml +3 -1
- {zonix-0.2.2 → zonix-0.3.2}/scripts/smoke_real_provider.py +0 -3
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/__init__.py +28 -0
- zonix-0.3.2/src/zonix/content.py +109 -0
- zonix-0.3.2/src/zonix/engine.py +696 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/events.py +3 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/exceptions.py +0 -4
- zonix-0.3.2/src/zonix/graph.py +112 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/hitl.py +10 -1
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/memory/__init__.py +5 -2
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/models/__init__.py +2 -0
- zonix-0.3.2/src/zonix/models/anthropic.py +451 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/models/base.py +11 -1
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/models/fake.py +2 -1
- zonix-0.3.2/src/zonix/models/gemini.py +351 -0
- zonix-0.3.2/src/zonix/models/openai.py +770 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/multi/team.py +103 -6
- zonix-0.3.2/src/zonix/multi/workflow.py +300 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/obs.py +0 -1
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/runtime.py +49 -7
- zonix-0.3.2/src/zonix/spec.py +331 -0
- zonix-0.3.2/src/zonix/sync.py +67 -0
- zonix-0.3.2/src/zonix/tools.py +261 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/types.py +98 -9
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/wire/ai_sdk.py +5 -1
- zonix-0.2.2/docs/tutorial.zh-CN.md +0 -372
- zonix-0.2.2/src/zonix/engine.py +0 -336
- zonix-0.2.2/src/zonix/models/anthropic.py +0 -154
- zonix-0.2.2/src/zonix/models/openai.py +0 -222
- zonix-0.2.2/src/zonix/multi/workflow.py +0 -151
- zonix-0.2.2/src/zonix/spec.py +0 -194
- zonix-0.2.2/src/zonix/tools.py +0 -109
- {zonix-0.2.2 → zonix-0.3.2}/.gitignore +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/CONTRIBUTING.md +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/LICENSE +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/SECURITY.md +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/examples/real_provider_case.py +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/examples/single_agent.py +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/examples/workflow_team.py +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/logo.png +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/multi/__init__.py +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/py.typed +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/serialization.py +0 -0
- {zonix-0.2.2 → zonix-0.3.2}/src/zonix/wire/__init__.py +0 -0
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.2
|
|
4
|
+
|
|
5
|
+
- Kept approved tool input overrides in sync with `ToolContext.call.input`.
|
|
6
|
+
- Stored workflow scratch outputs by node name instead of class name to avoid
|
|
7
|
+
overwriting repeated agent steps.
|
|
8
|
+
- Treated typed first parameters such as `ctx: str` as normal tool inputs while
|
|
9
|
+
preserving untyped `ctx`/`context` as framework context parameters.
|
|
10
|
+
|
|
11
|
+
## 0.3.0
|
|
12
|
+
|
|
13
|
+
- Added model call inspection on `RunResult`, including raw upstream requests,
|
|
14
|
+
raw upstream responses, provider status, finish reasons, and per-call usage.
|
|
15
|
+
- Expanded usage accounting with cached, cache creation, cache read, reasoning,
|
|
16
|
+
and thinking token fields.
|
|
17
|
+
- Added chainable OpenAI Responses API controls for reasoning effort, reasoning
|
|
18
|
+
summaries, verbosity, output limits, previous responses, and includes.
|
|
19
|
+
- Added Anthropic thinking, adaptive effort, service tier, metadata, native
|
|
20
|
+
thinking stream handling, and richer usage details.
|
|
21
|
+
- Added a Gemini adapter with thinking budget, thought summaries, JSON output,
|
|
22
|
+
safety settings, tool config, and cached-content controls.
|
|
23
|
+
- Added workflow and team graph exports as Mermaid, DOT, SVG, PNG, or PDF.
|
|
24
|
+
- Updated documentation around Zonix's simple-first, chainable, traceable
|
|
25
|
+
design style and OpenAI-compatible providers such as DeepSeek.
|
|
26
|
+
|
|
3
27
|
## 0.2.2
|
|
4
28
|
|
|
5
29
|
- Added first-class manually supplied `message_history` for agents, workflows,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zonix
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Explicit, serializable AI workflow primitives inspired by pydantic-ai.
|
|
5
5
|
Project-URL: Homepage, https://github.com/zongxi1115/zonix
|
|
6
6
|
Project-URL: Repository, https://github.com/zongxi1115/zonix
|
|
@@ -23,18 +23,39 @@ Requires-Dist: anthropic>=0.45; extra == 'anthropic'
|
|
|
23
23
|
Provides-Extra: dev
|
|
24
24
|
Requires-Dist: mypy>=1.13; extra == 'dev'
|
|
25
25
|
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
26
|
+
Provides-Extra: gemini
|
|
27
|
+
Requires-Dist: google-genai>=1.0; extra == 'gemini'
|
|
26
28
|
Provides-Extra: openai
|
|
27
29
|
Requires-Dist: openai>=1.68; extra == 'openai'
|
|
30
|
+
Provides-Extra: viz
|
|
31
|
+
Requires-Dist: graphviz>=0.20; extra == 'viz'
|
|
28
32
|
Description-Content-Type: text/markdown
|
|
29
33
|
|
|
30
34
|
# Zonix
|
|
31
35
|
|
|
32
36
|

|
|
33
37
|
|
|
38
|
+
<p align="center"><strong>Call simply. Chain deeply. Trace everything.</strong></p>
|
|
39
|
+
|
|
34
40
|
Zonix is a Python AI workflow framework with explicit agents and a serializable
|
|
35
41
|
run engine. It borrows the clarity of pydantic-ai's `Agent`, then adds first
|
|
36
42
|
class `workflow`, `team`, and `router` primitives on top of one execution model.
|
|
37
43
|
|
|
44
|
+
The taste is deliberately practical: a beginner should be able to call one
|
|
45
|
+
object and get a useful answer, while an advanced user can turn on reasoning,
|
|
46
|
+
usage accounting, raw provider responses, graph export, and frontend streaming
|
|
47
|
+
without changing the shape of their business code.
|
|
48
|
+
|
|
49
|
+
Zonix has a few deliberately personal design choices:
|
|
50
|
+
|
|
51
|
+
- Simple first: `await agent(task)` is the happy path.
|
|
52
|
+
- Chainable when needed: reasoning, thinking budgets, output limits, and
|
|
53
|
+
provider quirks are small method calls instead of scattered dictionaries.
|
|
54
|
+
- Inspectable by default: `.run()` keeps trace, usage, model calls, messages,
|
|
55
|
+
raw upstream payloads, and checkpoint state together.
|
|
56
|
+
- Flow should be visible: workflow and team graphs can be exported as Mermaid,
|
|
57
|
+
DOT, SVG, PNG, or PDF.
|
|
58
|
+
|
|
38
59
|
The core idea:
|
|
39
60
|
|
|
40
61
|
```python
|
|
@@ -46,8 +67,9 @@ async for event in planner.stream("add captcha to the login page", ctx=ctx):
|
|
|
46
67
|
```
|
|
47
68
|
|
|
48
69
|
`__call__` returns the structured output. `.run()` returns the full trace, usage,
|
|
49
|
-
messages,
|
|
50
|
-
|
|
70
|
+
messages, model calls, raw upstream responses, and checkpoint metadata.
|
|
71
|
+
`.stream()` returns typed events that can be mapped to frontend protocols such
|
|
72
|
+
as the Vercel AI SDK data stream.
|
|
51
73
|
|
|
52
74
|
## Install
|
|
53
75
|
|
|
@@ -66,6 +88,8 @@ Optional model providers:
|
|
|
66
88
|
```bash
|
|
67
89
|
pip install "zonix[openai]"
|
|
68
90
|
pip install "zonix[anthropic]"
|
|
91
|
+
pip install "zonix[gemini]"
|
|
92
|
+
pip install "zonix[viz]"
|
|
69
93
|
```
|
|
70
94
|
|
|
71
95
|
## OpenAI-compatible and Anthropic-compatible endpoints
|
|
@@ -91,6 +115,50 @@ anthropic_model = Anthropic(
|
|
|
91
115
|
)
|
|
92
116
|
```
|
|
93
117
|
|
|
118
|
+
For OpenAI-compatible providers such as DeepSeek, keep the same adapter and only
|
|
119
|
+
change the endpoint and model:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
deepseek_model = OpenAI(
|
|
123
|
+
model="deepseek-v4-flash",
|
|
124
|
+
api_key=os.environ["DEEPSEEK_API_KEY"],
|
|
125
|
+
base_url="https://api.deepseek.com/v1",
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Newer model-specific controls stay chainable, so the common path remains
|
|
130
|
+
readable:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from zonix.models import Anthropic, Gemini, OpenAI
|
|
134
|
+
|
|
135
|
+
planner_model = (
|
|
136
|
+
OpenAI("gpt-5.5")
|
|
137
|
+
.responses()
|
|
138
|
+
.reasoning("low", summary="auto")
|
|
139
|
+
.verbosity("low")
|
|
140
|
+
.max_output(8000)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
claude_model = (
|
|
144
|
+
Anthropic("claude-sonnet-4-6")
|
|
145
|
+
.thinking("adaptive")
|
|
146
|
+
.effort("medium")
|
|
147
|
+
.max_output(16000)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
gemini_model = (
|
|
151
|
+
Gemini("gemini-3-pro")
|
|
152
|
+
.thinking_budget(4096)
|
|
153
|
+
.include_thoughts()
|
|
154
|
+
.max_output(8000)
|
|
155
|
+
)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
OpenAI's Responses API is opt-in with `.responses()` so existing
|
|
159
|
+
OpenAI-compatible gateways that only implement Chat Completions can keep using
|
|
160
|
+
the default adapter path.
|
|
161
|
+
|
|
94
162
|
Run the real provider example:
|
|
95
163
|
|
|
96
164
|
```bash
|
|
@@ -120,7 +188,7 @@ planner = (
|
|
|
120
188
|
agent(
|
|
121
189
|
"planner",
|
|
122
190
|
role="Plan code work",
|
|
123
|
-
model=OpenAI("gpt-5.
|
|
191
|
+
model=OpenAI("gpt-5.5", temperature=0.2).responses().reasoning("low"),
|
|
124
192
|
output=Plan,
|
|
125
193
|
)
|
|
126
194
|
.use(read_tree, search_code)
|
|
@@ -159,6 +227,25 @@ async def write_file(ctx, path: str, content: str) -> bool:
|
|
|
159
227
|
If a tool takes `ctx` as its first parameter, Zonix passes a `ToolContext` with
|
|
160
228
|
`deps`, shared usage, the current run state, and the owning agent.
|
|
161
229
|
|
|
230
|
+
Tools can require approval before execution. Register the approval handler with
|
|
231
|
+
the tools that share the same approval flow, so the run loop does not need a
|
|
232
|
+
large central router.
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
def review_tool_call(pending):
|
|
236
|
+
print(pending.tool, pending.input)
|
|
237
|
+
return True
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
assistant = agent("assistant").use(send_email, approval=review_tool_call)
|
|
241
|
+
run = await assistant.run("send the draft")
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Middleware is for more involved interception, such as blocking a call, rewriting
|
|
245
|
+
parsed input, or requiring approval only under specific runtime conditions.
|
|
246
|
+
Without a registered approval handler, `run()` returns a paused `RunResult` that
|
|
247
|
+
can be resumed later from a UI, queue, or separate process.
|
|
248
|
+
|
|
162
249
|
## Three call levels
|
|
163
250
|
|
|
164
251
|
```python
|
|
@@ -173,6 +260,34 @@ All three calls use the same run engine. The engine owns prompt assembly, model
|
|
|
173
260
|
calls, tool execution, output validation, usage aggregation, spans, checkpoints,
|
|
174
261
|
and event emission.
|
|
175
262
|
|
|
263
|
+
Synchronous callers can use the explicit blocking facade:
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
output = planner.call_sync(task, ctx=ctx)
|
|
267
|
+
run = planner.run_sync(task, ctx=ctx)
|
|
268
|
+
|
|
269
|
+
for event in planner.stream_sync(task, ctx=ctx):
|
|
270
|
+
print(event)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The async engine remains the source of truth; the sync facade bridges it for
|
|
274
|
+
scripts, CLIs, notebooks, and other non-async entry points.
|
|
275
|
+
|
|
276
|
+
`.run()` is the inspection layer:
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
run = await planner.run(task, ctx=ctx)
|
|
280
|
+
|
|
281
|
+
print(run.output)
|
|
282
|
+
print(run.usage.reasoning_tokens)
|
|
283
|
+
print(run.model_calls[-1].raw_request)
|
|
284
|
+
print(run.model_calls[-1].raw_response)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
That raw-response escape hatch is intentional. Zonix keeps the beginner API
|
|
288
|
+
small, but it should never hide the provider payload when you need to debug a
|
|
289
|
+
token spike, a refusal, a tool-call mismatch, or a gateway quirk.
|
|
290
|
+
|
|
176
291
|
## Manual message history
|
|
177
292
|
|
|
178
293
|
You can pass an explicit prior transcript when you want to replay or continue
|
|
@@ -237,6 +352,14 @@ flow = (
|
|
|
237
352
|
)
|
|
238
353
|
```
|
|
239
354
|
|
|
355
|
+
Workflow graphs can be exported for review or documentation:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
flow.graph().save("review.mmd")
|
|
359
|
+
flow.graph().save("review.png") # requires zonix[viz] and Graphviz
|
|
360
|
+
print(flow.to_mermaid())
|
|
361
|
+
```
|
|
362
|
+
|
|
240
363
|
## Team and router
|
|
241
364
|
|
|
242
365
|
```python
|
|
@@ -263,6 +386,12 @@ answer = await code_team.solve("review the auth changes", ctx=ctx)
|
|
|
263
386
|
A router can be a rule function, another agent, or any node that returns
|
|
264
387
|
`Route(next=..., done=..., input=...)`.
|
|
265
388
|
|
|
389
|
+
Teams expose the same graph API:
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
code_team.graph().save("code_team.svg")
|
|
393
|
+
```
|
|
394
|
+
|
|
266
395
|
## Memory
|
|
267
396
|
|
|
268
397
|
```python
|
|
@@ -327,6 +456,7 @@ zonix/
|
|
|
327
456
|
spec.py agent()/team()/workflow()/router() factories
|
|
328
457
|
engine.py serializable Run engine and Agent execution
|
|
329
458
|
runtime.py __call__/run/stream driver shared by every node
|
|
459
|
+
graph.py workflow/team graph specs, Mermaid, DOT, and image export
|
|
330
460
|
memory/ Window, Summarize, Vector, Session
|
|
331
461
|
multi/ Workflow, Team, Router nodes
|
|
332
462
|
hitl.py checkpoint save/load and approval keys
|
|
@@ -2,10 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
+
<p align="center"><strong>Call simply. Chain deeply. Trace everything.</strong></p>
|
|
6
|
+
|
|
5
7
|
Zonix is a Python AI workflow framework with explicit agents and a serializable
|
|
6
8
|
run engine. It borrows the clarity of pydantic-ai's `Agent`, then adds first
|
|
7
9
|
class `workflow`, `team`, and `router` primitives on top of one execution model.
|
|
8
10
|
|
|
11
|
+
The taste is deliberately practical: a beginner should be able to call one
|
|
12
|
+
object and get a useful answer, while an advanced user can turn on reasoning,
|
|
13
|
+
usage accounting, raw provider responses, graph export, and frontend streaming
|
|
14
|
+
without changing the shape of their business code.
|
|
15
|
+
|
|
16
|
+
Zonix has a few deliberately personal design choices:
|
|
17
|
+
|
|
18
|
+
- Simple first: `await agent(task)` is the happy path.
|
|
19
|
+
- Chainable when needed: reasoning, thinking budgets, output limits, and
|
|
20
|
+
provider quirks are small method calls instead of scattered dictionaries.
|
|
21
|
+
- Inspectable by default: `.run()` keeps trace, usage, model calls, messages,
|
|
22
|
+
raw upstream payloads, and checkpoint state together.
|
|
23
|
+
- Flow should be visible: workflow and team graphs can be exported as Mermaid,
|
|
24
|
+
DOT, SVG, PNG, or PDF.
|
|
25
|
+
|
|
9
26
|
The core idea:
|
|
10
27
|
|
|
11
28
|
```python
|
|
@@ -17,8 +34,9 @@ async for event in planner.stream("add captcha to the login page", ctx=ctx):
|
|
|
17
34
|
```
|
|
18
35
|
|
|
19
36
|
`__call__` returns the structured output. `.run()` returns the full trace, usage,
|
|
20
|
-
messages,
|
|
21
|
-
|
|
37
|
+
messages, model calls, raw upstream responses, and checkpoint metadata.
|
|
38
|
+
`.stream()` returns typed events that can be mapped to frontend protocols such
|
|
39
|
+
as the Vercel AI SDK data stream.
|
|
22
40
|
|
|
23
41
|
## Install
|
|
24
42
|
|
|
@@ -37,6 +55,8 @@ Optional model providers:
|
|
|
37
55
|
```bash
|
|
38
56
|
pip install "zonix[openai]"
|
|
39
57
|
pip install "zonix[anthropic]"
|
|
58
|
+
pip install "zonix[gemini]"
|
|
59
|
+
pip install "zonix[viz]"
|
|
40
60
|
```
|
|
41
61
|
|
|
42
62
|
## OpenAI-compatible and Anthropic-compatible endpoints
|
|
@@ -62,6 +82,50 @@ anthropic_model = Anthropic(
|
|
|
62
82
|
)
|
|
63
83
|
```
|
|
64
84
|
|
|
85
|
+
For OpenAI-compatible providers such as DeepSeek, keep the same adapter and only
|
|
86
|
+
change the endpoint and model:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
deepseek_model = OpenAI(
|
|
90
|
+
model="deepseek-v4-flash",
|
|
91
|
+
api_key=os.environ["DEEPSEEK_API_KEY"],
|
|
92
|
+
base_url="https://api.deepseek.com/v1",
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Newer model-specific controls stay chainable, so the common path remains
|
|
97
|
+
readable:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from zonix.models import Anthropic, Gemini, OpenAI
|
|
101
|
+
|
|
102
|
+
planner_model = (
|
|
103
|
+
OpenAI("gpt-5.5")
|
|
104
|
+
.responses()
|
|
105
|
+
.reasoning("low", summary="auto")
|
|
106
|
+
.verbosity("low")
|
|
107
|
+
.max_output(8000)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
claude_model = (
|
|
111
|
+
Anthropic("claude-sonnet-4-6")
|
|
112
|
+
.thinking("adaptive")
|
|
113
|
+
.effort("medium")
|
|
114
|
+
.max_output(16000)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
gemini_model = (
|
|
118
|
+
Gemini("gemini-3-pro")
|
|
119
|
+
.thinking_budget(4096)
|
|
120
|
+
.include_thoughts()
|
|
121
|
+
.max_output(8000)
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
OpenAI's Responses API is opt-in with `.responses()` so existing
|
|
126
|
+
OpenAI-compatible gateways that only implement Chat Completions can keep using
|
|
127
|
+
the default adapter path.
|
|
128
|
+
|
|
65
129
|
Run the real provider example:
|
|
66
130
|
|
|
67
131
|
```bash
|
|
@@ -91,7 +155,7 @@ planner = (
|
|
|
91
155
|
agent(
|
|
92
156
|
"planner",
|
|
93
157
|
role="Plan code work",
|
|
94
|
-
model=OpenAI("gpt-5.
|
|
158
|
+
model=OpenAI("gpt-5.5", temperature=0.2).responses().reasoning("low"),
|
|
95
159
|
output=Plan,
|
|
96
160
|
)
|
|
97
161
|
.use(read_tree, search_code)
|
|
@@ -130,6 +194,25 @@ async def write_file(ctx, path: str, content: str) -> bool:
|
|
|
130
194
|
If a tool takes `ctx` as its first parameter, Zonix passes a `ToolContext` with
|
|
131
195
|
`deps`, shared usage, the current run state, and the owning agent.
|
|
132
196
|
|
|
197
|
+
Tools can require approval before execution. Register the approval handler with
|
|
198
|
+
the tools that share the same approval flow, so the run loop does not need a
|
|
199
|
+
large central router.
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
def review_tool_call(pending):
|
|
203
|
+
print(pending.tool, pending.input)
|
|
204
|
+
return True
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
assistant = agent("assistant").use(send_email, approval=review_tool_call)
|
|
208
|
+
run = await assistant.run("send the draft")
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Middleware is for more involved interception, such as blocking a call, rewriting
|
|
212
|
+
parsed input, or requiring approval only under specific runtime conditions.
|
|
213
|
+
Without a registered approval handler, `run()` returns a paused `RunResult` that
|
|
214
|
+
can be resumed later from a UI, queue, or separate process.
|
|
215
|
+
|
|
133
216
|
## Three call levels
|
|
134
217
|
|
|
135
218
|
```python
|
|
@@ -144,6 +227,34 @@ All three calls use the same run engine. The engine owns prompt assembly, model
|
|
|
144
227
|
calls, tool execution, output validation, usage aggregation, spans, checkpoints,
|
|
145
228
|
and event emission.
|
|
146
229
|
|
|
230
|
+
Synchronous callers can use the explicit blocking facade:
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
output = planner.call_sync(task, ctx=ctx)
|
|
234
|
+
run = planner.run_sync(task, ctx=ctx)
|
|
235
|
+
|
|
236
|
+
for event in planner.stream_sync(task, ctx=ctx):
|
|
237
|
+
print(event)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The async engine remains the source of truth; the sync facade bridges it for
|
|
241
|
+
scripts, CLIs, notebooks, and other non-async entry points.
|
|
242
|
+
|
|
243
|
+
`.run()` is the inspection layer:
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
run = await planner.run(task, ctx=ctx)
|
|
247
|
+
|
|
248
|
+
print(run.output)
|
|
249
|
+
print(run.usage.reasoning_tokens)
|
|
250
|
+
print(run.model_calls[-1].raw_request)
|
|
251
|
+
print(run.model_calls[-1].raw_response)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
That raw-response escape hatch is intentional. Zonix keeps the beginner API
|
|
255
|
+
small, but it should never hide the provider payload when you need to debug a
|
|
256
|
+
token spike, a refusal, a tool-call mismatch, or a gateway quirk.
|
|
257
|
+
|
|
147
258
|
## Manual message history
|
|
148
259
|
|
|
149
260
|
You can pass an explicit prior transcript when you want to replay or continue
|
|
@@ -208,6 +319,14 @@ flow = (
|
|
|
208
319
|
)
|
|
209
320
|
```
|
|
210
321
|
|
|
322
|
+
Workflow graphs can be exported for review or documentation:
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
flow.graph().save("review.mmd")
|
|
326
|
+
flow.graph().save("review.png") # requires zonix[viz] and Graphviz
|
|
327
|
+
print(flow.to_mermaid())
|
|
328
|
+
```
|
|
329
|
+
|
|
211
330
|
## Team and router
|
|
212
331
|
|
|
213
332
|
```python
|
|
@@ -234,6 +353,12 @@ answer = await code_team.solve("review the auth changes", ctx=ctx)
|
|
|
234
353
|
A router can be a rule function, another agent, or any node that returns
|
|
235
354
|
`Route(next=..., done=..., input=...)`.
|
|
236
355
|
|
|
356
|
+
Teams expose the same graph API:
|
|
357
|
+
|
|
358
|
+
```python
|
|
359
|
+
code_team.graph().save("code_team.svg")
|
|
360
|
+
```
|
|
361
|
+
|
|
237
362
|
## Memory
|
|
238
363
|
|
|
239
364
|
```python
|
|
@@ -298,6 +423,7 @@ zonix/
|
|
|
298
423
|
spec.py agent()/team()/workflow()/router() factories
|
|
299
424
|
engine.py serializable Run engine and Agent execution
|
|
300
425
|
runtime.py __call__/run/stream driver shared by every node
|
|
426
|
+
graph.py workflow/team graph specs, Mermaid, DOT, and image export
|
|
301
427
|
memory/ Window, Summarize, Vector, Session
|
|
302
428
|
multi/ Workflow, Team, Router nodes
|
|
303
429
|
hitl.py checkpoint save/load and approval keys
|