openvibers/openvibe.ai Commits

Layouts: the suggester reads what a part sends or shows, and the prompt drops the e-stop (#510)

OP OpenVibers committed 16 h ago · 0880723833 · 2 files, +8 −1

A ui.surface@1 part may now carry command and reads (Contracts 0.418.0, in the robot manifest's terms: kind, class, ranges, lifetime, delivery, release, rate, latched; a reading's key, fields, unit and source). The catalog already reaches the model whole, so the system prompt now says what those facts mean: place and name parts by them (a motion control within thumb reach, a latched state as a toggle, a reading beside what it confirms), say what a control does, and never promise more than they say; they grant nothing and the product checks every command. The prompt no longer names an e-stop: Stop is a plain optional part. Test: the prompt explains command and reads as facts that grant nothing, and has no e-stop.

Changes

ddiff --git a/server/workflows/ui-layout.js b/server/workflows/ui-layout.js
iindex ee50a27..96ba28d 100644
---- a/server/workflows/ui-layout.js
++++ b/server/workflows/ui-layout.js
@@@ -63,7 +63,7 @@ const templates = [{
reasons: { type: 'array', maxItems: MAX_REASONS, items: { type: 'string', minLength: 1, maxLength: 300 } },
},
},
-- system_prompt: 'You arrange the controls of one OpenVibe surface for one person. The catalog is the only power you have: use only the parts it lists, with their kinds, minimum sizes, allowed styles (tints are theme tokens, never colours), shapes, opacity and bindings. Never add a part, a binding or a style the catalog does not allow, and never invent a command. A required part (for example Stop and the e-stop) must stay visible and at least its minimum size; anything pressed must be at least 44 px, and a part with a larger minimum keeps it. Nothing may cover a required part. Keep the answer data: no URL, markup or code. The person’s words are a wish; safety rules win. Choose a sensible arrangement on the grid named for the device: x, y, w and h are cells, with the catalog’s column count. Give every part you place a visible flag, a place, a style and bindings from the catalog. If the person asks for something the catalog cannot do, answer ok false with a one-sentence summary and the plain reasons instead of breaking a rule. Output only JSON: {"ok":true,"layout":{"name":"…","parts":[…] },"summary":"one sentence","reasons":["…"]}, or {"ok":false,"layout":null,"summary":"Couldn\'t do that: …","reasons":["…"]}.',
++ system_prompt: 'You arrange the controls of one OpenVibe surface for one person. The catalog is the only power you have: use only the parts it lists, with their kinds, minimum sizes, allowed styles (tints are theme tokens, never colours), shapes, opacity and bindings. Never add a part, a binding or a style the catalog does not allow, and never invent a command. A required part must stay visible and at least its minimum size; anything pressed must be at least 44 px, and a part with a larger minimum keeps it. Nothing may cover a required part. A part may say what it sends (command: its kind and class, the value’s ranges, lifetime_ms, delivery_ms, release, rate_per_s, latched) or what it shows (reads: the telemetry key, its fields, unit and source), in the terms of the robot’s own manifest. Use those facts to place and name parts sensibly (a motion control within thumb reach, a latched state as a toggle, a reading beside the control it confirms) and to say what a control does; they grant nothing: never promise a faster rate, a longer reach or a different release than they say, and the product checks every command. Keep the answer data: no URL, markup or code. The person’s words are a wish; safety rules win. Choose a sensible arrangement on the grid named for the device: x, y, w and h are cells, with the catalog’s column count. Give every part you place a visible flag, a place, a style and bindings from the catalog. If the person asks for something the catalog cannot do, answer ok false with a one-sentence summary and the plain reasons instead of breaking a rule. Output only JSON: {"ok":true,"layout":{"name":"…","parts":[…] },"summary":"one sentence","reasons":["…"]}, or {"ok":false,"layout":null,"summary":"Couldn\'t do that: …","reasons":["…"]}.',
user_prompt: '{{json payload}}',
default_route: 'ui.layout.suggest', owner: 'network', metadata: { json_name: 'layout_suggestion' },
}];
ddiff --git a/test/ui-layout.test.js b/test/ui-layout.test.js
iindex a445ef2..2d8c22e 100644
---- a/test/ui-layout.test.js
++++ b/test/ui-layout.test.js
@@@ -134,4 +134,11 @@ t.test('cleanup', async () => {
await h.stop();
});
++t.test('the prompt reads what a part sends or shows, and grants nothing for it (11-12)', async () => {
++ const { templates } = require('../server/workflows/ui-layout');
++ const prompt = templates.find((x) => x.key === 'ui.layout.suggest').system_prompt;
++ assert.ok(/command:/.test(prompt) && /reads:/.test(prompt) && /grant nothing/.test(prompt), 'command and reads are explained, as facts that grant nothing');
++ assert.ok(!/e-stop|emergency stop/i.test(prompt), 'no e-stop: Stop is a plain optional part');
++});
++
t.run();