Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: publish misc #7907

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion invokeai/app/invocations/infill.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,16 @@ def infill(self, image: Image.Image):
return infilled


LAMA_MODEL_URL = "https://github.com/Sanster/models/releases/download/add_big_lama/big-lama.pt"


@invocation("infill_lama", title="LaMa Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.2")
class LaMaInfillInvocation(InfillImageProcessorInvocation):
"""Infills transparent areas of an image using the LaMa model"""

def infill(self, image: Image.Image):
with self._context.models.load_remote_model(
source="https://github.com/Sanster/models/releases/download/add_big_lama/big-lama.pt",
source=LAMA_MODEL_URL,
loader=LaMA.load_jit_model,
) as model:
lama = LaMA(model)
Expand Down
2 changes: 1 addition & 1 deletion invokeai/frontend/web/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1809,7 +1809,7 @@
"cannotPublish": "Cannot publish workflow",
"publishWarnings": "Warnings",
"errorWorkflowHasUnsavedChanges": "Workflow has unsaved changes",
"errorWorkflowHasBatchOrGeneratorNodes": "Workflow has batch and/or generator nodes",
"errorWorkflowHasUnpublishableNodes": "Workflow has batch, generator, or metadata extraction nodes",
"errorWorkflowHasInvalidGraph": "Workflow graph invalid (hover Invoke button for details)",
"errorWorkflowHasNoOutputNode": "No output node selected",
"warningWorkflowHasNoPublishableInputFields": "No publishable input fields selected - published workflow will run with only default values",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
$isSelectingOutputNode,
$outputNodeId,
$validationRunData,
selectHasUnpublishableNodes,
usePublishInputs,
} from 'features/nodes/components/sidePanel/workflow/publish';
import { useInputFieldTemplateTitleOrThrow } from 'features/nodes/hooks/useInputFieldTemplateTitleOrThrow';
Expand All @@ -36,7 +37,6 @@ import { useNodeUserTitleOrThrow } from 'features/nodes/hooks/useNodeUserTitleOr
import { useOutputFieldNames } from 'features/nodes/hooks/useOutputFieldNames';
import { useOutputFieldTemplate } from 'features/nodes/hooks/useOutputFieldTemplate';
import { useZoomToNode } from 'features/nodes/hooks/useZoomToNode';
import { selectHasBatchOrGeneratorNodes } from 'features/nodes/store/selectors';
import { useEnqueueWorkflows } from 'features/queue/hooks/useEnqueueWorkflows';
import { $isReadyToEnqueue } from 'features/queue/store/readiness';
import { selectAllowPublishWorkflows } from 'features/system/store/configSlice';
Expand Down Expand Up @@ -201,7 +201,7 @@ const PublishWorkflowButton = memo(() => {
const isReadyToDoValidationRun = useStore($isReadyToDoValidationRun);
const isReadyToEnqueue = useStore($isReadyToEnqueue);
const doesWorkflowHaveUnsavedChanges = useDoesWorkflowHaveUnsavedChanges();
const hasBatchOrGeneratorNodes = useAppSelector(selectHasBatchOrGeneratorNodes);
const hasUnpublishableNodes = useAppSelector(selectHasUnpublishableNodes);
const outputNodeId = useStore($outputNodeId);
const isSelectingOutputNode = useStore($isSelectingOutputNode);
const inputs = usePublishInputs();
Expand Down Expand Up @@ -249,7 +249,7 @@ const PublishWorkflowButton = memo(() => {
return (
<PublishTooltip
isWorkflowSaved={!doesWorkflowHaveUnsavedChanges}
hasBatchOrGeneratorNodes={hasBatchOrGeneratorNodes}
hasUnpublishableNodes={hasUnpublishableNodes}
isReadyToEnqueue={isReadyToEnqueue}
hasOutputNode={outputNodeId !== null && !isSelectingOutputNode}
hasPublishableInputs={inputs.publishable.length > 0}
Expand All @@ -261,7 +261,7 @@ const PublishWorkflowButton = memo(() => {
!allowPublishWorkflows ||
!isReadyToEnqueue ||
doesWorkflowHaveUnsavedChanges ||
hasBatchOrGeneratorNodes ||
hasUnpublishableNodes ||
!isReadyToDoValidationRun ||
!(outputNodeId !== null && !isSelectingOutputNode)
}
Expand Down Expand Up @@ -330,7 +330,7 @@ export const StartPublishFlowButton = memo(() => {
const allowPublishWorkflows = useAppSelector(selectAllowPublishWorkflows);
const isReadyToEnqueue = useStore($isReadyToEnqueue);
const doesWorkflowHaveUnsavedChanges = useDoesWorkflowHaveUnsavedChanges();
const hasBatchOrGeneratorNodes = useAppSelector(selectHasBatchOrGeneratorNodes);
const hasUnpublishableNodes = useAppSelector(selectHasUnpublishableNodes);
const inputs = usePublishInputs();

const onClick = useCallback(() => {
Expand All @@ -340,7 +340,7 @@ export const StartPublishFlowButton = memo(() => {
return (
<PublishTooltip
isWorkflowSaved={!doesWorkflowHaveUnsavedChanges}
hasBatchOrGeneratorNodes={hasBatchOrGeneratorNodes}
hasUnpublishableNodes={hasUnpublishableNodes}
isReadyToEnqueue={isReadyToEnqueue}
hasOutputNode={true}
hasPublishableInputs={inputs.publishable.length > 0}
Expand All @@ -352,7 +352,7 @@ export const StartPublishFlowButton = memo(() => {
variant="ghost"
size="sm"
isDisabled={
!allowPublishWorkflows || !isReadyToEnqueue || doesWorkflowHaveUnsavedChanges || hasBatchOrGeneratorNodes
!allowPublishWorkflows || !isReadyToEnqueue || doesWorkflowHaveUnsavedChanges || hasUnpublishableNodes
}
>
{t('workflows.builder.publish')}
Expand All @@ -366,15 +366,15 @@ StartPublishFlowButton.displayName = 'StartPublishFlowButton';
const PublishTooltip = memo(
({
isWorkflowSaved,
hasBatchOrGeneratorNodes,
hasUnpublishableNodes,
isReadyToEnqueue,
hasOutputNode,
hasPublishableInputs,
hasUnpublishableInputs,
children,
}: PropsWithChildren<{
isWorkflowSaved: boolean;
hasBatchOrGeneratorNodes: boolean;
hasUnpublishableNodes: boolean;
isReadyToEnqueue: boolean;
hasOutputNode: boolean;
hasPublishableInputs: boolean;
Expand All @@ -396,8 +396,8 @@ const PublishTooltip = memo(
if (!isWorkflowSaved) {
_errors.push(t('workflows.builder.errorWorkflowHasUnsavedChanges'));
}
if (hasBatchOrGeneratorNodes) {
_errors.push(t('workflows.builder.errorWorkflowHasBatchOrGeneratorNodes'));
if (hasUnpublishableNodes) {
_errors.push(t('workflows.builder.errorWorkflowHasUnpublishableNodes'));
}
if (!isReadyToEnqueue) {
_errors.push(t('workflows.builder.errorWorkflowHasInvalidGraph'));
Expand All @@ -406,7 +406,7 @@ const PublishTooltip = memo(
_errors.push(t('workflows.builder.errorWorkflowHasNoOutputNode'));
}
return _errors;
}, [hasBatchOrGeneratorNodes, hasOutputNode, isReadyToEnqueue, isWorkflowSaved, t]);
}, [hasUnpublishableNodes, hasOutputNode, isReadyToEnqueue, isWorkflowSaved, t]);

if (errors.length === 0 && warnings.length === 0) {
return children;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { skipToken } from '@reduxjs/toolkit/query';
import { useAppSelector } from 'app/store/storeHooks';
import { $templates } from 'features/nodes/store/nodesSlice';
import {
selectNodes,
selectNodesSlice,
selectWorkflowFormNodeFieldFieldIdentifiersDeduped,
selectWorkflowId,
} from 'features/nodes/store/selectors';
import type { Templates } from 'features/nodes/store/types';
import type { FieldIdentifier } from 'features/nodes/types/field';
import { isBoardFieldType } from 'features/nodes/types/field';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { isBatchNode, isGeneratorNode, isInvocationNode } from 'features/nodes/types/invocation';
import { atom, computed } from 'nanostores';
import { useMemo } from 'react';
import { useGetBatchStatusQuery } from 'services/api/endpoints/queue';
Expand Down Expand Up @@ -108,3 +109,31 @@ export const useIsWorkflowPublished = () => {

return isPublished;
};

// These nodes are not allowed to be in published workflows because they dynamically generate model identifiers
const NODE_TYPE_PUBLISH_DENYLIST = [
'metadata_to_model',
'metadata_to_sdxl_model',
'metadata_to_vae',
'metadata_to_lora_collection',
'metadata_to_loras',
'metadata_to_sdlx_loras',
'metadata_to_controlnets',
'metadata_to_ip_adapters',
'metadata_to_t2i_adapters',
];

export const selectHasUnpublishableNodes = createSelector(selectNodes, (nodes) => {
for (const node of nodes) {
if (!isInvocationNode(node)) {
return true;
}
if (isBatchNode(node) || isGeneratorNode(node)) {
return true;
}
if (NODE_TYPE_PUBLISH_DENYLIST.includes(node.data.type)) {
return true;
}
}
return false;
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getElement } from 'features/nodes/components/sidePanel/builder/form-man
import type { NodesState } from 'features/nodes/store/types';
import type { FieldInputInstance } from 'features/nodes/types/field';
import type { AnyNode, InvocationNode, InvocationNodeData } from 'features/nodes/types/invocation';
import { isBatchNode, isGeneratorNode, isInvocationNode } from 'features/nodes/types/invocation';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { isContainerElement, isNodeFieldElement } from 'features/nodes/types/workflow';
import { uniqBy } from 'lodash-es';
import { assert } from 'tsafe';
Expand Down Expand Up @@ -85,10 +85,6 @@ export const selectMayRedo = createSelector(
(nodes) => nodes.future.length > 0
);

export const selectHasBatchOrGeneratorNodes = createSelector(selectNodes, (nodes) =>
nodes.filter(isInvocationNode).some((node) => isBatchNode(node) || isGeneratorNode(node))
);

export const selectWorkflowName = createNodesSelector((nodes) => nodes.name);

export const selectWorkflowId = createNodesSelector((workflow) => workflow.id);
Expand Down