wizardflow 0.3.0__tar.gz → 0.5.0__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.
- {wizardflow-0.3.0 → wizardflow-0.5.0}/CONTRIBUTING.md +19 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/PKG-INFO +33 -11
- {wizardflow-0.3.0 → wizardflow-0.5.0}/README.md +32 -10
- wizardflow-0.5.0/assets/demo.gif +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/examples/multibranch.html +28 -11
- {wizardflow-0.3.0 → wizardflow-0.5.0}/examples/multibranch.md +21 -11
- {wizardflow-0.3.0 → wizardflow-0.5.0}/examples/multibranch.py +23 -2
- {wizardflow-0.3.0 → wizardflow-0.5.0}/examples/quickstart.html +20 -6
- {wizardflow-0.3.0 → wizardflow-0.5.0}/examples/quickstart.md +12 -6
- {wizardflow-0.3.0 → wizardflow-0.5.0}/examples/quickstart.py +17 -2
- {wizardflow-0.3.0 → wizardflow-0.5.0}/pyproject.toml +1 -1
- wizardflow-0.5.0/scripts/live_trace_demo.py +211 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/__init__.py +6 -2
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_render.py +9 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/404.html +7 -25
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/__next.__PAGE__.txt +2 -2
- wizardflow-0.5.0/src/wizardflow/_ui/__next._full.txt +23 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/__next._head.txt +1 -1
- wizardflow-0.5.0/src/wizardflow/_ui/__next._index.txt +7 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/__next._tree.txt +1 -1
- wizardflow-0.3.0/src/wizardflow/_ui/_next/static/chunks/0ux_7aev0a2kt.js → wizardflow-0.5.0/src/wizardflow/_ui/_next/static/chunks/07ur0v9hb4ida.js +3 -3
- wizardflow-0.3.0/src/wizardflow/_ui/_next/static/chunks/0u1x9l49cgb30.js → wizardflow-0.5.0/src/wizardflow/_ui/_next/static/chunks/1_way7swvumpv.js +1 -1
- wizardflow-0.5.0/src/wizardflow/_ui/_next/static/chunks/1fb6xczxpe9s3.js +260 -0
- wizardflow-0.5.0/src/wizardflow/_ui/_next/static/chunks/221oy04d3f6t2.js +7 -0
- wizardflow-0.5.0/src/wizardflow/_ui/_not-found/__next._full.txt +18 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_not-found/__next._head.txt +1 -1
- wizardflow-0.5.0/src/wizardflow/_ui/_not-found/__next._index.txt +7 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_not-found/__next._not-found/__PAGE__.txt +1 -1
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_not-found/__next._not-found.txt +1 -1
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_not-found/__next._tree.txt +1 -1
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_not-found.html +7 -25
- wizardflow-0.5.0/src/wizardflow/_ui/_not-found.txt +18 -0
- wizardflow-0.5.0/src/wizardflow/_ui/index.html +7 -0
- wizardflow-0.5.0/src/wizardflow/_ui/index.txt +23 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/sitemap.xml +1 -1
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/cli.py +102 -8
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/client.py +20 -7
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/html.py +9 -1
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/markdown.py +6 -1
- wizardflow-0.5.0/tests/test_cli.py +311 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/tests/test_html.py +23 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/tests/test_markdown.py +21 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/tests/test_reader.py +10 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/tests/test_trace.py +37 -0
- wizardflow-0.3.0/assets/demo.gif +0 -0
- wizardflow-0.3.0/src/wizardflow/_ui/__next._full.txt +0 -22
- wizardflow-0.3.0/src/wizardflow/_ui/__next._index.txt +0 -6
- wizardflow-0.3.0/src/wizardflow/_ui/_next/static/chunks/142r2alz4_x6t.js +0 -1
- wizardflow-0.3.0/src/wizardflow/_ui/_next/static/chunks/2q_qu9w0vej6s.js +0 -115
- wizardflow-0.3.0/src/wizardflow/_ui/_not-found/__next._full.txt +0 -17
- wizardflow-0.3.0/src/wizardflow/_ui/_not-found/__next._index.txt +0 -6
- wizardflow-0.3.0/src/wizardflow/_ui/_not-found.txt +0 -17
- wizardflow-0.3.0/src/wizardflow/_ui/index.html +0 -25
- wizardflow-0.3.0/src/wizardflow/_ui/index.txt +0 -22
- wizardflow-0.3.0/tests/test_cli.py +0 -158
- {wizardflow-0.3.0 → wizardflow-0.5.0}/.gitignore +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/LICENSE +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/examples/data_types.py +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/scripts/build_ui.py +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/05-c3ty_6dwfk.js +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/09s72r58ijxwx.js +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/0cz1d0mv5g_q7.js +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/14mrh2-p_w84d.js +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/18clj6xsyhc_j.css +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/2-j1b74l3lxxu.css +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/27jktro2p5rq9.js +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/2gj5bmeov-r8f.js +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/310vm2bl3xxpt.js +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/3lt2ss7dftc2n.css +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/3n7dm2ojtyzwn.js +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/chunks/turbopack-2fblmukzx7kws.js +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/4fa387ec64143e14-s.2tuy5pz7dlieh.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/53b9e256198e5412-s.390ncx5urfkfu.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/5ce348bf30bf5439-s.31988l_ccedte.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/6306c77e7c8268e4-s.2dbetqa9o8jxf.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/7178b3e590c64307-s.21jp631_3pja2.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/797e433ab948586e-s.p.0r6juujl39pe6.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/7d817b4c03b0c5f1-s.1uyisp29ctx0d.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/8a480f0b521d4e75-s.1qq4vpdcun5oj.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/bbc41e54d2fcbd21-s.1rgnod-3esatf.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/caa3a2e1cccd8315-s.p.0wgildi0cnwt9.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/fef07dbb0973bf53-s.3p2_lha1f2xer.woff2 +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/_next/static/media/icon.09qublg69ek7b.svg +0 -0
- {wizardflow-0.3.0/src/wizardflow/_ui/_next/static/AJjiS0WLQjNzSyuZBS0Ff → wizardflow-0.5.0/src/wizardflow/_ui/_next/static/ncaFVj6v0NRTp4NatoEBp}/_buildManifest.js +0 -0
- {wizardflow-0.3.0/src/wizardflow/_ui/_next/static/AJjiS0WLQjNzSyuZBS0Ff → wizardflow-0.5.0/src/wizardflow/_ui/_next/static/ncaFVj6v0NRTp4NatoEBp}/_clientMiddlewareManifest.js +0 -0
- {wizardflow-0.3.0/src/wizardflow/_ui/_next/static/AJjiS0WLQjNzSyuZBS0Ff → wizardflow-0.5.0/src/wizardflow/_ui/_next/static/ncaFVj6v0NRTp4NatoEBp}/_ssgManifest.js +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/icon.svg +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/opengraph-image +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/_ui/robots.txt +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/constants.py +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/py.typed +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/src/wizardflow/reader.py +0 -0
- {wizardflow-0.3.0 → wizardflow-0.5.0}/tests/conftest.py +0 -0
|
@@ -50,3 +50,22 @@ That script runs the shared Next frontend with
|
|
|
50
50
|
`NEXT_PUBLIC_WIZARDFLOW_TARGET=local`, copies the static export into
|
|
51
51
|
`src/wizardflow/_ui/`, and removes hosted-only legal route artifacts (`Impressum`
|
|
52
52
|
/ `Datenschutz`). The SDK UI keeps the GitHub project link.
|
|
53
|
+
|
|
54
|
+
## Checking live traces by hand
|
|
55
|
+
|
|
56
|
+
The viewer follows a trace file while it is still being written: it polls with
|
|
57
|
+
`If-None-Match`, extends the timeline without losing your place, and stops when
|
|
58
|
+
the part rotates away. None of that is covered by an automated test, so there is
|
|
59
|
+
a generator to watch it with:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
cd sdk/python
|
|
63
|
+
python scripts/live_trace_demo.py
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
It records a synthetic run with the ordinary SDK calls, writing into the
|
|
67
|
+
monorepo's `web/public/` (so the trace is same-origin for `npm run dev` in
|
|
68
|
+
`web/`) and printing the viewer URL. It rotates after 18 messages, so the sealed
|
|
69
|
+
part, the part navigation, and the live pulse dot can all be seen in a couple of
|
|
70
|
+
minutes. Like `build_ui.py`, it needs the full monorepo checkout and is never
|
|
71
|
+
packaged.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wizardflow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Python SDK for recording agent flows into the WizardFlow / AgentTrace file format.
|
|
5
5
|
Project-URL: Homepage, https://getwizardflow.com
|
|
6
6
|
Project-URL: Documentation, https://getwizardflow.com
|
|
@@ -57,6 +57,10 @@ runtime dependencies**, no daemon, no setup.
|
|
|
57
57
|
|
|
58
58
|

|
|
59
59
|
|
|
60
|
+
▶ **[Watch this exact run live](https://getwizardflow.com/?example=doctor-consultation)** —
|
|
61
|
+
the flow from the GIF, replaying in your browser right now. No install, and
|
|
62
|
+
nothing is uploaded.
|
|
63
|
+
|
|
60
64
|
The file it produces is JSON Lines with a small, documented schema: line 1 is a
|
|
61
65
|
`header` record carrying the `graph { nodes, edges }`, then one `message`
|
|
62
66
|
record per line (`steps[] → payloads[] { label, value }`). Every line is plain
|
|
@@ -150,7 +154,11 @@ wizardflow.end_message("msg-2") # writes msg-2
|
|
|
150
154
|
|
|
151
155
|
Pass the **string `id`**, never a handle object — safe to hand to a callback or
|
|
152
156
|
across threads. A message is created on first reference and finalized by
|
|
153
|
-
`end_message`; `end_message(id, title="...")` optionally gives it a human title
|
|
157
|
+
`end_message`; `end_message(id, title="...")` optionally gives it a human title,
|
|
158
|
+
and `end_message(id, meta={...})` attaches flat metadata about the message as a
|
|
159
|
+
whole (outcome, latency, user id, …) — the viewer shows it on the message's
|
|
160
|
+
timeline chip. Keep meta values short scalars; large structured data belongs in
|
|
161
|
+
`log()` payloads.
|
|
154
162
|
|
|
155
163
|
### Choosing message ids
|
|
156
164
|
|
|
@@ -169,8 +177,9 @@ Do **not** use a session id, user id, or thread id as the message id. Those
|
|
|
169
177
|
identify a *conversation*, and a conversation contains many messages — after
|
|
170
178
|
the first `end_message(session_id)`, every further `log(session_id, ...)` in
|
|
171
179
|
that session raises, because that "message" has already ended. If you want the
|
|
172
|
-
session visible in the trace, put it in `init(meta=...)` or in the message
|
|
173
|
-
|
|
180
|
+
session visible in the trace, put it in `init(meta=...)` or in the message's
|
|
181
|
+
meta (`end_message(msg_id, meta={"session": session_id})`) — never in the id.
|
|
182
|
+
Ids are opaque:
|
|
174
183
|
the viewer only displays and groups by them, so encoding meaning into them
|
|
175
184
|
buys nothing. Hardcoded ids like `"msg-1"` are fine for demo scripts; `uuid4`
|
|
176
185
|
is the right default for real applications.
|
|
@@ -302,9 +311,11 @@ metadata never fails extraction (the edge is just emitted plain). Call it
|
|
|
302
311
|
**message id**, the second is the node. With `label`/`content` it records a
|
|
303
312
|
payload; bare `log(id, "node")` records a visit with no payloads. The message
|
|
304
313
|
is created on first reference; this only accumulates in memory.
|
|
305
|
-
- `end_message(id, title=None)` — finalize a message and append it
|
|
306
|
-
trace; returns the current trace path. The **only** call that touches
|
|
307
|
-
Optional `title` sets the message's human title
|
|
314
|
+
- `end_message(id, title=None, meta=None)` — finalize a message and append it
|
|
315
|
+
to the trace; returns the current trace path. The **only** call that touches
|
|
316
|
+
disk. Optional `title` sets the message's human title; optional `meta` (a
|
|
317
|
+
flat dict of short scalars) attaches message-level metadata shown on the
|
|
318
|
+
message's chip in the viewer. Idempotent.
|
|
308
319
|
- `reinit(name=None, description=None, meta=None)` — start a **new trace
|
|
309
320
|
file** (fresh timestamped name), keeping the graph and output configuration.
|
|
310
321
|
Use it at natural boundaries of a long-lived process — a new user session, a
|
|
@@ -440,6 +451,11 @@ wizardflow html run.jsonl
|
|
|
440
451
|
wizardflow json run.jsonl
|
|
441
452
|
# --path is equivalent everywhere:
|
|
442
453
|
wizardflow ui --path run.jsonl
|
|
454
|
+
# --latest: treat the path as a directory (default: the current directory) and
|
|
455
|
+
# open its most recently modified *.jsonl / *.json file — handy right after a
|
|
456
|
+
# run, or to attach to the trace an agent is writing right now:
|
|
457
|
+
wizardflow ui --latest
|
|
458
|
+
wizardflow ui --latest traces/
|
|
443
459
|
```
|
|
444
460
|
|
|
445
461
|
The SDK only ever **writes** JSONL, but these commands **read** either framing —
|
|
@@ -449,16 +465,22 @@ they stay at parity with the web viewer, which also accepts both.
|
|
|
449
465
|
### `wizardflow ui` — local viewer
|
|
450
466
|
|
|
451
467
|
```bash
|
|
452
|
-
wizardflow ui run.jsonl [--host 127.0.0.1] [--port 0] [--no-open]
|
|
468
|
+
wizardflow ui run.jsonl [--latest] [--host 127.0.0.1] [--port 0] [--no-open]
|
|
453
469
|
```
|
|
454
470
|
|
|
455
471
|
Binds a stdlib HTTP server, serves the static WizardFlow UI bundled in the SDK
|
|
456
|
-
package, and opens the selected trace in your browser
|
|
457
|
-
server-side and served to the UI as one JSON document, re-read on
|
|
458
|
-
|
|
472
|
+
package, and opens the selected trace in your browser. The JSONL is assembled
|
|
473
|
+
server-side and served to the UI as one JSON document, re-read on every fetch —
|
|
474
|
+
and the UI **follows a still-running trace live**: it polls the file (cheap
|
|
475
|
+
ETag revalidation; an unchanged file is a single `stat()` on the server) and
|
|
476
|
+
extends the view in place without resetting your selection, playback position,
|
|
477
|
+
or arranged node layout. A pulse dot in the header shows the watch is active;
|
|
478
|
+
messages that arrive while you're inspecting an older one appear as a
|
|
479
|
+
"+N new" chip in the timeline.
|
|
459
480
|
|
|
460
481
|
| flag | default | meaning |
|
|
461
482
|
| --- | --- | --- |
|
|
483
|
+
| `--latest` | off | treat the path as a directory (default: cwd) and open its most recently modified `*.jsonl` / `*.json` |
|
|
462
484
|
| `--host` | `127.0.0.1` | interface to bind |
|
|
463
485
|
| `--port` | `0` | port to bind; `0` asks the OS for a free port |
|
|
464
486
|
| `--no-open` | off | print the local URL instead of launching a browser |
|
|
@@ -10,6 +10,10 @@ runtime dependencies**, no daemon, no setup.
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
+
▶ **[Watch this exact run live](https://getwizardflow.com/?example=doctor-consultation)** —
|
|
14
|
+
the flow from the GIF, replaying in your browser right now. No install, and
|
|
15
|
+
nothing is uploaded.
|
|
16
|
+
|
|
13
17
|
The file it produces is JSON Lines with a small, documented schema: line 1 is a
|
|
14
18
|
`header` record carrying the `graph { nodes, edges }`, then one `message`
|
|
15
19
|
record per line (`steps[] → payloads[] { label, value }`). Every line is plain
|
|
@@ -103,7 +107,11 @@ wizardflow.end_message("msg-2") # writes msg-2
|
|
|
103
107
|
|
|
104
108
|
Pass the **string `id`**, never a handle object — safe to hand to a callback or
|
|
105
109
|
across threads. A message is created on first reference and finalized by
|
|
106
|
-
`end_message`; `end_message(id, title="...")` optionally gives it a human title
|
|
110
|
+
`end_message`; `end_message(id, title="...")` optionally gives it a human title,
|
|
111
|
+
and `end_message(id, meta={...})` attaches flat metadata about the message as a
|
|
112
|
+
whole (outcome, latency, user id, …) — the viewer shows it on the message's
|
|
113
|
+
timeline chip. Keep meta values short scalars; large structured data belongs in
|
|
114
|
+
`log()` payloads.
|
|
107
115
|
|
|
108
116
|
### Choosing message ids
|
|
109
117
|
|
|
@@ -122,8 +130,9 @@ Do **not** use a session id, user id, or thread id as the message id. Those
|
|
|
122
130
|
identify a *conversation*, and a conversation contains many messages — after
|
|
123
131
|
the first `end_message(session_id)`, every further `log(session_id, ...)` in
|
|
124
132
|
that session raises, because that "message" has already ended. If you want the
|
|
125
|
-
session visible in the trace, put it in `init(meta=...)` or in the message
|
|
126
|
-
|
|
133
|
+
session visible in the trace, put it in `init(meta=...)` or in the message's
|
|
134
|
+
meta (`end_message(msg_id, meta={"session": session_id})`) — never in the id.
|
|
135
|
+
Ids are opaque:
|
|
127
136
|
the viewer only displays and groups by them, so encoding meaning into them
|
|
128
137
|
buys nothing. Hardcoded ids like `"msg-1"` are fine for demo scripts; `uuid4`
|
|
129
138
|
is the right default for real applications.
|
|
@@ -255,9 +264,11 @@ metadata never fails extraction (the edge is just emitted plain). Call it
|
|
|
255
264
|
**message id**, the second is the node. With `label`/`content` it records a
|
|
256
265
|
payload; bare `log(id, "node")` records a visit with no payloads. The message
|
|
257
266
|
is created on first reference; this only accumulates in memory.
|
|
258
|
-
- `end_message(id, title=None)` — finalize a message and append it
|
|
259
|
-
trace; returns the current trace path. The **only** call that touches
|
|
260
|
-
Optional `title` sets the message's human title
|
|
267
|
+
- `end_message(id, title=None, meta=None)` — finalize a message and append it
|
|
268
|
+
to the trace; returns the current trace path. The **only** call that touches
|
|
269
|
+
disk. Optional `title` sets the message's human title; optional `meta` (a
|
|
270
|
+
flat dict of short scalars) attaches message-level metadata shown on the
|
|
271
|
+
message's chip in the viewer. Idempotent.
|
|
261
272
|
- `reinit(name=None, description=None, meta=None)` — start a **new trace
|
|
262
273
|
file** (fresh timestamped name), keeping the graph and output configuration.
|
|
263
274
|
Use it at natural boundaries of a long-lived process — a new user session, a
|
|
@@ -393,6 +404,11 @@ wizardflow html run.jsonl
|
|
|
393
404
|
wizardflow json run.jsonl
|
|
394
405
|
# --path is equivalent everywhere:
|
|
395
406
|
wizardflow ui --path run.jsonl
|
|
407
|
+
# --latest: treat the path as a directory (default: the current directory) and
|
|
408
|
+
# open its most recently modified *.jsonl / *.json file — handy right after a
|
|
409
|
+
# run, or to attach to the trace an agent is writing right now:
|
|
410
|
+
wizardflow ui --latest
|
|
411
|
+
wizardflow ui --latest traces/
|
|
396
412
|
```
|
|
397
413
|
|
|
398
414
|
The SDK only ever **writes** JSONL, but these commands **read** either framing —
|
|
@@ -402,16 +418,22 @@ they stay at parity with the web viewer, which also accepts both.
|
|
|
402
418
|
### `wizardflow ui` — local viewer
|
|
403
419
|
|
|
404
420
|
```bash
|
|
405
|
-
wizardflow ui run.jsonl [--host 127.0.0.1] [--port 0] [--no-open]
|
|
421
|
+
wizardflow ui run.jsonl [--latest] [--host 127.0.0.1] [--port 0] [--no-open]
|
|
406
422
|
```
|
|
407
423
|
|
|
408
424
|
Binds a stdlib HTTP server, serves the static WizardFlow UI bundled in the SDK
|
|
409
|
-
package, and opens the selected trace in your browser
|
|
410
|
-
server-side and served to the UI as one JSON document, re-read on
|
|
411
|
-
|
|
425
|
+
package, and opens the selected trace in your browser. The JSONL is assembled
|
|
426
|
+
server-side and served to the UI as one JSON document, re-read on every fetch —
|
|
427
|
+
and the UI **follows a still-running trace live**: it polls the file (cheap
|
|
428
|
+
ETag revalidation; an unchanged file is a single `stat()` on the server) and
|
|
429
|
+
extends the view in place without resetting your selection, playback position,
|
|
430
|
+
or arranged node layout. A pulse dot in the header shows the watch is active;
|
|
431
|
+
messages that arrive while you're inspecting an older one appear as a
|
|
432
|
+
"+N new" chip in the timeline.
|
|
412
433
|
|
|
413
434
|
| flag | default | meaning |
|
|
414
435
|
| --- | --- | --- |
|
|
436
|
+
| `--latest` | off | treat the path as a directory (default: cwd) and open its most recently modified `*.jsonl` / `*.json` |
|
|
415
437
|
| `--host` | `127.0.0.1` | interface to bind |
|
|
416
438
|
| `--port` | `0` | port to bind; `0` asks the OS for a free port |
|
|
417
439
|
| `--no-open` | off | print the local URL instead of launching a browser |
|
|
Binary file
|
|
@@ -18,6 +18,10 @@ blockquote {
|
|
|
18
18
|
color: #8a8a8a; font-style: italic;
|
|
19
19
|
}
|
|
20
20
|
table.meta { border-collapse: collapse; font-size: .85rem; margin-bottom: 2rem; }
|
|
21
|
+
section.nodes { margin: -1rem 0 2rem; }
|
|
22
|
+
section.nodes h2 { font-size: 1.05rem; margin: 0 0 .2rem; }
|
|
23
|
+
section.nodes ul { margin: 0; padding-left: 1.2rem; font-size: .9rem; }
|
|
24
|
+
section.nodes li { margin: .15rem 0; }
|
|
21
25
|
table.meta th, table.meta td {
|
|
22
26
|
border: 1px solid #8884; padding: .25rem .6rem; text-align: left;
|
|
23
27
|
}
|
|
@@ -27,6 +31,7 @@ section.message {
|
|
|
27
31
|
padding: .25rem 1rem 1rem; margin-bottom: 1.5rem;
|
|
28
32
|
}
|
|
29
33
|
section.message > h2 { font-size: 1.15rem; margin: .8rem 0 .4rem; }
|
|
34
|
+
section.message > p.msg-meta { font-size: .85rem; color: #8a8a8a; margin: -.2rem 0 .6rem; }
|
|
30
35
|
section.step { padding: .9rem 0; }
|
|
31
36
|
section.step:first-of-type { padding-top: .2rem; }
|
|
32
37
|
/* Divider between consecutive nodes in a message. */
|
|
@@ -57,48 +62,60 @@ pre code { background: none; padding: 0; }
|
|
|
57
62
|
<table class="meta">
|
|
58
63
|
<tr><th>version</th><td>0.2</td></tr>
|
|
59
64
|
</table>
|
|
65
|
+
<section class="nodes">
|
|
66
|
+
<h2>Nodes</h2>
|
|
67
|
+
<ul>
|
|
68
|
+
<li><span class="label">router</span> — Classifies the request and picks a branch: planner or retriever.</li>
|
|
69
|
+
<li><span class="label">planner</span> — Decomposes a tool-using request into concrete tool calls.</li>
|
|
70
|
+
<li><span class="label">retriever</span> — Fetches the most relevant documents for the query.</li>
|
|
71
|
+
<li><span class="label">tool_node</span> — Runs the planned tool calls against external APIs.</li>
|
|
72
|
+
<li><span class="label">generator</span> — Writes the answer from the tool result or retrieved docs.</li>
|
|
73
|
+
</ul>
|
|
74
|
+
</section>
|
|
60
75
|
<section class="message">
|
|
61
76
|
<h2>Weather in Berlin</h2>
|
|
77
|
+
<p class="msg-meta"><span class="label">branch</span>: planner · <span class="label">outcome</span>: answered · <span class="label">latency_ms</span>: 10210</p>
|
|
62
78
|
<section class="step">
|
|
63
|
-
<h3>user_input <span class="time">·
|
|
79
|
+
<h3>user_input <span class="time">· 13:14:05</span></h3>
|
|
64
80
|
<p class="scalar"><span class="label">Input</span> <code>What's the weather in Berlin?</code></p>
|
|
65
81
|
</section>
|
|
66
82
|
<section class="step">
|
|
67
|
-
<h3>router <span class="time">·
|
|
83
|
+
<h3>router <span class="time">· 13:14:05</span></h3>
|
|
68
84
|
<p class="scalar"><span class="label">llm_input</span> <code>Pick a route for the request...</code></p>
|
|
69
85
|
<p class="scalar"><span class="label">llm_output</span> <code>{"route": "planner", "confidence": 0.92}</code></p>
|
|
70
86
|
</section>
|
|
71
87
|
<section class="step">
|
|
72
|
-
<h3>planner <span class="time">·
|
|
88
|
+
<h3>planner <span class="time">· 13:14:05</span></h3>
|
|
73
89
|
<p class="scalar"><span class="label">llm_input</span> <code>Decompose into tool calls...</code></p>
|
|
74
90
|
<p class="scalar"><span class="label">llm_output</span> <code>{"plan": ["weather_api(city='Berlin')"]}</code></p>
|
|
75
91
|
</section>
|
|
76
92
|
<section class="step">
|
|
77
|
-
<h3>tool_node <span class="time">·
|
|
93
|
+
<h3>tool_node <span class="time">· 13:14:05</span></h3>
|
|
78
94
|
</section>
|
|
79
95
|
<section class="step">
|
|
80
|
-
<h3>generator <span class="time">·
|
|
96
|
+
<h3>generator <span class="time">· 13:14:05</span></h3>
|
|
81
97
|
<p class="scalar"><span class="label">llm_input</span> <code>Answer using the tool result...</code></p>
|
|
82
98
|
<p class="scalar"><span class="label">llm_output</span> <code>It's 19C and partly cloudy in Berlin.</code></p>
|
|
83
99
|
</section>
|
|
84
100
|
<section class="step">
|
|
85
|
-
<h3>final_response <span class="time">·
|
|
101
|
+
<h3>final_response <span class="time">· 13:14:05</span></h3>
|
|
86
102
|
<p class="scalar"><span class="label">Output</span> <code>It's 19C and partly cloudy in Berlin.</code></p>
|
|
87
103
|
</section>
|
|
88
104
|
</section>
|
|
89
105
|
<section class="message">
|
|
90
106
|
<h2>Summarize research paper</h2>
|
|
107
|
+
<p class="msg-meta"><span class="label">branch</span>: retriever · <span class="label">outcome</span>: answered · <span class="label">docs_used</span>: 2 · <span class="label">latency_ms</span>: 7130</p>
|
|
91
108
|
<section class="step">
|
|
92
|
-
<h3>user_input <span class="time">·
|
|
109
|
+
<h3>user_input <span class="time">· 13:14:05</span></h3>
|
|
93
110
|
<p class="scalar"><span class="label">Input</span> <code>Summarize the attached research paper.</code></p>
|
|
94
111
|
</section>
|
|
95
112
|
<section class="step">
|
|
96
|
-
<h3>router <span class="time">·
|
|
113
|
+
<h3>router <span class="time">· 13:14:05</span></h3>
|
|
97
114
|
<p class="scalar"><span class="label">llm_input</span> <code>Pick a route for the request...</code></p>
|
|
98
115
|
<p class="scalar"><span class="label">llm_output</span> <code>{"route": "retriever", "confidence": 0.88}</code></p>
|
|
99
116
|
</section>
|
|
100
117
|
<section class="step">
|
|
101
|
-
<h3>retriever <span class="time">·
|
|
118
|
+
<h3>retriever <span class="time">· 13:14:05</span></h3>
|
|
102
119
|
<div class="payload"><span class="label">Input</span><pre><code>{
|
|
103
120
|
"topK": 4,
|
|
104
121
|
"namespace": "papers"
|
|
@@ -115,12 +132,12 @@ pre code { background: none; padding: 0; }
|
|
|
115
132
|
]</code></pre></div>
|
|
116
133
|
</section>
|
|
117
134
|
<section class="step">
|
|
118
|
-
<h3>generator <span class="time">·
|
|
135
|
+
<h3>generator <span class="time">· 13:14:05</span></h3>
|
|
119
136
|
<p class="scalar"><span class="label">llm_input</span> <code>Summarize the retrieved documents...</code></p>
|
|
120
137
|
<p class="scalar"><span class="label">llm_output</span> <code>The paper proposes a sparse attention variant with near-linear cost.</code></p>
|
|
121
138
|
</section>
|
|
122
139
|
<section class="step">
|
|
123
|
-
<h3>final_response <span class="time">·
|
|
140
|
+
<h3>final_response <span class="time">· 13:14:05</span></h3>
|
|
124
141
|
<p class="scalar"><span class="label">Output</span> <code>The paper proposes a sparse attention variant with near-linear cost.</code></p>
|
|
125
142
|
</section>
|
|
126
143
|
</section>
|
|
@@ -26,49 +26,59 @@ flowchart TD
|
|
|
26
26
|
n5 --> n6
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
- **router** — Classifies the request and picks a branch: planner or retriever.
|
|
30
|
+
- **planner** — Decomposes a tool-using request into concrete tool calls.
|
|
31
|
+
- **retriever** — Fetches the most relevant documents for the query.
|
|
32
|
+
- **tool_node** — Runs the planned tool calls against external APIs.
|
|
33
|
+
- **generator** — Writes the answer from the tool result or retrieved docs.
|
|
34
|
+
|
|
29
35
|
## Weather in Berlin
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
**branch**: planner · **outcome**: answered · **latency_ms**: 10210
|
|
38
|
+
|
|
39
|
+
### user_input · 13:14:05
|
|
32
40
|
|
|
33
41
|
**Input**: `What's the weather in Berlin?`
|
|
34
42
|
|
|
35
|
-
### router ·
|
|
43
|
+
### router · 13:14:05
|
|
36
44
|
|
|
37
45
|
**llm_input**: `Pick a route for the request...`
|
|
38
46
|
|
|
39
47
|
**llm_output**: `{"route": "planner", "confidence": 0.92}`
|
|
40
48
|
|
|
41
|
-
### planner ·
|
|
49
|
+
### planner · 13:14:05
|
|
42
50
|
|
|
43
51
|
**llm_input**: `Decompose into tool calls...`
|
|
44
52
|
|
|
45
53
|
**llm_output**: `{"plan": ["weather_api(city='Berlin')"]}`
|
|
46
54
|
|
|
47
|
-
### tool_node ·
|
|
55
|
+
### tool_node · 13:14:05
|
|
48
56
|
|
|
49
|
-
### generator ·
|
|
57
|
+
### generator · 13:14:05
|
|
50
58
|
|
|
51
59
|
**llm_input**: `Answer using the tool result...`
|
|
52
60
|
|
|
53
61
|
**llm_output**: `It's 19C and partly cloudy in Berlin.`
|
|
54
62
|
|
|
55
|
-
### final_response ·
|
|
63
|
+
### final_response · 13:14:05
|
|
56
64
|
|
|
57
65
|
**Output**: `It's 19C and partly cloudy in Berlin.`
|
|
58
66
|
|
|
59
67
|
## Summarize research paper
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
**branch**: retriever · **outcome**: answered · **docs_used**: 2 · **latency_ms**: 7130
|
|
70
|
+
|
|
71
|
+
### user_input · 13:14:05
|
|
62
72
|
|
|
63
73
|
**Input**: `Summarize the attached research paper.`
|
|
64
74
|
|
|
65
|
-
### router ·
|
|
75
|
+
### router · 13:14:05
|
|
66
76
|
|
|
67
77
|
**llm_input**: `Pick a route for the request...`
|
|
68
78
|
|
|
69
79
|
**llm_output**: `{"route": "retriever", "confidence": 0.88}`
|
|
70
80
|
|
|
71
|
-
### retriever ·
|
|
81
|
+
### retriever · 13:14:05
|
|
72
82
|
|
|
73
83
|
**Input**
|
|
74
84
|
|
|
@@ -94,12 +104,12 @@ flowchart TD
|
|
|
94
104
|
]
|
|
95
105
|
```
|
|
96
106
|
|
|
97
|
-
### generator ·
|
|
107
|
+
### generator · 13:14:05
|
|
98
108
|
|
|
99
109
|
**llm_input**: `Summarize the retrieved documents...`
|
|
100
110
|
|
|
101
111
|
**llm_output**: `The paper proposes a sparse attention variant with near-linear cost.`
|
|
102
112
|
|
|
103
|
-
### final_response ·
|
|
113
|
+
### final_response · 13:14:05
|
|
104
114
|
|
|
105
115
|
**Output**: `The paper proposes a sparse attention variant with near-linear cost.`
|
|
@@ -43,6 +43,16 @@ wiz = wizardflow.init(
|
|
|
43
43
|
("retriever", "generator"), # branches rejoin
|
|
44
44
|
("generator", "final_response"),
|
|
45
45
|
],
|
|
46
|
+
# Optional one-line description per node (keyed by id), shown behind an
|
|
47
|
+
# info icon in the viewer when the node is selected. Plumbing nodes
|
|
48
|
+
# (user_input, final_response) are intentionally left out.
|
|
49
|
+
node_descriptions={
|
|
50
|
+
"router": "Classifies the request and picks a branch: planner or retriever.",
|
|
51
|
+
"planner": "Decomposes a tool-using request into concrete tool calls.",
|
|
52
|
+
"retriever": "Fetches the most relevant documents for the query.",
|
|
53
|
+
"tool_node": "Runs the planned tool calls against external APIs.",
|
|
54
|
+
"generator": "Writes the answer from the tool result or retrieved docs.",
|
|
55
|
+
},
|
|
46
56
|
)
|
|
47
57
|
|
|
48
58
|
# msg-1 — takes the planner / tool branch.
|
|
@@ -55,7 +65,14 @@ wizardflow.log("msg-1", "tool_node") # ran the tool, logged no payload
|
|
|
55
65
|
wizardflow.log("msg-1", "generator", "llm_input", "Answer using the tool result...")
|
|
56
66
|
wizardflow.log("msg-1", "generator", "llm_output", "It's 19C and partly cloudy in Berlin.")
|
|
57
67
|
wizardflow.log("msg-1", "final_response", "Output", "It's 19C and partly cloudy in Berlin.")
|
|
58
|
-
|
|
68
|
+
# Optional meta: flat facts about the message as a whole (short scalars only),
|
|
69
|
+
# shown on the message's chip in the viewer. Here it records which branch ran
|
|
70
|
+
# and how the run turned out.
|
|
71
|
+
wizardflow.end_message(
|
|
72
|
+
"msg-1",
|
|
73
|
+
title="Weather in Berlin",
|
|
74
|
+
meta={"branch": "planner", "outcome": "answered", "latency_ms": 10210},
|
|
75
|
+
)
|
|
59
76
|
|
|
60
77
|
# msg-2 — takes the retriever branch (skips planner/tool entirely).
|
|
61
78
|
wizardflow.log("msg-2", "user_input", "Input", "Summarize the attached research paper.")
|
|
@@ -71,6 +88,10 @@ wizardflow.log("msg-2", "generator", "llm_output",
|
|
|
71
88
|
"The paper proposes a sparse attention variant with near-linear cost.")
|
|
72
89
|
wizardflow.log("msg-2", "final_response", "Output",
|
|
73
90
|
"The paper proposes a sparse attention variant with near-linear cost.")
|
|
74
|
-
wizardflow.end_message(
|
|
91
|
+
wizardflow.end_message(
|
|
92
|
+
"msg-2",
|
|
93
|
+
title="Summarize research paper",
|
|
94
|
+
meta={"branch": "retriever", "outcome": "answered", "docs_used": 2, "latency_ms": 7130},
|
|
95
|
+
)
|
|
75
96
|
|
|
76
97
|
print(f"wrote {wiz.current_path}")
|
|
@@ -18,6 +18,10 @@ blockquote {
|
|
|
18
18
|
color: #8a8a8a; font-style: italic;
|
|
19
19
|
}
|
|
20
20
|
table.meta { border-collapse: collapse; font-size: .85rem; margin-bottom: 2rem; }
|
|
21
|
+
section.nodes { margin: -1rem 0 2rem; }
|
|
22
|
+
section.nodes h2 { font-size: 1.05rem; margin: 0 0 .2rem; }
|
|
23
|
+
section.nodes ul { margin: 0; padding-left: 1.2rem; font-size: .9rem; }
|
|
24
|
+
section.nodes li { margin: .15rem 0; }
|
|
21
25
|
table.meta th, table.meta td {
|
|
22
26
|
border: 1px solid #8884; padding: .25rem .6rem; text-align: left;
|
|
23
27
|
}
|
|
@@ -27,6 +31,7 @@ section.message {
|
|
|
27
31
|
padding: .25rem 1rem 1rem; margin-bottom: 1.5rem;
|
|
28
32
|
}
|
|
29
33
|
section.message > h2 { font-size: 1.15rem; margin: .8rem 0 .4rem; }
|
|
34
|
+
section.message > p.msg-meta { font-size: .85rem; color: #8a8a8a; margin: -.2rem 0 .6rem; }
|
|
30
35
|
section.step { padding: .9rem 0; }
|
|
31
36
|
section.step:first-of-type { padding-top: .2rem; }
|
|
32
37
|
/* Divider between consecutive nodes in a message. */
|
|
@@ -57,33 +62,42 @@ pre code { background: none; padding: 0; }
|
|
|
57
62
|
<table class="meta">
|
|
58
63
|
<tr><th>version</th><td>0.2</td></tr>
|
|
59
64
|
</table>
|
|
65
|
+
<section class="nodes">
|
|
66
|
+
<h2>Nodes</h2>
|
|
67
|
+
<ul>
|
|
68
|
+
<li><span class="label">router</span> — Classifies the request and picks the next node.</li>
|
|
69
|
+
<li><span class="label">planner</span> — Decomposes the request into concrete tool calls.</li>
|
|
70
|
+
<li><span class="label">tool_node</span> — Runs the planned tool calls against external APIs.</li>
|
|
71
|
+
</ul>
|
|
72
|
+
</section>
|
|
60
73
|
<section class="message">
|
|
61
74
|
<h2>msg-1</h2>
|
|
62
75
|
<section class="step">
|
|
63
|
-
<h3>user_input <span class="time">·
|
|
76
|
+
<h3>user_input <span class="time">· 13:13:58</span></h3>
|
|
64
77
|
<p class="scalar"><span class="label">Input</span> <code>What's the weather in Berlin?</code></p>
|
|
65
78
|
</section>
|
|
66
79
|
<section class="step">
|
|
67
|
-
<h3>router <span class="time">·
|
|
80
|
+
<h3>router <span class="time">· 13:13:58</span></h3>
|
|
68
81
|
<p class="scalar"><span class="label">llm_input</span> <code>Route this request...</code></p>
|
|
69
82
|
<p class="scalar"><span class="label">llm_output</span> <code>{"route": "planner"}</code></p>
|
|
70
83
|
</section>
|
|
71
84
|
<section class="step">
|
|
72
|
-
<h3>tool_node <span class="time">·
|
|
85
|
+
<h3>tool_node <span class="time">· 13:13:58</span></h3>
|
|
73
86
|
</section>
|
|
74
87
|
<section class="step">
|
|
75
|
-
<h3>final_response <span class="time">·
|
|
88
|
+
<h3>final_response <span class="time">· 13:13:58</span></h3>
|
|
76
89
|
<p class="scalar"><span class="label">Output</span> <code>It's 19C and partly cloudy in Berlin.</code></p>
|
|
77
90
|
</section>
|
|
78
91
|
</section>
|
|
79
92
|
<section class="message">
|
|
80
93
|
<h2>Summarize the paper</h2>
|
|
94
|
+
<p class="msg-meta"><span class="label">outcome</span>: answered · <span class="label">latency_ms</span>: 320</p>
|
|
81
95
|
<section class="step">
|
|
82
|
-
<h3>user_input <span class="time">·
|
|
96
|
+
<h3>user_input <span class="time">· 13:13:58</span></h3>
|
|
83
97
|
<p class="scalar"><span class="label">Input</span> <code>Summarize the paper.</code></p>
|
|
84
98
|
</section>
|
|
85
99
|
<section class="step">
|
|
86
|
-
<h3>router <span class="time">·
|
|
100
|
+
<h3>router <span class="time">· 13:13:58</span></h3>
|
|
87
101
|
<p class="scalar"><span class="label">llm_output</span> <code>{"route": "planner"}</code></p>
|
|
88
102
|
</section>
|
|
89
103
|
</section>
|
|
@@ -21,30 +21,36 @@ flowchart TD
|
|
|
21
21
|
n3 --> n4
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
- **router** — Classifies the request and picks the next node.
|
|
25
|
+
- **planner** — Decomposes the request into concrete tool calls.
|
|
26
|
+
- **tool_node** — Runs the planned tool calls against external APIs.
|
|
27
|
+
|
|
24
28
|
## msg-1
|
|
25
29
|
|
|
26
|
-
### user_input ·
|
|
30
|
+
### user_input · 13:13:58
|
|
27
31
|
|
|
28
32
|
**Input**: `What's the weather in Berlin?`
|
|
29
33
|
|
|
30
|
-
### router ·
|
|
34
|
+
### router · 13:13:58
|
|
31
35
|
|
|
32
36
|
**llm_input**: `Route this request...`
|
|
33
37
|
|
|
34
38
|
**llm_output**: `{"route": "planner"}`
|
|
35
39
|
|
|
36
|
-
### tool_node ·
|
|
40
|
+
### tool_node · 13:13:58
|
|
37
41
|
|
|
38
|
-
### final_response ·
|
|
42
|
+
### final_response · 13:13:58
|
|
39
43
|
|
|
40
44
|
**Output**: `It's 19C and partly cloudy in Berlin.`
|
|
41
45
|
|
|
42
46
|
## Summarize the paper
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
**outcome**: answered · **latency_ms**: 320
|
|
49
|
+
|
|
50
|
+
### user_input · 13:13:58
|
|
45
51
|
|
|
46
52
|
**Input**: `Summarize the paper.`
|
|
47
53
|
|
|
48
|
-
### router ·
|
|
54
|
+
### router · 13:13:58
|
|
49
55
|
|
|
50
56
|
**llm_output**: `{"route": "planner"}`
|
|
@@ -26,6 +26,15 @@ wiz = wizardflow.init(
|
|
|
26
26
|
("planner", "tool_node"),
|
|
27
27
|
("tool_node", "final_response"),
|
|
28
28
|
],
|
|
29
|
+
# Optional: a one-line description per node, keyed by node id. It shows
|
|
30
|
+
# behind an info icon in the viewer when the node is selected. Plumbing
|
|
31
|
+
# nodes (user_input, final_response) are left out on purpose — not every
|
|
32
|
+
# node needs one.
|
|
33
|
+
node_descriptions={
|
|
34
|
+
"router": "Classifies the request and picks the next node.",
|
|
35
|
+
"planner": "Decomposes the request into concrete tool calls.",
|
|
36
|
+
"tool_node": "Runs the planned tool calls against external APIs.",
|
|
37
|
+
},
|
|
29
38
|
)
|
|
30
39
|
|
|
31
40
|
# Message 1 — each log names its message ("msg-1") in the first argument.
|
|
@@ -36,10 +45,16 @@ wizardflow.log("msg-1", "tool_node") # visited, no payloads
|
|
|
36
45
|
wizardflow.log("msg-1", "final_response", "Output", "It's 19C and partly cloudy in Berlin.")
|
|
37
46
|
wizardflow.end_message("msg-1") # <- writes the file; msg-1 is now persisted
|
|
38
47
|
|
|
39
|
-
# Message 2 — same shape; an optional title gives the message a human title
|
|
48
|
+
# Message 2 — same shape; an optional title gives the message a human title,
|
|
49
|
+
# and optional meta attaches flat facts about the message as a whole (shown on
|
|
50
|
+
# the message's chip in the viewer). Keep meta values short scalars.
|
|
40
51
|
wizardflow.log("msg-2", "user_input", "Input", "Summarize the paper.")
|
|
41
52
|
wizardflow.log("msg-2", "router", "llm_output", '{"route": "planner"}')
|
|
42
|
-
wizardflow.end_message(
|
|
53
|
+
wizardflow.end_message(
|
|
54
|
+
"msg-2",
|
|
55
|
+
title="Summarize the paper",
|
|
56
|
+
meta={"outcome": "answered", "latency_ms": 320},
|
|
57
|
+
)
|
|
43
58
|
# -> the part file now contains msg-1 and msg-2
|
|
44
59
|
|
|
45
60
|
print(f"wrote {wiz.current_path}")
|