Document the wren node and the chat's Control tab

This commit is contained in:
2026-10-05 20:59:23 +02:00
parent afe0c94a24
commit aa7d9eece8
3 changed files with 100 additions and 0 deletions
+15
View File
@@ -114,6 +114,21 @@ configuration, and the bin in the panel's header starts it over. `MCP_ENABLED`
is not needed for it: the endpoint is for agents outside, the chat for the
one inside.
## Control
Where a live flow has a [`wren` node](../reference/node-types.md#wren) with
its `prompt` and `actions` wired, the chat gains a **Control** tab, and Home a
chat button at the top right of the brain graph (in the header, on a phone)
that opens it. No provider is needed for this one: the model is the node.
A command is published to the node's `prompt` message as a value from the
chat, and the answer is the next value of its `actions`: each call, the
message it went to, whether it was applied, and how sure the model was. A
failing node answers with its error, and one that says nothing for fifteen
seconds is reported as such. With several nodes, a picker in the header
chooses. The conversation is kept in the browser only, and a reload clears
it.
## A note on working this way
The pattern that works is the same one that works with a person: let the agent
+4
View File
@@ -53,6 +53,10 @@ its label showing so you can see which one it is without hovering.
Each flow also has a switch beside it in the list, which starts and stops it.
A [`wren` node](../reference/node-types.md#wren) anywhere adds a chat button
at the graph's top right, in the header on a phone, which opens the chat on
its [Control tab](../code/agents.md#control).
### Flows and dashboards
Two columns under the graph, exactly as tall as each other, most recently
+81
View File
@@ -290,6 +290,87 @@ reloads identically.
A worked example of numeric logic, not a modelling tool. To train something,
use a [batch flow](../concepts/runs.md) and a Function node.
## Language
### Wren
**`wren`** — turn a typed command into values for the devices wired to it.
[Wren](https://github.com/stroblme/wren) is a small language model that runs
on the engine's CPU: about 40 MB, a fifth of a second per command. It reads the
house as text, the areas, the devices in each and their states, and answers a
command with calls on those devices. It is a separate package, installed into
the engine with `pip install wren`; without it the node says so on the canvas.
| Input | Type | Notes |
|---|---|---|
| `prompt` | `str` | the command, and the only input that runs the node |
| `presence` | `str` | the area the user is in; anything else reads as away |
| `outdoor` | `float` | °C; 20 without it |
| `dark` | `bool` | without it, dark before 07:00 and from 18:00 |
The outputs are `actions`, a list of records, and any number of **targets**. A
target is an output named `<area>_<kind>` and bound to the message its device
listens on: `living_room_setpoint` bound to `home.setpoint` is the living
room's heating. The wiring is the allowlist, so the model can write nothing
that is not wired to the node.
- **area**: `living_room`, `kitchen`, `bathroom`, `bedroom`, `office`,
`garden`, `patio`, `garage`, `driveway`, `balcony`
- **kind**: `light`, `lamp`, `brightness` for a light; `setpoint`, `heating`,
`heat`, `boost` for heating; `media`, `music`, `tv`; `shutter`, `blind`,
`cover`; `water`, `watering`, `sprinkler`
A port naming anything else is refused when the flow is built. The editor
names a port after its message, so there a target's message carries the name;
binding a port to a message named otherwise, as `{"name": "setpoint", "port":
"living_room_setpoint"}`, takes the flow API.
Targets of one area and kind are one device to the model, and each takes what
a call means for its type. A target's current value is the state the model
reads.
| Call | `bool` | number | `str` |
|---|---|---|---|
| `lights.on` / `off` | `true` / `false` | `100` / `0` | `on` / `off` |
| `media.play` / `stop` | `true` / `false` | — | `play` / `stop` |
| `heating.set` | — | the temperature | — |
| `heating.boost` / `off` | `true` / `false` | — | `boost` / `off` |
| `shutters.up` / `down` | `true` / `false` | `100` / `0` | `up` / `down` |
| `shutters.stop` | — | — | `stop` |
| `watering.start` / `stop` | `true` / `false` | — | `on` / `off` |
| Setting | Default | Notes |
|---|---|---|
| `apply` | `false` | write to the targets; off, the node only reports |
| `min_confidence` | `0.93` | below this nothing is written |
Each record in `actions` is `{message, value, applied, delay_s, call,
confidence}`, one per call and target. A call nothing is wired for, such as
`sensors.temperature` or a light in a room without one, gets a record with an
empty `message`. The confidence is the lowest probability the model gave any
part of its answer: at the default threshold, nine in ten right answers pass,
and 38 of the 40 wrong ones in the model's own evaluation do not.
A call with a delay ("in 10m") is held by the engine's work queue, like the
Trigger node's second value: it survives a restart where the queue is in
Redis, and not otherwise.
Every house the model was trained on had a living room with a light, media
and heating, and a light in every other area. The node fills in what is not
wired, so the model reads a house it knows; a call on one of those is
reported, never applied.
!!! note "What the current model cannot do"
No levels: a light is on or off, never dimmed, coloured or half open.
Delays are whole minutes. Only one device per kind and area. Only the ten
areas above: one it was not trained on is answered confidently and wrongly,
which is why such a port is refused rather than tried. Commands are English,
phrased roughly like "turn on the lights in the kitchen".
The chat's [Control tab](../code/agents.md#control) sends commands by hand.
## Connectors
Anything else in the palette came from an installed **connector** package: a