From ee3a1a95ef72972764b66e9db4aa240af6b94697 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 14 Mar 2024 10:07:15 +1100 Subject: [PATCH] fix(ui): control adapters require control images There wasn't enough validation of control adapters during graph building. It would be possible for a graph to be built with empty collect node, causing an error. Addressed with an extra check. This should never happen in practice, because the invoke button should be disabled if an invalid CA is active. --- .../nodes/util/graph/addControlNetToLinearGraph.ts | 10 ++++++++-- .../nodes/util/graph/addIPAdapterToLinearGraph.ts | 9 ++++++--- .../nodes/util/graph/addT2IAdapterToLinearGraph.ts | 10 ++++++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addControlNetToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/addControlNetToLinearGraph.ts index 12a163dd79..b5dad9e080 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addControlNetToLinearGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/addControlNetToLinearGraph.ts @@ -18,7 +18,13 @@ export const addControlNetToLinearGraph = async ( baseNodeId: string ): Promise => { const validControlNets = selectValidControlNets(state.controlAdapters).filter( - (ca) => ca.model?.base === state.generation.model?.base + ({ model, processedControlImage, processorType, controlImage, isEnabled }) => { + const hasModel = Boolean(model); + const doesBaseMatch = model?.base === state.generation.model?.base; + const hasControlImage = (processedControlImage && processorType !== 'none') || controlImage; + + return isEnabled && hasModel && doesBaseMatch && hasControlImage; + } ); // const metadataAccumulator = graph.nodes[METADATA_ACCUMULATOR] as @@ -83,7 +89,7 @@ export const addControlNetToLinearGraph = async ( image_name: controlImage, }; } else { - // Skip ControlNets without an unprocessed image - should never happen if everything is working correctly + // Skip CAs without an unprocessed image - should never happen, we already filtered the list of valid CAs return; } diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addIPAdapterToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/addIPAdapterToLinearGraph.ts index 7328b8f868..a76265f69a 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addIPAdapterToLinearGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/addIPAdapterToLinearGraph.ts @@ -17,9 +17,12 @@ export const addIPAdapterToLinearGraph = async ( graph: NonNullableGraph, baseNodeId: string ): Promise => { - const validIPAdapters = selectValidIPAdapters(state.controlAdapters).filter( - (ca) => ca.model?.base === state.generation.model?.base - ); + const validIPAdapters = selectValidIPAdapters(state.controlAdapters).filter(({ model, controlImage, isEnabled }) => { + const hasModel = Boolean(model); + const doesBaseMatch = model?.base === state.generation.model?.base; + const hasControlImage = controlImage; + return isEnabled && hasModel && doesBaseMatch && hasControlImage; + }); if (validIPAdapters.length) { // Even though denoise_latents' ip adapter input is collection or scalar, keep it simple and always use a collect diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addT2IAdapterToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/addT2IAdapterToLinearGraph.ts index a77897588f..91a4beded7 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addT2IAdapterToLinearGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/addT2IAdapterToLinearGraph.ts @@ -18,7 +18,13 @@ export const addT2IAdaptersToLinearGraph = async ( baseNodeId: string ): Promise => { const validT2IAdapters = selectValidT2IAdapters(state.controlAdapters).filter( - (ca) => ca.model?.base === state.generation.model?.base + ({ model, processedControlImage, processorType, controlImage, isEnabled }) => { + const hasModel = Boolean(model); + const doesBaseMatch = model?.base === state.generation.model?.base; + const hasControlImage = (processedControlImage && processorType !== 'none') || controlImage; + + return isEnabled && hasModel && doesBaseMatch && hasControlImage; + } ); if (validT2IAdapters.length) { @@ -77,7 +83,7 @@ export const addT2IAdaptersToLinearGraph = async ( image_name: controlImage, }; } else { - // Skip ControlNets without an unprocessed image - should never happen if everything is working correctly + // Skip CAs without an unprocessed image - should never happen, we already filtered the list of valid CAs return; }