Add async-tasks UIWorker example
A UIWorker with a custom reply tool fans research out to three BaseWorker peers via start_user_job_group; their progress streams to the client as ui-task cards and the user can cancel a group mid-flight.
This commit is contained in:
90
examples/multi-worker/ui-worker/async-tasks/README.md
Normal file
90
examples/multi-worker/ui-worker/async-tasks/README.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# async-tasks
|
||||
|
||||
The UIWorker fans out long-running work to multiple peer workers in
|
||||
parallel, streams their progress to an in-flight panel on the page, and
|
||||
lets the user cancel mid-flight.
|
||||
|
||||
## What it shows
|
||||
|
||||
- The **`user_job_group` / `start_user_job_group`** API: dispatching
|
||||
parallel work to multiple peer workers and automatically forwarding
|
||||
every job lifecycle event to the client. The `reply` tool calls
|
||||
`start_user_job_group("wikipedia", "news", "scholar", payload=...,
|
||||
label=...)` and the `UIWorker` does the rest.
|
||||
- The four **`ui-task` envelopes** the worker forwards (`group_started`,
|
||||
`task_update`, `task_completed`, `group_completed`) and the
|
||||
client-side `RTVIEvent.UITask` event for consuming them. The client
|
||||
keeps a state map keyed by `task_id` and renders per-worker progress.
|
||||
- **Cancellation**: the in-flight card's Cancel button calls
|
||||
`client.cancelUITask(task_id, reason)`. The reserved `__cancel_task`
|
||||
event is translated by the `UIWorker` into `cancel_job_group(task_id)`
|
||||
on the registered group; cancelled workers report status `cancelled`.
|
||||
- **Background dispatch from a tool**: `start_user_job_group` returns
|
||||
immediately so the `reply` tool can speak its acknowledgement
|
||||
("Researching the Mariana Trench now") while the workers run — the
|
||||
main LLM is free to take follow-up turns.
|
||||
|
||||
## What it adds vs. the prior demos
|
||||
|
||||
The other examples use the request/response half of the bus protocol
|
||||
(main LLM → UIWorker → reply). This one adds the streaming job-group
|
||||
half: UIWorker → peer workers → progress events forwarded to the client.
|
||||
The architecture grows from "one delegate" to "one delegate plus a
|
||||
worker pool" — the peers are plain `BaseWorker`s launched on the runner.
|
||||
|
||||
## Run
|
||||
|
||||
Two terminals.
|
||||
|
||||
**Terminal 1 — bot:**
|
||||
|
||||
```bash
|
||||
cd examples/multi-worker/ui-worker/async-tasks
|
||||
uv run python bot.py
|
||||
```
|
||||
|
||||
The bot starts on `http://localhost:7860`.
|
||||
|
||||
**Terminal 2 — client:**
|
||||
|
||||
```bash
|
||||
cd examples/multi-worker/ui-worker/async-tasks/client
|
||||
npm install # one-time
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open `http://localhost:5173` and click **Connect**.
|
||||
|
||||
## What to try
|
||||
|
||||
The workers are simulated (canned summaries, randomized `asyncio.sleep`
|
||||
delays) so the demo focuses on the protocol, not the AI. Each research
|
||||
call takes a few seconds.
|
||||
|
||||
- _"Research the Mariana Trench."_ — the worker spawns three peers,
|
||||
acknowledges in one short reply, and a card appears showing each
|
||||
peer's status as it progresses (searching → found N results →
|
||||
summarizing → completed).
|
||||
- _"Look up octopus cognition."_ — same flow; a second card stacks.
|
||||
- _"Research the moon, then research Mars."_ — two groups run
|
||||
concurrently.
|
||||
- _"How are you?"_ (no research) — quick reply, no job group.
|
||||
- **Click Cancel on an in-flight card** — the cancellation routes
|
||||
through, the peers' tasks raise `CancelledError`, and their responses
|
||||
come back as `cancelled`.
|
||||
|
||||
## Requirements
|
||||
|
||||
- `OPENAI_API_KEY`
|
||||
- `DEEPGRAM_API_KEY`
|
||||
- `CARTESIA_API_KEY`
|
||||
|
||||
A `.env` in the example folder is the easiest way to set these (see
|
||||
`examples/multi-worker/env.example`).
|
||||
|
||||
## What this example _doesn't_ show
|
||||
|
||||
Real worker integrations (the peers are simulated), LLM-driven peers
|
||||
(these are pure data-fetch — a peer can itself be an `LLMWorker`),
|
||||
streaming chunks (`send_job_stream_data` for progressive output), or
|
||||
worker-to-worker fan-out (nested job groups).
|
||||
Reference in New Issue
Block a user