Skip to content

feat(gui): make the Behavior Input node placeable and configurable - #132

Merged
gbradham merged 1 commit into
mainfrom
feat/behavior-input-node-ui
Aug 20, 2026
Merged

feat(gui): make the Behavior Input node placeable and configurable#132
gbradham merged 1 commit into
mainfrom
feat/behavior-input-node-ui

Conversation

@gbradham

Copy link
Copy Markdown
Member

Follow-up to #131. That PR landed the closed loop and proved it with an integration test — but the Behavior Input node had no presence in the app at all: no palette entry, no port configuration, no properties. The loop worked when a graph was built in Python and could not be built in the Builder, which is where experiments are actually authored.

The sharp one: ports

setup_node_ports falls back to ([">in"], [">out"]) for any type it doesn't list. BehaviorInputNode has zero inputs and four outputsActive, Behavior, On Enter, On Exit. So the fallback drew the wrong shape, and On Enter (output index 2) was unreachable: a connection drawn on the canvas would have wired Active instead. The exact connection #131's integration test makes programmatically could not be drawn by hand.

Maimu gets an entry in the same table, which only relabels its ports from in/out to exec/exec — its shape was already right by luck.

The behavior is picked, not typed

A mistyped label is silently inert. The node compares the emitted string against the configured one, so "freezng" simply never matches: the stimulus never fires and nothing anywhere says why. That is the worst failure mode available to a closed-loop stimulus, so the properties panel offers a dropdown of the loaded model's own vocabulary.

It stays an editable combo on purpose:

  • a graph can be authored before any model is loaded, which is normal
  • a label saved from a model that isn't currently loaded is preserved, not dropped

How the vocabulary gets there. Over LiveSignalBus, which is already the boundary between vision and the flow. The camera panel publishes the model's classes when inference goes live — the same place it already hands them to the preview overlay. The alternative, having the properties panel reach into the camera panel for a worker thread's attribute, would couple the flow editor to the vision UI's internals.

Also

  • A Behavior section in the node library, so the node can be placed at all
  • BehaviorInput in the interface category, so it styles like the other trigger nodes
  • The confirmation window as a spin box floored at 1 (one frame is no confirmation — the configuration the node exists to prevent), with the latency stated in the panel rather than only in the docstring: at 30 fps, 5 frames is about 167 ms on top of inference

Tests — 15 new

tests/unit/gui/test_behavior_input_editor.py: the palette offers the button; the ports are the node's real four with On Enter at index 2 and Active as data not exec; Maimu no longer takes the generic fallback; the dropdown is populated from the model; a saved behavior shows; a label saved before the model loaded survives; choosing and editing both persist; the panel opens with no bus at all (vision not running); the bus hands back a copy rather than its own list; and loading a model publishes its classes to the bus.

Verification

PYTHONPATH=src QT_QPA_PLATFORM=offscreen pytest tests/3642 passed, 3 skipped, ruff and black clean.

The node was registered with the flow engine and had no presence in the app:
no palette entry, no port configuration, no properties. So the closed loop
worked when a graph was built in Python and could not be built at all in the
Builder, which is where experiments are actually authored.

The port gap was the sharp one. setup_node_ports falls back to one generic
input and one generic output for an unlisted type, but this node has no inputs
and four outputs, so the fallback rendered the wrong shape and left On Enter
-- output index 2 -- unreachable. A connection drawn on the canvas would have
been wired to Active instead. Maimu gets an entry in the same table, which
only relabels its ports from in/out to exec/exec.

The behavior is chosen from a dropdown of the loaded model's own vocabulary
rather than typed. A mistyped label is silently inert: the node compares the
emitted string against the configured one and simply never matches, so the
stimulus never fires and nothing anywhere says why. The combo stays editable
so a graph can still be authored before any model is loaded, and a label saved
from a model that is not currently loaded is preserved rather than dropped.

The vocabulary reaches the editor over LiveSignalBus, which is already the
boundary between vision and the flow. The camera panel publishes the model's
classes when inference goes live, the same place it already hands them to the
preview overlay. The alternative -- the properties panel reaching into the
camera panel for a worker thread's attribute -- would couple the flow editor
to the vision UI's internals.

Confirmation window is exposed as a spin box floored at 1, with the latency
its docstring documents stated in the panel: at 30 fps, 5 frames is about
167 ms on top of inference.
@gbradham
gbradham merged commit 1799196 into main Aug 20, 2026
4 checks passed
@gbradham
gbradham deleted the feat/behavior-input-node-ui branch August 20, 2026 19:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant