refactor(ui): make all readiness checking async

This fixes the broken readiness checks introduced in the previous commit.

To support async batch generators, all of the validation of the generators needs to be async. This is problematic because a lot of the validation logic was in redux selectors, which are necessarily synchronous.

To resolve this, the readiness checks and related logic are restructured to be run async in response to redux state changes via `useEffect` (another option is to directly subscribe to redux store). These async functions then set some react state. The checks are debounced to prevent thrashing the UI.

See #7580 for more context about this issue.

Other changes:
- Fix a minor issue where empty collections were also checked against their min and max sizes, and errors were shown for all the checks. If a collection is empty, we don't need to do the min/max checks. If a collection is empty, we skip the other min/max checks and do not report those errors to the user.
- When a field is connected, do not attempt to check its value. This fixes an issue where collection fields with a connection could erroneously appear to be invalid.
- Improved error messages for batch nodes.
This commit is contained in:
psychedelicious
2025-02-26 09:25:56 +10:00
parent 43349cb5ce
commit 2e13bbbe1b
9 changed files with 210 additions and 174 deletions

View File

@@ -16,13 +16,13 @@ export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) =
enqueueRequested.match(action) && action.payload.tabName === 'workflows',
effect: async (action, { getState, dispatch }) => {
const state = getState();
const nodes = selectNodesSlice(state);
const nodesState = selectNodesSlice(state);
const workflow = state.workflow;
const templates = $templates.get();
const graph = buildNodesGraph(state, templates);
const builtWorkflow = buildWorkflowWithValidation({
nodes: nodes.nodes,
edges: nodes.edges,
nodes: nodesState.nodes,
edges: nodesState.edges,
workflow,
});
@@ -33,7 +33,7 @@ export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) =
const data: Batch['data'] = [];
const invocationNodes = nodes.nodes.filter(isInvocationNode);
const invocationNodes = nodesState.nodes.filter(isInvocationNode);
const batchNodes = invocationNodes.filter(isBatchNode);
// Handle zipping batch nodes. First group the batch nodes by their batch_group_id
@@ -44,9 +44,11 @@ export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) =
const zippedBatchDataCollectionItems: NonNullable<Batch['data']>[number] = [];
for (const node of batchNodes) {
const value = resolveBatchValue(node, invocationNodes, nodes.edges);
const value = await resolveBatchValue({ nodesState, node, dispatch });
const sourceHandle = node.data.type === 'image_batch' ? 'image' : 'value';
const edgesFromBatch = nodes.edges.filter((e) => e.source === node.id && e.sourceHandle === sourceHandle);
const edgesFromBatch = nodesState.edges.filter(
(e) => e.source === node.id && e.sourceHandle === sourceHandle
);
if (batchGroupId !== 'None') {
// If this batch node has a batch_group_id, we will zip the data collection items
for (const edge of edgesFromBatch) {