wizardflow 0.2.0__tar.gz → 0.4.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.2.0 → wizardflow-0.4.0}/PKG-INFO +135 -20
- {wizardflow-0.2.0 → wizardflow-0.4.0}/README.md +134 -19
- {wizardflow-0.2.0 → wizardflow-0.4.0}/examples/multibranch.html +28 -11
- {wizardflow-0.2.0 → wizardflow-0.4.0}/examples/multibranch.md +21 -11
- {wizardflow-0.2.0 → wizardflow-0.4.0}/examples/multibranch.py +23 -2
- {wizardflow-0.2.0 → wizardflow-0.4.0}/examples/quickstart.html +20 -6
- {wizardflow-0.2.0 → wizardflow-0.4.0}/examples/quickstart.md +12 -6
- {wizardflow-0.2.0 → wizardflow-0.4.0}/examples/quickstart.py +17 -2
- {wizardflow-0.2.0 → wizardflow-0.4.0}/pyproject.toml +1 -1
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/__init__.py +26 -2
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_render.py +9 -0
- wizardflow-0.4.0/src/wizardflow/_ui/404.html +7 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/__next.__PAGE__.txt +3 -3
- wizardflow-0.4.0/src/wizardflow/_ui/__next._full.txt +23 -0
- wizardflow-0.4.0/src/wizardflow/_ui/__next._head.txt +6 -0
- wizardflow-0.4.0/src/wizardflow/_ui/__next._index.txt +7 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/__next._tree.txt +2 -2
- wizardflow-0.2.0/src/wizardflow/_ui/_next/static/chunks/0ux_7aev0a2kt.js → wizardflow-0.4.0/src/wizardflow/_ui/_next/static/chunks/07ur0v9hb4ida.js +3 -3
- wizardflow-0.2.0/src/wizardflow/_ui/_next/static/chunks/0u1x9l49cgb30.js → wizardflow-0.4.0/src/wizardflow/_ui/_next/static/chunks/1_way7swvumpv.js +1 -1
- wizardflow-0.4.0/src/wizardflow/_ui/_next/static/chunks/221oy04d3f6t2.js +7 -0
- wizardflow-0.4.0/src/wizardflow/_ui/_next/static/chunks/35wrz4n1txs_o.js +265 -0
- wizardflow-0.2.0/src/wizardflow/_ui/_next/static/chunks/0mz_v1wicwnmn.css → wizardflow-0.4.0/src/wizardflow/_ui/_next/static/chunks/3lt2ss7dftc2n.css +1 -1
- wizardflow-0.4.0/src/wizardflow/_ui/_next/static/media/icon.09qublg69ek7b.svg +34 -0
- wizardflow-0.4.0/src/wizardflow/_ui/_not-found/__next._full.txt +18 -0
- wizardflow-0.4.0/src/wizardflow/_ui/_not-found/__next._head.txt +6 -0
- wizardflow-0.4.0/src/wizardflow/_ui/_not-found/__next._index.txt +7 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_not-found/__next._not-found/__PAGE__.txt +1 -1
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_not-found/__next._not-found.txt +1 -1
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_not-found/__next._tree.txt +1 -1
- wizardflow-0.4.0/src/wizardflow/_ui/_not-found.html +7 -0
- wizardflow-0.4.0/src/wizardflow/_ui/_not-found.txt +18 -0
- wizardflow-0.4.0/src/wizardflow/_ui/icon.svg +34 -0
- wizardflow-0.4.0/src/wizardflow/_ui/index.html +7 -0
- wizardflow-0.4.0/src/wizardflow/_ui/index.txt +23 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/sitemap.xml +1 -1
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/client.py +143 -19
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/html.py +34 -1
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/markdown.py +29 -4
- {wizardflow-0.2.0 → wizardflow-0.4.0}/tests/test_html.py +40 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/tests/test_markdown.py +48 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/tests/test_reader.py +10 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/tests/test_trace.py +287 -0
- wizardflow-0.2.0/src/wizardflow/_ui/404.html +0 -25
- wizardflow-0.2.0/src/wizardflow/_ui/__next._full.txt +0 -22
- wizardflow-0.2.0/src/wizardflow/_ui/__next._head.txt +0 -6
- wizardflow-0.2.0/src/wizardflow/_ui/__next._index.txt +0 -6
- wizardflow-0.2.0/src/wizardflow/_ui/_next/static/chunks/142r2alz4_x6t.js +0 -1
- wizardflow-0.2.0/src/wizardflow/_ui/_next/static/chunks/4158u-399exxn.js +0 -115
- wizardflow-0.2.0/src/wizardflow/_ui/_next/static/media/icon.3okpzkln1vq00.svg +0 -19
- wizardflow-0.2.0/src/wizardflow/_ui/_not-found/__next._full.txt +0 -17
- wizardflow-0.2.0/src/wizardflow/_ui/_not-found/__next._head.txt +0 -6
- wizardflow-0.2.0/src/wizardflow/_ui/_not-found/__next._index.txt +0 -6
- wizardflow-0.2.0/src/wizardflow/_ui/_not-found.html +0 -25
- wizardflow-0.2.0/src/wizardflow/_ui/_not-found.txt +0 -17
- wizardflow-0.2.0/src/wizardflow/_ui/icon.svg +0 -19
- wizardflow-0.2.0/src/wizardflow/_ui/index.html +0 -25
- wizardflow-0.2.0/src/wizardflow/_ui/index.txt +0 -22
- {wizardflow-0.2.0 → wizardflow-0.4.0}/.gitignore +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/CONTRIBUTING.md +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/LICENSE +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/assets/demo.gif +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/examples/data_types.py +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/scripts/build_ui.py +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/05-c3ty_6dwfk.js +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/09s72r58ijxwx.js +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/0cz1d0mv5g_q7.js +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/14mrh2-p_w84d.js +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/18clj6xsyhc_j.css +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/2-j1b74l3lxxu.css +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/27jktro2p5rq9.js +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/2gj5bmeov-r8f.js +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/310vm2bl3xxpt.js +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/3n7dm2ojtyzwn.js +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/chunks/turbopack-2fblmukzx7kws.js +0 -0
- {wizardflow-0.2.0/src/wizardflow/_ui/_next/static/F7xCRRyix77IKo6qy-ltF → wizardflow-0.4.0/src/wizardflow/_ui/_next/static/hZT2Q8C4grDYd_BRHVtw0}/_buildManifest.js +0 -0
- {wizardflow-0.2.0/src/wizardflow/_ui/_next/static/F7xCRRyix77IKo6qy-ltF → wizardflow-0.4.0/src/wizardflow/_ui/_next/static/hZT2Q8C4grDYd_BRHVtw0}/_clientMiddlewareManifest.js +0 -0
- {wizardflow-0.2.0/src/wizardflow/_ui/_next/static/F7xCRRyix77IKo6qy-ltF → wizardflow-0.4.0/src/wizardflow/_ui/_next/static/hZT2Q8C4grDYd_BRHVtw0}/_ssgManifest.js +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/4fa387ec64143e14-s.2tuy5pz7dlieh.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/53b9e256198e5412-s.390ncx5urfkfu.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/5ce348bf30bf5439-s.31988l_ccedte.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/6306c77e7c8268e4-s.2dbetqa9o8jxf.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/7178b3e590c64307-s.21jp631_3pja2.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/797e433ab948586e-s.p.0r6juujl39pe6.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/7d817b4c03b0c5f1-s.1uyisp29ctx0d.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/8a480f0b521d4e75-s.1qq4vpdcun5oj.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/bbc41e54d2fcbd21-s.1rgnod-3esatf.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/caa3a2e1cccd8315-s.p.0wgildi0cnwt9.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/_next/static/media/fef07dbb0973bf53-s.3p2_lha1f2xer.woff2 +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/opengraph-image +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/_ui/robots.txt +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/cli.py +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/constants.py +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/py.typed +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/src/wizardflow/reader.py +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/tests/conftest.py +0 -0
- {wizardflow-0.2.0 → wizardflow-0.4.0}/tests/test_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wizardflow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.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=university-consultant)** —
|
|
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
|
|
@@ -98,6 +102,8 @@ No runtime dependencies. Developing the SDK itself? See
|
|
|
98
102
|
## Quickstart
|
|
99
103
|
|
|
100
104
|
```python
|
|
105
|
+
import uuid
|
|
106
|
+
|
|
101
107
|
import wizardflow
|
|
102
108
|
|
|
103
109
|
wizardflow.init(
|
|
@@ -110,10 +116,11 @@ wizardflow.init(
|
|
|
110
116
|
|
|
111
117
|
# Every log names its message in the first argument:
|
|
112
118
|
# log(message_id, node, payload_label, payload_value)
|
|
113
|
-
|
|
114
|
-
wizardflow.log(
|
|
115
|
-
wizardflow.log("
|
|
116
|
-
wizardflow.
|
|
119
|
+
msg_id = str(uuid.uuid4()) # one fresh id per message
|
|
120
|
+
wizardflow.log(msg_id, "router", "llm_input", prompt) # same node, two payloads ->
|
|
121
|
+
wizardflow.log(msg_id, "router", "llm_output", output) # folded into one step
|
|
122
|
+
wizardflow.log(msg_id, "tool_node") # visited, no payloads
|
|
123
|
+
wizardflow.end_message(msg_id) # -> writes the trace
|
|
117
124
|
```
|
|
118
125
|
|
|
119
126
|
There is **no `save()`** and no autosave: `log()` only accumulates in memory,
|
|
@@ -147,7 +154,88 @@ wizardflow.end_message("msg-2") # writes msg-2
|
|
|
147
154
|
|
|
148
155
|
Pass the **string `id`**, never a handle object — safe to hand to a callback or
|
|
149
156
|
across threads. A message is created on first reference and finalized by
|
|
150
|
-
`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.
|
|
162
|
+
|
|
163
|
+
### Choosing message ids
|
|
164
|
+
|
|
165
|
+
A message is **one unit of work** — one user turn, one run through the graph.
|
|
166
|
+
Its id only has to be unique within the run, and it **cannot be reused** once
|
|
167
|
+
`end_message` has been called on it. The simplest safe choice is a fresh UUID
|
|
168
|
+
per message:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
import uuid
|
|
172
|
+
|
|
173
|
+
msg_id = str(uuid.uuid4())
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Do **not** use a session id, user id, or thread id as the message id. Those
|
|
177
|
+
identify a *conversation*, and a conversation contains many messages — after
|
|
178
|
+
the first `end_message(session_id)`, every further `log(session_id, ...)` in
|
|
179
|
+
that session raises, because that "message" has already ended. If you want the
|
|
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:
|
|
183
|
+
the viewer only displays and groups by them, so encoding meaning into them
|
|
184
|
+
buys nothing. Hardcoded ids like `"msg-1"` are fine for demo scripts; `uuid4`
|
|
185
|
+
is the right default for real applications.
|
|
186
|
+
|
|
187
|
+
## One flow, or several at once
|
|
188
|
+
|
|
189
|
+
`init()` installs the client it returns as the module default. With a single
|
|
190
|
+
flow — the common case — **ignore the return value** and call
|
|
191
|
+
`wizardflow.log(...)` / `wizardflow.end_message(...)` directly: they work from
|
|
192
|
+
any module, with no handle to thread through your code.
|
|
193
|
+
|
|
194
|
+
Running several agent flows side by side (say, a doctor flow and a patient
|
|
195
|
+
flow) means several traces, and each needs its own instance. Keep the returned
|
|
196
|
+
client per flow and log through it — and name it **`tracer`**, not `client`,
|
|
197
|
+
since `client` in an agent codebase is almost always the LLM client:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
doctor_tracer = wizardflow.init(file_prefix="doctor", nodes=[...], edges=[...])
|
|
201
|
+
patient_tracer = wizardflow.init(file_prefix="patient", nodes=[...], edges=[...])
|
|
202
|
+
|
|
203
|
+
doctor_tracer.log(doc_msg, "diagnose", "llm_input", prompt)
|
|
204
|
+
patient_tracer.log(pat_msg, "intake", "llm_input", prompt)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
With more than one tracer, don't mix in the bare module-level calls:
|
|
208
|
+
`wizardflow.log(...)` always targets the most recently initialized tracer.
|
|
209
|
+
|
|
210
|
+
## Node descriptions, labels & colors
|
|
211
|
+
|
|
212
|
+
Nodes can carry a short human description of what they do. In the viewer it
|
|
213
|
+
stays out of the way: when a node is selected, a small info icon appears next
|
|
214
|
+
to its name in the inspector — click it to read the description. Nodes without
|
|
215
|
+
one show no icon, and `wizardflow md` / `wizardflow html` list the described
|
|
216
|
+
nodes under the graph.
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
wizardflow.init(
|
|
220
|
+
nodes=["router", "retriever", "generator"],
|
|
221
|
+
edges=[("router", "retriever"), ("retriever", "generator")],
|
|
222
|
+
node_descriptions={
|
|
223
|
+
"router": "Chooses the next step.",
|
|
224
|
+
"retriever": "Fetches relevant documents.",
|
|
225
|
+
"generator": "Writes the final answer.",
|
|
226
|
+
},
|
|
227
|
+
)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Two sibling kwargs work the same way: `node_labels` maps ids to the display
|
|
231
|
+
name the viewer shows instead of the raw id (especially useful with
|
|
232
|
+
`init_from_langgraph`, where the extracted ids are your function names), and
|
|
233
|
+
`node_colors` sets accent colors. All three are validated against the declared
|
|
234
|
+
nodes: an unknown id raises a `WizardFlowError` at `init()` time so typos fail
|
|
235
|
+
fast; with `silent=True` it is logged as a warning on the `wizardflow` logger
|
|
236
|
+
and skipped instead. All three also work on `init_from_langgraph`, where they
|
|
237
|
+
attach to the extracted node ids — `log()` always targets the **id**, never
|
|
238
|
+
the label.
|
|
151
239
|
|
|
152
240
|
## LangGraph: automatic topology
|
|
153
241
|
|
|
@@ -180,8 +268,8 @@ wizardflow.init_from_langgraph(
|
|
|
180
268
|
```
|
|
181
269
|
|
|
182
270
|
By default, a color key that does not match an extracted node id raises a
|
|
183
|
-
`WizardFlowError` so typos fail fast. With `silent=True`, unknown color keys
|
|
184
|
-
|
|
271
|
+
`WizardFlowError` so typos fail fast. With `silent=True`, unknown color keys
|
|
272
|
+
are skipped and logged as a warning instead.
|
|
185
273
|
|
|
186
274
|
It extracts node ids (keeping `__start__` / `__end__`) and directed edges, and
|
|
187
275
|
marks runtime branches with `"conditional": true` (deterministic and parallel
|
|
@@ -202,30 +290,39 @@ A non-LangGraph object raises `LangGraphExtractionError`; missing conditional
|
|
|
202
290
|
metadata never fails extraction (the edge is just emitted plain). Call it
|
|
203
291
|
**after `compile()`**, when the topology actually exists.
|
|
204
292
|
|
|
205
|
-
## API
|
|
293
|
+
## API
|
|
206
294
|
|
|
207
|
-
|
|
208
|
-
> appending — the old single-document `.json` format is gone from the SDK
|
|
209
|
-
> (writer *and* CLI readers). The web UI at getwizardflow.com still opens old
|
|
210
|
-
> `.json` traces.
|
|
211
|
-
|
|
212
|
-
- `init(output_dir=, file_prefix="wizardflow", name=, description=, nodes=, edges=, meta=, silent=False, max_bytes=16_000_000, max_messages=2_000) -> Client`
|
|
295
|
+
- `init(output_dir=, file_prefix="wizardflow", name=, description=, nodes=, edges=, node_labels=, node_colors=, node_descriptions=, meta=, silent=False, max_bytes=16_000_000, max_messages=2_000) -> Client`
|
|
213
296
|
- `description` lands in `meta.description` (matches the schema field).
|
|
214
297
|
- `nodes=` enables fast-fail: `log()` to an undeclared node raises
|
|
215
298
|
`UnknownNodeError` immediately (unless silenced).
|
|
299
|
+
- `node_labels` / `node_colors` / `node_descriptions` map declared node ids
|
|
300
|
+
to a display name / CSS color / short description; an unknown id raises
|
|
301
|
+
unless silenced (then it's logged as a warning and skipped).
|
|
216
302
|
- `output_dir` is optional; omitted, traces are written in cwd.
|
|
217
303
|
- `file_prefix` is optional; omitted, filenames start with `wizardflow`.
|
|
218
304
|
- `max_bytes` / `max_messages` cap each part file before rotation (see below).
|
|
219
|
-
- `init_from_langgraph(app, output_dir=, file_prefix="wizardflow", name=, description=, meta=, node_colors=, silent=False, max_bytes=..., max_messages=...) -> Client`
|
|
305
|
+
- `init_from_langgraph(app, output_dir=, file_prefix="wizardflow", name=, description=, meta=, node_labels=, node_colors=, node_descriptions=, silent=False, max_bytes=..., max_messages=...) -> Client`
|
|
220
306
|
- same as `init`, but `nodes`/`edges` come from `app.get_graph()`.
|
|
221
|
-
- `
|
|
307
|
+
- `node_labels` renames extracted ids for display (they're your function
|
|
308
|
+
names); `node_colors` maps them to CSS colors such as `"#A78BFA"`;
|
|
309
|
+
`node_descriptions` to short descriptions.
|
|
222
310
|
- `log(id, node, label=None, content=None)` — the first positional is the
|
|
223
311
|
**message id**, the second is the node. With `label`/`content` it records a
|
|
224
312
|
payload; bare `log(id, "node")` records a visit with no payloads. The message
|
|
225
313
|
is created on first reference; this only accumulates in memory.
|
|
226
|
-
- `end_message(id, title=None)` — finalize a message and append it
|
|
227
|
-
trace; returns the current trace path. The **only** call that touches
|
|
228
|
-
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.
|
|
319
|
+
- `reinit(name=None, description=None, meta=None)` — start a **new trace
|
|
320
|
+
file** (fresh timestamped name), keeping the graph and output configuration.
|
|
321
|
+
Use it at natural boundaries of a long-lived process — a new user session, a
|
|
322
|
+
new day. The old file is left as-is (**no seal**: it's a finished run, not a
|
|
323
|
+
rotated part). Open messages carry over and are written wherever they end;
|
|
324
|
+
completed message ids become reusable. `name`/`description`/`meta` replace
|
|
325
|
+
the current values when given. Returns the new trace path.
|
|
229
326
|
- `Client.current_path` — the trace file currently being written.
|
|
230
327
|
- `to_dict()` / `to_json()` — inspect the active part (completed messages),
|
|
231
328
|
assembled into one `AgentTraceFile` object.
|
|
@@ -251,6 +348,10 @@ unparseable final line (a crash mid-append leaves a torn tail; everything
|
|
|
251
348
|
before it is intact). Only **completed** messages are written; an in-progress
|
|
252
349
|
message lives in memory until it ends.
|
|
253
350
|
|
|
351
|
+
> Early SDK versions (0.1) wrote a trace as one single-document `.json` file
|
|
352
|
+
> instead. Newer versions write JSONL only, but every reader — the CLI
|
|
353
|
+
> subcommands and the web UI at getwizardflow.com — still opens both formats.
|
|
354
|
+
|
|
254
355
|
### How saving works
|
|
255
356
|
|
|
256
357
|
`end_message` **appends one line** to the active part — O(1) no matter how
|
|
@@ -312,6 +413,20 @@ Consecutive `log()` calls to the **same** node within a message fold into a
|
|
|
312
413
|
single step with multiple payloads (e.g. a router step carrying both
|
|
313
414
|
`llm_input` and `llm_output`). A `log()` to a different node starts a new step.
|
|
314
415
|
|
|
416
|
+
## What to log (conventions)
|
|
417
|
+
|
|
418
|
+
Payload labels are free-form strings — the schema reserves none of them. These
|
|
419
|
+
conventions just keep traces consistent and readable:
|
|
420
|
+
|
|
421
|
+
- **LLM-backed nodes** — log the exact prompt as `llm_input` and the raw
|
|
422
|
+
completion as `llm_output`. When a run goes wrong, the trace then shows
|
|
423
|
+
precisely what the model saw and what it said — most of what you need to
|
|
424
|
+
debug an agent — and step folding merges the pair into one step.
|
|
425
|
+
- **Everything else** — log the node's real domain data under honest labels: a
|
|
426
|
+
retriever logs its `query` and `results`, a tool node `tool_input` and
|
|
427
|
+
`tool_output`, a deterministic router its `decision`. Don't force
|
|
428
|
+
`llm_input`/`llm_output` onto nodes that never call a model.
|
|
429
|
+
|
|
315
430
|
## Examples
|
|
316
431
|
|
|
317
432
|
Fuller runnable examples live in
|
|
@@ -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=university-consultant)** —
|
|
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
|
|
@@ -51,6 +55,8 @@ No runtime dependencies. Developing the SDK itself? See
|
|
|
51
55
|
## Quickstart
|
|
52
56
|
|
|
53
57
|
```python
|
|
58
|
+
import uuid
|
|
59
|
+
|
|
54
60
|
import wizardflow
|
|
55
61
|
|
|
56
62
|
wizardflow.init(
|
|
@@ -63,10 +69,11 @@ wizardflow.init(
|
|
|
63
69
|
|
|
64
70
|
# Every log names its message in the first argument:
|
|
65
71
|
# log(message_id, node, payload_label, payload_value)
|
|
66
|
-
|
|
67
|
-
wizardflow.log(
|
|
68
|
-
wizardflow.log("
|
|
69
|
-
wizardflow.
|
|
72
|
+
msg_id = str(uuid.uuid4()) # one fresh id per message
|
|
73
|
+
wizardflow.log(msg_id, "router", "llm_input", prompt) # same node, two payloads ->
|
|
74
|
+
wizardflow.log(msg_id, "router", "llm_output", output) # folded into one step
|
|
75
|
+
wizardflow.log(msg_id, "tool_node") # visited, no payloads
|
|
76
|
+
wizardflow.end_message(msg_id) # -> writes the trace
|
|
70
77
|
```
|
|
71
78
|
|
|
72
79
|
There is **no `save()`** and no autosave: `log()` only accumulates in memory,
|
|
@@ -100,7 +107,88 @@ wizardflow.end_message("msg-2") # writes msg-2
|
|
|
100
107
|
|
|
101
108
|
Pass the **string `id`**, never a handle object — safe to hand to a callback or
|
|
102
109
|
across threads. A message is created on first reference and finalized by
|
|
103
|
-
`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.
|
|
115
|
+
|
|
116
|
+
### Choosing message ids
|
|
117
|
+
|
|
118
|
+
A message is **one unit of work** — one user turn, one run through the graph.
|
|
119
|
+
Its id only has to be unique within the run, and it **cannot be reused** once
|
|
120
|
+
`end_message` has been called on it. The simplest safe choice is a fresh UUID
|
|
121
|
+
per message:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
import uuid
|
|
125
|
+
|
|
126
|
+
msg_id = str(uuid.uuid4())
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Do **not** use a session id, user id, or thread id as the message id. Those
|
|
130
|
+
identify a *conversation*, and a conversation contains many messages — after
|
|
131
|
+
the first `end_message(session_id)`, every further `log(session_id, ...)` in
|
|
132
|
+
that session raises, because that "message" has already ended. If you want the
|
|
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:
|
|
136
|
+
the viewer only displays and groups by them, so encoding meaning into them
|
|
137
|
+
buys nothing. Hardcoded ids like `"msg-1"` are fine for demo scripts; `uuid4`
|
|
138
|
+
is the right default for real applications.
|
|
139
|
+
|
|
140
|
+
## One flow, or several at once
|
|
141
|
+
|
|
142
|
+
`init()` installs the client it returns as the module default. With a single
|
|
143
|
+
flow — the common case — **ignore the return value** and call
|
|
144
|
+
`wizardflow.log(...)` / `wizardflow.end_message(...)` directly: they work from
|
|
145
|
+
any module, with no handle to thread through your code.
|
|
146
|
+
|
|
147
|
+
Running several agent flows side by side (say, a doctor flow and a patient
|
|
148
|
+
flow) means several traces, and each needs its own instance. Keep the returned
|
|
149
|
+
client per flow and log through it — and name it **`tracer`**, not `client`,
|
|
150
|
+
since `client` in an agent codebase is almost always the LLM client:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
doctor_tracer = wizardflow.init(file_prefix="doctor", nodes=[...], edges=[...])
|
|
154
|
+
patient_tracer = wizardflow.init(file_prefix="patient", nodes=[...], edges=[...])
|
|
155
|
+
|
|
156
|
+
doctor_tracer.log(doc_msg, "diagnose", "llm_input", prompt)
|
|
157
|
+
patient_tracer.log(pat_msg, "intake", "llm_input", prompt)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
With more than one tracer, don't mix in the bare module-level calls:
|
|
161
|
+
`wizardflow.log(...)` always targets the most recently initialized tracer.
|
|
162
|
+
|
|
163
|
+
## Node descriptions, labels & colors
|
|
164
|
+
|
|
165
|
+
Nodes can carry a short human description of what they do. In the viewer it
|
|
166
|
+
stays out of the way: when a node is selected, a small info icon appears next
|
|
167
|
+
to its name in the inspector — click it to read the description. Nodes without
|
|
168
|
+
one show no icon, and `wizardflow md` / `wizardflow html` list the described
|
|
169
|
+
nodes under the graph.
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
wizardflow.init(
|
|
173
|
+
nodes=["router", "retriever", "generator"],
|
|
174
|
+
edges=[("router", "retriever"), ("retriever", "generator")],
|
|
175
|
+
node_descriptions={
|
|
176
|
+
"router": "Chooses the next step.",
|
|
177
|
+
"retriever": "Fetches relevant documents.",
|
|
178
|
+
"generator": "Writes the final answer.",
|
|
179
|
+
},
|
|
180
|
+
)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Two sibling kwargs work the same way: `node_labels` maps ids to the display
|
|
184
|
+
name the viewer shows instead of the raw id (especially useful with
|
|
185
|
+
`init_from_langgraph`, where the extracted ids are your function names), and
|
|
186
|
+
`node_colors` sets accent colors. All three are validated against the declared
|
|
187
|
+
nodes: an unknown id raises a `WizardFlowError` at `init()` time so typos fail
|
|
188
|
+
fast; with `silent=True` it is logged as a warning on the `wizardflow` logger
|
|
189
|
+
and skipped instead. All three also work on `init_from_langgraph`, where they
|
|
190
|
+
attach to the extracted node ids — `log()` always targets the **id**, never
|
|
191
|
+
the label.
|
|
104
192
|
|
|
105
193
|
## LangGraph: automatic topology
|
|
106
194
|
|
|
@@ -133,8 +221,8 @@ wizardflow.init_from_langgraph(
|
|
|
133
221
|
```
|
|
134
222
|
|
|
135
223
|
By default, a color key that does not match an extracted node id raises a
|
|
136
|
-
`WizardFlowError` so typos fail fast. With `silent=True`, unknown color keys
|
|
137
|
-
|
|
224
|
+
`WizardFlowError` so typos fail fast. With `silent=True`, unknown color keys
|
|
225
|
+
are skipped and logged as a warning instead.
|
|
138
226
|
|
|
139
227
|
It extracts node ids (keeping `__start__` / `__end__`) and directed edges, and
|
|
140
228
|
marks runtime branches with `"conditional": true` (deterministic and parallel
|
|
@@ -155,30 +243,39 @@ A non-LangGraph object raises `LangGraphExtractionError`; missing conditional
|
|
|
155
243
|
metadata never fails extraction (the edge is just emitted plain). Call it
|
|
156
244
|
**after `compile()`**, when the topology actually exists.
|
|
157
245
|
|
|
158
|
-
## API
|
|
246
|
+
## API
|
|
159
247
|
|
|
160
|
-
|
|
161
|
-
> appending — the old single-document `.json` format is gone from the SDK
|
|
162
|
-
> (writer *and* CLI readers). The web UI at getwizardflow.com still opens old
|
|
163
|
-
> `.json` traces.
|
|
164
|
-
|
|
165
|
-
- `init(output_dir=, file_prefix="wizardflow", name=, description=, nodes=, edges=, meta=, silent=False, max_bytes=16_000_000, max_messages=2_000) -> Client`
|
|
248
|
+
- `init(output_dir=, file_prefix="wizardflow", name=, description=, nodes=, edges=, node_labels=, node_colors=, node_descriptions=, meta=, silent=False, max_bytes=16_000_000, max_messages=2_000) -> Client`
|
|
166
249
|
- `description` lands in `meta.description` (matches the schema field).
|
|
167
250
|
- `nodes=` enables fast-fail: `log()` to an undeclared node raises
|
|
168
251
|
`UnknownNodeError` immediately (unless silenced).
|
|
252
|
+
- `node_labels` / `node_colors` / `node_descriptions` map declared node ids
|
|
253
|
+
to a display name / CSS color / short description; an unknown id raises
|
|
254
|
+
unless silenced (then it's logged as a warning and skipped).
|
|
169
255
|
- `output_dir` is optional; omitted, traces are written in cwd.
|
|
170
256
|
- `file_prefix` is optional; omitted, filenames start with `wizardflow`.
|
|
171
257
|
- `max_bytes` / `max_messages` cap each part file before rotation (see below).
|
|
172
|
-
- `init_from_langgraph(app, output_dir=, file_prefix="wizardflow", name=, description=, meta=, node_colors=, silent=False, max_bytes=..., max_messages=...) -> Client`
|
|
258
|
+
- `init_from_langgraph(app, output_dir=, file_prefix="wizardflow", name=, description=, meta=, node_labels=, node_colors=, node_descriptions=, silent=False, max_bytes=..., max_messages=...) -> Client`
|
|
173
259
|
- same as `init`, but `nodes`/`edges` come from `app.get_graph()`.
|
|
174
|
-
- `
|
|
260
|
+
- `node_labels` renames extracted ids for display (they're your function
|
|
261
|
+
names); `node_colors` maps them to CSS colors such as `"#A78BFA"`;
|
|
262
|
+
`node_descriptions` to short descriptions.
|
|
175
263
|
- `log(id, node, label=None, content=None)` — the first positional is the
|
|
176
264
|
**message id**, the second is the node. With `label`/`content` it records a
|
|
177
265
|
payload; bare `log(id, "node")` records a visit with no payloads. The message
|
|
178
266
|
is created on first reference; this only accumulates in memory.
|
|
179
|
-
- `end_message(id, title=None)` — finalize a message and append it
|
|
180
|
-
trace; returns the current trace path. The **only** call that touches
|
|
181
|
-
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.
|
|
272
|
+
- `reinit(name=None, description=None, meta=None)` — start a **new trace
|
|
273
|
+
file** (fresh timestamped name), keeping the graph and output configuration.
|
|
274
|
+
Use it at natural boundaries of a long-lived process — a new user session, a
|
|
275
|
+
new day. The old file is left as-is (**no seal**: it's a finished run, not a
|
|
276
|
+
rotated part). Open messages carry over and are written wherever they end;
|
|
277
|
+
completed message ids become reusable. `name`/`description`/`meta` replace
|
|
278
|
+
the current values when given. Returns the new trace path.
|
|
182
279
|
- `Client.current_path` — the trace file currently being written.
|
|
183
280
|
- `to_dict()` / `to_json()` — inspect the active part (completed messages),
|
|
184
281
|
assembled into one `AgentTraceFile` object.
|
|
@@ -204,6 +301,10 @@ unparseable final line (a crash mid-append leaves a torn tail; everything
|
|
|
204
301
|
before it is intact). Only **completed** messages are written; an in-progress
|
|
205
302
|
message lives in memory until it ends.
|
|
206
303
|
|
|
304
|
+
> Early SDK versions (0.1) wrote a trace as one single-document `.json` file
|
|
305
|
+
> instead. Newer versions write JSONL only, but every reader — the CLI
|
|
306
|
+
> subcommands and the web UI at getwizardflow.com — still opens both formats.
|
|
307
|
+
|
|
207
308
|
### How saving works
|
|
208
309
|
|
|
209
310
|
`end_message` **appends one line** to the active part — O(1) no matter how
|
|
@@ -265,6 +366,20 @@ Consecutive `log()` calls to the **same** node within a message fold into a
|
|
|
265
366
|
single step with multiple payloads (e.g. a router step carrying both
|
|
266
367
|
`llm_input` and `llm_output`). A `log()` to a different node starts a new step.
|
|
267
368
|
|
|
369
|
+
## What to log (conventions)
|
|
370
|
+
|
|
371
|
+
Payload labels are free-form strings — the schema reserves none of them. These
|
|
372
|
+
conventions just keep traces consistent and readable:
|
|
373
|
+
|
|
374
|
+
- **LLM-backed nodes** — log the exact prompt as `llm_input` and the raw
|
|
375
|
+
completion as `llm_output`. When a run goes wrong, the trace then shows
|
|
376
|
+
precisely what the model saw and what it said — most of what you need to
|
|
377
|
+
debug an agent — and step folding merges the pair into one step.
|
|
378
|
+
- **Everything else** — log the node's real domain data under honest labels: a
|
|
379
|
+
retriever logs its `query` and `results`, a tool node `tool_input` and
|
|
380
|
+
`tool_output`, a deterministic router its `decision`. Don't force
|
|
381
|
+
`llm_input`/`llm_output` onto nodes that never call a model.
|
|
382
|
+
|
|
268
383
|
## Examples
|
|
269
384
|
|
|
270
385
|
Fuller runnable examples live in
|
|
@@ -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.`
|