mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
- Update most model identifiers to be `{key: string}` instead of name/base/type. Doesn't change the model select components yet. - Update model _parameters_, stored in redux, to be `{key: string, base: BaseModel}` - we need to store the base model to be able to check model compatibility. May want to store the whole config? Not sure...
138 lines
4.4 KiB
TypeScript
138 lines
4.4 KiB
TypeScript
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
import { useAppSelector } from 'app/store/storeHooks';
|
|
import {
|
|
selectControlAdapterAll,
|
|
selectControlAdaptersSlice,
|
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
|
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
|
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
|
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
|
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
|
import { isInvocationNode } from 'features/nodes/types/invocation';
|
|
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
|
import { selectSystemSlice } from 'features/system/store/systemSlice';
|
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
|
import i18n from 'i18next';
|
|
import { forEach } from 'lodash-es';
|
|
import { getConnectedEdges } from 'reactflow';
|
|
|
|
const selector = createMemoizedSelector(
|
|
[
|
|
selectControlAdaptersSlice,
|
|
selectGenerationSlice,
|
|
selectSystemSlice,
|
|
selectNodesSlice,
|
|
selectDynamicPromptsSlice,
|
|
activeTabNameSelector,
|
|
],
|
|
(controlAdapters, generation, system, nodes, dynamicPrompts, activeTabName) => {
|
|
const { initialImage, model, positivePrompt } = generation;
|
|
|
|
const { isConnected } = system;
|
|
|
|
const reasons: string[] = [];
|
|
|
|
// Cannot generate if not connected
|
|
if (!isConnected) {
|
|
reasons.push(i18n.t('parameters.invoke.systemDisconnected'));
|
|
}
|
|
|
|
if (activeTabName === 'img2img' && !initialImage) {
|
|
reasons.push(i18n.t('parameters.invoke.noInitialImageSelected'));
|
|
}
|
|
|
|
if (activeTabName === 'nodes') {
|
|
if (nodes.shouldValidateGraph) {
|
|
if (!nodes.nodes.length) {
|
|
reasons.push(i18n.t('parameters.invoke.noNodesInGraph'));
|
|
}
|
|
|
|
nodes.nodes.forEach((node) => {
|
|
if (!isInvocationNode(node)) {
|
|
return;
|
|
}
|
|
|
|
const nodeTemplate = nodes.templates[node.data.type];
|
|
|
|
if (!nodeTemplate) {
|
|
// Node type not found
|
|
reasons.push(i18n.t('parameters.invoke.missingNodeTemplate'));
|
|
return;
|
|
}
|
|
|
|
const connectedEdges = getConnectedEdges([node], nodes.edges);
|
|
|
|
forEach(node.data.inputs, (field) => {
|
|
const fieldTemplate = nodeTemplate.inputs[field.name];
|
|
const hasConnection = connectedEdges.some(
|
|
(edge) => edge.target === node.id && edge.targetHandle === field.name
|
|
);
|
|
|
|
if (!fieldTemplate) {
|
|
reasons.push(i18n.t('parameters.invoke.missingFieldTemplate'));
|
|
return;
|
|
}
|
|
|
|
if (fieldTemplate.required && field.value === undefined && !hasConnection) {
|
|
reasons.push(
|
|
i18n.t('parameters.invoke.missingInputForField', {
|
|
nodeLabel: node.data.label || nodeTemplate.title,
|
|
fieldLabel: field.label || fieldTemplate.title,
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
} else {
|
|
if (dynamicPrompts.prompts.length === 0 && getShouldProcessPrompt(positivePrompt)) {
|
|
reasons.push(i18n.t('parameters.invoke.noPrompts'));
|
|
}
|
|
|
|
if (!model) {
|
|
reasons.push(i18n.t('parameters.invoke.noModelSelected'));
|
|
}
|
|
|
|
selectControlAdapterAll(controlAdapters).forEach((ca, i) => {
|
|
if (!ca.isEnabled) {
|
|
return;
|
|
}
|
|
|
|
if (!ca.model) {
|
|
reasons.push(
|
|
i18n.t('parameters.invoke.noModelForControlAdapter', {
|
|
number: i + 1,
|
|
})
|
|
);
|
|
} else if (ca.model.base !== model?.base) {
|
|
// This should never happen, just a sanity check
|
|
reasons.push(
|
|
i18n.t('parameters.invoke.incompatibleBaseModelForControlAdapter', {
|
|
number: i + 1,
|
|
})
|
|
);
|
|
}
|
|
|
|
if (
|
|
!ca.controlImage ||
|
|
(isControlNetOrT2IAdapter(ca) && !ca.processedControlImage && ca.processorType !== 'none')
|
|
) {
|
|
reasons.push(
|
|
i18n.t('parameters.invoke.noControlImageForControlAdapter', {
|
|
number: i + 1,
|
|
})
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
return { isReady: !reasons.length, reasons };
|
|
}
|
|
);
|
|
|
|
export const useIsReadyToEnqueue = () => {
|
|
const { isReady, reasons } = useAppSelector(selector);
|
|
return { isReady, reasons };
|
|
};
|