Skip to content

feat(ui): add Use Cache and Save To Gallery to the form builder, make them connectable - #9456

Open
Pfannkuchensack wants to merge 8 commits into
invoke-ai:mainfrom
Pfannkuchensack:feat/node-settings-form-and-connections
Open

feat(ui): add Use Cache and Save To Gallery to the form builder, make them connectable#9456
Pfannkuchensack wants to merge 8 commits into
invoke-ai:mainfrom
Pfannkuchensack:feat/node-settings-form-and-connections

Conversation

@Pfannkuchensack

Copy link
Copy Markdown
Member

Summary

Feature (frontend + backend). Adds the two node footer toggles — Use Cache and Save To Gallery — to the workflow form builder, and makes their underlying fields connectable.

Why

Every other node control can be placed in a workflow's form. These two could not, because they are node attributes: their value lives on node.data.useCache / node.data.isIntermediate, not in node.data.inputs, so the existing node-field form element cannot address them.

How

1. A node-setting form element (features/nodes/types/workflow.ts)

Parallel to node-field, holding { nodeId, setting, label }. Added to the form the same way node fields are: a +/ button revealed on hover in the node footer, or by dragging the setting's label into the form. The label is editable on double-click — two nodes' "Use Cache" entries would otherwise be indistinguishable in a form.

Rendered as a Switch to match how BooleanField inputs render. Settings that no longer apply to their node are hidden in view mode and flagged in edit mode; use_cache stays admin-only, mirroring the node footer.

2. The fields become connection targets

BaseInvocation already declares both as ordinary pydantic fields, so the backend change is small: both now declare Input.Any, and is_intermediate drops its ui_type=_IsIntermediate, which had made it parse as its own field type that no BooleanField output could connect to.

The interesting part is that this needs no workflow migration:

before after
In invocation template filtered out parsed in (needed for handle + connection validation)
In node's input list still filtered out (rendered in the footer instead)
Field instance in node.data.inputs none still nonebuildInvocationNode skips them, and updateNode derives its allowed keys from there
Value storage node.data.useCache unchanged
buildNodesGraph writes the literal unchanged — the edge is emitted from state.edges and the backend overwrites the literal on resolution

Edge values are applied in GraphExecutionState.next() before the invocation is handed to the invoker, so a connected use_cache is resolved by the time the cache is consulted, and a connected is_intermediate by the time the image is saved.

3. Guards against edges that would do nothing

The footer hosts the handles, and useWithFooter only renders it for executable nodes with a gallery output. Connections to node attribute fields on any other node are rejected — there would be no handle to attach to, and buildNodesGraph drops batch and generator nodes entirely.

When an edge drives a setting, the node's checkbox is removed (matching how a connected input field renders) and the form's toggle is disabled, since the local value is no longer what the node runs with.

4. Layout

The footer became one row per setting so each connection handle lines up with its own label, following InputFieldWrapper. This makes affected nodes ~16px taller. UseCacheCheckbox / SaveToGalleryCheckbox are consolidated into one NodeSettingFooterControl; useIsBatchNode and useNodeHasGalleryOutput were absorbed into shared predicates in types/invocation.ts.

Notes for reviewers

  • use_cache is only reachable on nodes with a gallery output. That is pre-existing (useWithFooter = isExecutableNode && hasGalleryOutput) and this PR deliberately does not widen it — the connection guard just matches reality. Happy to change it if that gate is considered a bug.
  • The is_intermediate inversion remains. An edge carries is_intermediate, the UI says "Save To Gallery". Connecting a boolean node means thinking inverted. Fixing that properly means either a dedicated renderer or renaming the backend field to save_to_gallery; both felt out of scope here.
  • The Canvas workflow integration preview skips node settings — it renders a saved workflow, not the editor state the toggle would write to.

Related Issues / Discussions

QA Instructions

Form builder

  1. Workflow editor → a node with a gallery output (e.g. Latents to Image). Hover the footer → + buttons appear next to both settings.
  2. Click + on Use Cache → it appears in the form; the button becomes .
  3. Toggle it in the form → the node's checkbox follows, and vice versa.
  4. Double-click the label in the form builder → rename it; the custom label persists after reload.
  5. Drag the setting by its label into the form → same result as +.
  6. Delete the node → its settings disappear from the form.

Connections
7. Add a Boolean Primitive, drag from its Value output onto the Use Cache handle on the footer's left edge → the edge connects, and the checkbox disappears (the edge now supplies the value).
8. If the setting is also in the form, its toggle is now disabled.
9. Try connecting to a node with no gallery output (e.g. Add Integers) → no handle is offered.
10. Invoke → the connected value is what the node runs with.

Regression
11. Confirm use_cache / is_intermediate do not appear in any node's input list.
12. Load a workflow saved before this PR → unchanged; no migration warnings.

Merge Plan

Nothing special.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Tests added / updated (if applicable)
  • ❗Changes to a redux slice have a corresponding migration — n/a, additive only (see Merge Plan)
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

… them connectable

The two node footer toggles were the only node controls that could not be
added to a workflow's form, because they are node attributes stored on
`node.data` rather than input fields in `node.data.inputs`.

Add a `node-setting` form element type for them, with an add/remove button and
drag handle in the node footer mirroring how node fields are added. The label is
editable, since two nodes' "Use Cache" entries would otherwise be
indistinguishable in a form.

Also expose the underlying fields as connection targets. `BaseInvocation`
already declares `is_intermediate` and `use_cache` as pydantic fields, so they
only needed `Input.Any` and, for `is_intermediate`, dropping the `_IsIntermediate`
ui_type that prevented a BooleanField output from connecting. The frontend now
parses them into invocation templates - required for handles and connection
validation - but still filters them out of the node's input list and never
creates field instances for them. Their value keeps living on the node, so
`buildNodesGraph` is unchanged and no workflow migration is needed. Graph
execution applies edge values in `GraphExecutionState.next()`, before the cache
is consulted and before the output image is saved.

Node attribute fields are only reachable on nodes that render a footer, so
connections to them are rejected elsewhere - otherwise the edge would have no
handle to attach to and, on batch and generator nodes, no effect at all. When an
edge drives a setting, the node's checkbox is dropped and the form's toggle is
disabled, since the local value is no longer what the node runs with.
@github-actions github-actions Bot added python PRs that change python files invocations PRs that change invocations frontend PRs that change frontend files python-tests PRs that change python tests labels Aug 3, 2026
@lstein lstein added the 7.0.0 label Aug 8, 2026
@lstein lstein moved this to 7.0 Theme: Tabbed Layout UI in Invoke - Community Roadmap Aug 8, 2026
JPPhoto and others added 5 commits August 9, 2026 21:14
The main merge on this branch auto-resolved openapi.json in favor of main,
which reverted exactly what the PR changes: use_cache and is_intermediate went
back to "input": "direct" / "orig_required": true instead of the connectable
"input": "any" / "orig_required": false. That is why openapi-checks was red —
the committed schema no longer matched what the code emits.

Regenerated with generate_openapi_schema.py + prettier, matching the CI check
byte for byte. schema.ts was already correct and is untouched.

@JPPhoto JPPhoto left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge blockers:

  • invokeai/app/invocations/baseinvocation.py:267: Changes inherited is_intermediate and use_cache from non-connectable node attributes to Input.Any targets, changing every invocation schema. No invocation versions change. Effect: version-based workflow migration cannot detect the new input contract. Likelihood: all built-in invocations. Recovery: bump affected invocation versions and add compatibility tests. Test: compare changed OpenAPI input metadata against invocation decorator versions; current diff has no decorator version changes.

Other findings/issues:

  • invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeSettingElementEditMode.tsx:59: Edit mode renders an enabled use_cache switch without the admin guard retained in NodeSettingFooterControl. Effect: non-admin multiuser users can change and save the admin-only, process-global cache policy. Likelihood: any shared workflow containing an existing use_cache element. Recovery: admin must reset/remove the setting or clear cache. Test: render as non-admin, click the switch, assert no nodeUseCacheChanged dispatch.

  • invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowIntegration/WorkflowFormPreview.tsx:245: Every node-setting is silently returned as null, while Canvas integration still accepts forms containing these elements. Effect: saved forms show no Use Cache or Save To Gallery control; execution silently uses saved node values. Likelihood: any Canvas-integrated workflow with an ImageField plus node setting. Recovery: edit the workflow node directly or remove the form element. Test: select such a workflow and assert the preview renders the setting control; current branch renders nothing.

Suggestions:

  • Bump every affected invocation version and cover old/new workflow loading.

  • Instead of checking admin status only in the footer, enforce it in shared node-setting edit, view, and add paths.

  • Instead of silently dropping settings in Canvas preview, wire their values into Canvas execution or reject incompatible forms during selection.

Addresses review 4978552086 on invoke-ai#9456.

The form builder's edit mode rendered an enabled `use_cache` switch with
no admin check, so a shared workflow carrying that element let any
multiuser non-admin flip and save the process-global, admin-only cache
policy. The check existed twice as an ad-hoc `setting === 'use_cache' &&
!isAdmin` — in the node footer and in the form's view mode — and was
simply missing from edit mode. It now lives in one place,
`getIsNodeSettingPermitted` / `useIsNodeSettingPermitted`, which the
footer, view mode and edit mode all consult. The add-to-form and
drag-into-form paths live inside the footer control, so they are gated
by it too. Edit mode keeps the element visible and movable but replaces
the switch with a notice, the same treatment a non-applicable setting
already gets.

The Canvas integration silently returned null for every `node-setting`
element while still accepting forms that contain them, so a saved Use
Cache or Save To Gallery control was invisible and execution quietly ran
with whatever the workflow was saved with. Node settings are node
attributes, not inputs, so they cannot ride along in `fieldValues`: the
canvas slice now carries a separate `nodeSettingValues` record keyed
`"nodeId.setting"`, the preview renders a control for them under the
same applicability and permission rules as the editor, and
`resolveNodeSettings` applies the result when the executor builds each
invocation. `use_cache` overrides are applied for admins only — the
slice is persisted per browser, not per user. The canvas output node
stays intermediate regardless, since its images go to the staging area,
and the preview offers no control that would pretend otherwise.

No invocation version bumps. The review asked for one per affected
invocation, but a node instance's contract is unchanged: `use_cache` and
`is_intermediate` were previously dropped while parsing the schema and
are now parsed into the template but explicitly skipped by
`buildInvocationNode`, whose keys `updateNode` derives its allowlist
from. No field instance existed before and none exists now — the
serialized node is byte-identical, and a test on this branch pins that.
A bump would mark every node in every existing workflow as needing a
no-op update, would not help an older client (which drops an unknown
edge either way, and would fail the node update outright on a major
bump), and cannot be applied at all to the custom node packs that
inherit these fields from `BaseInvocation`.

Covered by unit tests for `getIsNodeSettingPermitted` and
`resolveNodeSettings`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

7.0.0 frontend PRs that change frontend files invocations PRs that change invocations python PRs that change python files python-tests PRs that change python tests

Projects

Status: 7.0 Theme: Tabbed Layout UI

Development

Successfully merging this pull request may close these issues.

3 participants