diff --git a/docs/code/agents.md b/docs/code/agents.md index 0a10d6a..8286b6f 100644 --- a/docs/code/agents.md +++ b/docs/code/agents.md @@ -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 diff --git a/docs/interface/index.md b/docs/interface/index.md index bc5e618..5c49d9c 100644 --- a/docs/interface/index.md +++ b/docs/interface/index.md @@ -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 diff --git a/docs/reference/node-types.md b/docs/reference/node-types.md index 1d515d8..19a1f74 100644 --- a/docs/reference/node-types.md +++ b/docs/reference/node-types.md @@ -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 `_` 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