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.
Files changed (48) hide show
  1. {zonix-0.2.2 → zonix-0.3.2}/CHANGELOG.md +24 -0
  2. {zonix-0.2.2 → zonix-0.3.2}/PKG-INFO +134 -4
  3. {zonix-0.2.2 → zonix-0.3.2}/README.md +129 -3
  4. zonix-0.3.2/docs/tutorial.zh-CN.md +880 -0
  5. {zonix-0.2.2 → zonix-0.3.2}/pyproject.toml +3 -1
  6. {zonix-0.2.2 → zonix-0.3.2}/scripts/smoke_real_provider.py +0 -3
  7. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/__init__.py +28 -0
  8. zonix-0.3.2/src/zonix/content.py +109 -0
  9. zonix-0.3.2/src/zonix/engine.py +696 -0
  10. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/events.py +3 -0
  11. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/exceptions.py +0 -4
  12. zonix-0.3.2/src/zonix/graph.py +112 -0
  13. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/hitl.py +10 -1
  14. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/memory/__init__.py +5 -2
  15. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/models/__init__.py +2 -0
  16. zonix-0.3.2/src/zonix/models/anthropic.py +451 -0
  17. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/models/base.py +11 -1
  18. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/models/fake.py +2 -1
  19. zonix-0.3.2/src/zonix/models/gemini.py +351 -0
  20. zonix-0.3.2/src/zonix/models/openai.py +770 -0
  21. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/multi/team.py +103 -6
  22. zonix-0.3.2/src/zonix/multi/workflow.py +300 -0
  23. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/obs.py +0 -1
  24. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/runtime.py +49 -7
  25. zonix-0.3.2/src/zonix/spec.py +331 -0
  26. zonix-0.3.2/src/zonix/sync.py +67 -0
  27. zonix-0.3.2/src/zonix/tools.py +261 -0
  28. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/types.py +98 -9
  29. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/wire/ai_sdk.py +5 -1
  30. zonix-0.2.2/docs/tutorial.zh-CN.md +0 -372
  31. zonix-0.2.2/src/zonix/engine.py +0 -336
  32. zonix-0.2.2/src/zonix/models/anthropic.py +0 -154
  33. zonix-0.2.2/src/zonix/models/openai.py +0 -222
  34. zonix-0.2.2/src/zonix/multi/workflow.py +0 -151
  35. zonix-0.2.2/src/zonix/spec.py +0 -194
  36. zonix-0.2.2/src/zonix/tools.py +0 -109
  37. {zonix-0.2.2 → zonix-0.3.2}/.gitignore +0 -0
  38. {zonix-0.2.2 → zonix-0.3.2}/CONTRIBUTING.md +0 -0
  39. {zonix-0.2.2 → zonix-0.3.2}/LICENSE +0 -0
  40. {zonix-0.2.2 → zonix-0.3.2}/SECURITY.md +0 -0
  41. {zonix-0.2.2 → zonix-0.3.2}/examples/real_provider_case.py +0 -0
  42. {zonix-0.2.2 → zonix-0.3.2}/examples/single_agent.py +0 -0
  43. {zonix-0.2.2 → zonix-0.3.2}/examples/workflow_team.py +0 -0
  44. {zonix-0.2.2 → zonix-0.3.2}/logo.png +0 -0
  45. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/multi/__init__.py +0 -0
  46. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/py.typed +0 -0
  47. {zonix-0.2.2 → zonix-0.3.2}/src/zonix/serialization.py +0 -0
  48. {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.2.2
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
  ![Zonix logo](https://raw.githubusercontent.com/zongxi1115/zonix/main/logo.png)
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, and checkpoint metadata. `.stream()` returns typed events that can be
50
- mapped to frontend protocols such as the Vercel AI SDK data stream.
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.2", temperature=0.2),
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
  ![Zonix logo](https://raw.githubusercontent.com/zongxi1115/zonix/main/logo.png)
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, and checkpoint metadata. `.stream()` returns typed events that can be
21
- mapped to frontend protocols such as the Vercel AI SDK data stream.
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.2", temperature=0.2),
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