Synthesis example: a ReplyToolMixin UIWorker adds a start_review tool that fans out to clarity/tone peers via start_user_job_group, translates each reviewer response into an add_note command in on_job_response, handles a client note_click event via @on_ui_event, and keeps history across turns.
document-review
The synthesis demo. A voice-driven workspace where the user reviews a draft article — combining the patterns from every prior demo into one application: snapshot reading, deixis (read + write), form-fill state-changing actions, async job-group fan-out with progress streaming, plus one custom command and one client-emitted event.
What it shows
- Read-side deixis: select a paragraph, ask "review this", and the worker grounds in the selected text.
- Async fan-out: a paragraph review spawns two peer workers (clarity
- tone) in parallel via
start_user_job_group. The in-flight card streams each worker's progress.
- tone) in parallel via
- Custom UI command: as each worker completes,
on_job_responseemits anadd_notecommand with the worker's feedback; the client renders a note attached to the reviewed paragraph. - State-changing actions: dictating a note fills the textarea and
clicks Save (
fills+clickfrom the bundledreplytool). - Write-side deixis: "where does it talk about rhythms?" → the worker
finds the paragraph and uses
select_textto put the page selection on it. - Client-emitted UI event: clicking a note sends a
note_clickevent back; the worker's@on_ui_event("note_click")handler dispatchesselect_textto jump to the paragraph. The round-trip event/command pattern. - Two LLM tools coexisting:
ReplyToolMixin'sreplyhandles normal turns; a customstart_reviewtool handles review kick-off. The prompt steers the model to pick one (single tool call per turn). on_job_responseinterception: the worker overrides this hook to translate reviewer responses intoadd_notecommands — the peers don't know they're driving a UI; the worker mediates.
What's new vs. the prior demos
| Prior demo | Pattern |
|---|---|
| hello-snapshot | snapshot streaming, voice/UI delegation |
| pointing | scroll + multi-highlight |
| deixis | bidirectional text selection |
| form-fill | fills + click |
| async-tasks | job-group fan-out + cancel |
This one stitches all five together, plus the two patterns no prior demo
touched: a custom UI command (add_note) and a custom
client-emitted event (note_click).
Run
Two terminals.
Terminal 1 — bot:
cd examples/multi-worker/ui-worker/document-review
uv run python bot.py
The bot starts on http://localhost:7860.
Terminal 2 — client:
cd examples/multi-worker/ui-worker/document-review/client
npm install # one-time
npm run dev
Open http://localhost:5173 and click Connect.
What to try
The article is a 6-paragraph draft seeded with one too-dense paragraph, one too-vague one, and one with absolutist tone problems.
Review flow (the centerpiece):
- Select the run-on paragraph, say "review this." — the worker acknowledges, the in-flight card appears, both reviewers tick through progress, and two notes attach to the paragraph (clarity flags the density).
- Select the absolutist paragraph, say "give me feedback." — tone flags the strong words.
Notes flow:
- "Add a note that this paragraph is too jargony." (with a paragraph selected) — the worker fills the textarea and clicks Save.
- Click any note in the panel — the page scrolls and selects the paragraph it was attached to.
Navigation:
- "Where does it talk about structured rhythms?" — the worker jumps to the paragraph by selecting it.
Cancellation:
- During a review, click Cancel on the in-flight card. The reviewers'
responses come back as
cancelled; feedback that already arrived stays as a note.
Requirements
OPENAI_API_KEYDEEPGRAM_API_KEYCARTESIA_API_KEY
A .env in the example folder is the easiest way to set these (see
examples/multi-worker/env.example).
What this example does not show
Real worker integrations (the reviewers compute simple text metrics — for
real LLM reviewers, swap them for LLMWorker subclasses whose
on_job_request runs the LLM with the paragraph text and a critique
prompt; everything else stays the same), note persistence, or
multi-document / multi-page flows.