diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 8b5afce4a7..46c4ed4c4c 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -161,7 +161,9 @@ "txt2img": "Text To Image", "unifiedCanvas": "Unified Canvas", "unknown": "Unknown", - "upload": "Upload" + "upload": "Upload", + "prevPage": "Previous Page", + "nextPage": "Next Page" }, "controlnet": { "controlAdapter_one": "Control Adapter", @@ -1599,5 +1601,12 @@ "showIntermediates": "Show Intermediates", "snapToGrid": "Snap to Grid", "undo": "Undo" + }, + "workflows": { + "workflowLibrary": "Workflow Library", + "userCategory": "User", + "systemCategory": "System", + "loadWorkflow": "$t(nodes.loadWorkflow)", + "deleteWorkflow": "Delete Workflow" } } diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts index b9b1060f18..6b6063ee1c 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts @@ -3,6 +3,7 @@ import { buildNodesGraph } from 'features/nodes/util/graph/buildNodesGraph'; import { queueApi } from 'services/api/endpoints/queue'; import { BatchConfig } from 'services/api/types'; import { startAppListening } from '..'; +import { buildWorkflow } from 'features/nodes/util/workflow/buildWorkflow'; export const addEnqueueRequestedNodes = () => { startAppListening({ @@ -11,9 +12,11 @@ export const addEnqueueRequestedNodes = () => { effect: async (action, { getState, dispatch }) => { const state = getState(); const graph = buildNodesGraph(state.nodes); + const workflow = buildWorkflow(state.nodes); const batchConfig: BatchConfig = { batch: { graph, + workflow, runs: state.generation.iterations, }, prepend: action.payload.prepend, diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts index bd677ca6f1..fc02330836 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts @@ -99,7 +99,6 @@ export const addWorkflowLoadRequestedListener = () => { ); } else { // Some other error occurred - console.log(e); log.error( { error: parseify(e) }, t('nodes.unknownErrorValidatingWorkflow') diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx index 04c64918f9..beb90e30d3 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx @@ -16,6 +16,8 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIIconButton from 'common/components/IAIIconButton'; import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton'; import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice'; +import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems'; +import { sentImageToImg2Img } from 'features/gallery/store/actions'; import { workflowLoadRequested } from 'features/nodes/store/actions'; import ParamUpscalePopover from 'features/parameters/components/Parameters/Upscale/ParamUpscaleSettings'; import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters'; @@ -39,12 +41,12 @@ import { FaSeedling, } from 'react-icons/fa'; import { FaCircleNodes, FaEllipsis } from 'react-icons/fa6'; -import { useGetImageDTOQuery } from 'services/api/endpoints/images'; +import { + useGetImageDTOQuery, + useLazyGetImageWorkflowQuery, +} from 'services/api/endpoints/images'; import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata'; -import { useDebouncedWorkflow } from 'services/api/hooks/useDebouncedWorkflow'; import { menuListMotionProps } from 'theme/components/menu'; -import { sentImageToImg2Img } from 'features/gallery/store/actions'; -import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems'; const currentImageButtonsSelector = createSelector( [stateSelector, activeTabNameSelector], @@ -111,18 +113,17 @@ const CurrentImageButtons = () => { lastSelectedImage?.image_name ); - const { workflow, isLoading: isLoadingWorkflow } = useDebouncedWorkflow( - lastSelectedImage?.workflow_id - ); - + const [getWorkflow, getWorkflowResult] = useLazyGetImageWorkflowQuery(); const handleLoadWorkflow = useCallback(() => { - if (!workflow) { + if (!lastSelectedImage) { return; } - dispatch(workflowLoadRequested(workflow)); - }, [dispatch, workflow]); + getWorkflow(lastSelectedImage?.image_name).then((workflow) => { + dispatch(workflowLoadRequested(workflow.data)); + }); + }, [dispatch, getWorkflow, lastSelectedImage]); - useHotkeys('w', handleLoadWorkflow, [workflow]); + useHotkeys('w', handleLoadWorkflow, [lastSelectedImage]); const handleClickUseAllParameters = useCallback(() => { recallAllParameters(metadata); @@ -255,12 +256,12 @@ const CurrentImageButtons = () => { } tooltip={`${t('nodes.loadWorkflow')} (W)`} aria-label={`${t('nodes.loadWorkflow')} (W)`} - isDisabled={!workflow} + isDisabled={!imageDTO?.has_workflow} onClick={handleLoadWorkflow} + isLoading={getWorkflowResult.isLoading} /> { const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata( imageDTO?.image_name ); - const { workflow, isLoading: isLoadingWorkflow } = useDebouncedWorkflow( - imageDTO?.workflow_id - ); + + const [getWorkflow, getWorkflowResult] = useLazyGetImageWorkflowQuery(); + const handleLoadWorkflow = useCallback(() => { + getWorkflow(imageDTO.image_name).then((workflow) => { + dispatch(workflowLoadRequested(workflow.data)); + }); + }, [dispatch, getWorkflow, imageDTO]); const [starImages] = useStarImagesMutation(); const [unstarImages] = useUnstarImagesMutation(); @@ -101,13 +105,6 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => { recallSeed(metadata?.seed); }, [metadata?.seed, recallSeed]); - const handleLoadWorkflow = useCallback(() => { - if (!workflow) { - return; - } - dispatch(workflowLoadRequested(workflow)); - }, [dispatch, workflow]); - const handleSendToImageToImage = useCallback(() => { dispatch(sentImageToImg2Img()); dispatch(initialImageSelected(imageDTO)); @@ -179,9 +176,9 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => { {t('parameters.downloadImage')} : } + icon={getWorkflowResult.isLoading ? : } onClickCapture={handleLoadWorkflow} - isDisabled={isLoadingWorkflow || !workflow} + isDisabled={!imageDTO.has_workflow} > {t('nodes.loadWorkflow')} diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx index e4d286aee6..217fc973af 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx @@ -14,10 +14,10 @@ import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableCon import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata'; -import { useDebouncedWorkflow } from 'services/api/hooks/useDebouncedWorkflow'; import { ImageDTO } from 'services/api/types'; import DataViewer from './DataViewer'; import ImageMetadataActions from './ImageMetadataActions'; +import ImageMetadataWorkflowTabContent from './ImageMetadataWorkflowTabContent'; type ImageMetadataViewerProps = { image: ImageDTO; @@ -32,7 +32,6 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => { const { t } = useTranslation(); const { metadata } = useDebouncedMetadata(image.image_name); - const { workflow } = useDebouncedWorkflow(image.workflow_id); return ( { > {t('metadata.recallParameters')} - {t('metadata.metadata')} + {t('metadata.metadata')} {t('metadata.imageDetails')} - {t('metadata.workflow')} + {t('metadata.workflow')} @@ -97,11 +96,7 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => { )} - {workflow ? ( - - ) : ( - - )} + diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx new file mode 100644 index 0000000000..f719d22478 --- /dev/null +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx @@ -0,0 +1,23 @@ +import { IAINoContentFallback } from 'common/components/IAIImageFallback'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useGetImageWorkflowQuery } from 'services/api/endpoints/images'; +import { ImageDTO } from 'services/api/types'; +import DataViewer from './DataViewer'; + +type Props = { + image: ImageDTO; +}; + +const ImageMetadataWorkflowTabContent = ({ image }: Props) => { + const { t } = useTranslation(); + const { currentData: workflow } = useGetImageWorkflowQuery(image.image_name); + + if (!workflow) { + return ; + } + + return ; +}; + +export default memo(ImageMetadataWorkflowTabContent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryButton.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryButton.tsx new file mode 100644 index 0000000000..5e71652558 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryButton.tsx @@ -0,0 +1,25 @@ +import { useDisclosure } from '@chakra-ui/react'; +import IAIIconButton from 'common/components/IAIIconButton'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { FaFolderOpen } from 'react-icons/fa'; +import WorkflowLibraryModal from './WorkflowLibraryModal'; + +const WorkflowLibraryButton = () => { + const { t } = useTranslation(); + const { isOpen, onClose, onOpen } = useDisclosure(); + + return ( + <> + } + onClick={onOpen} + tooltip={t('workflows.workflowLibrary')} + aria-label={t('workflows.workflowLibrary')} + /> + + + ); +}; + +export default memo(WorkflowLibraryButton); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryCategories.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryCategories.tsx new file mode 100644 index 0000000000..21eaf645bc --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryCategories.tsx @@ -0,0 +1,40 @@ +import { ButtonGroup, Flex } from '@chakra-ui/react'; +import IAIButton from 'common/components/IAIButton'; +import { WorkflowCategory } from './types'; +import { Dispatch, SetStateAction, memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +type Props = { + category: WorkflowCategory; + setCategory: Dispatch>; +}; + +const WorkflowLibraryCategories = ({ category, setCategory }: Props) => { + const { t } = useTranslation(); + const handleClickUser = useCallback(() => { + setCategory('user'); + }, [setCategory]); + const handleClickSystem = useCallback(() => { + setCategory('system'); + }, [setCategory]); + return ( + + + + {t('workflows.userCategory')} + + + {t('workflows.systemCategory')} + + + + ); +}; + +export default memo(WorkflowLibraryCategories); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryContent.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryContent.tsx new file mode 100644 index 0000000000..bc80083b13 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryContent.tsx @@ -0,0 +1,38 @@ +import { Flex } from '@chakra-ui/react'; +import { WorkflowCategory } from './types'; +import { Dispatch, SetStateAction, memo } from 'react'; +import { paths } from 'services/api/schema'; +import WorkflowLibraryCategories from './WorkflowLibraryCategories'; +import WorkflowLibraryPagination from './WorkflowLibraryPagination'; +import WorkflowLibraryList from './WorkflowLibraryList'; + +type Props = { + data: paths['/api/v1/workflows/']['get']['responses']['200']['content']['application/json']; + category: WorkflowCategory; + setCategory: Dispatch>; + page: number; + setPage: Dispatch>; +}; + +const WorkflowLibraryContent = ({ + data, + category, + setCategory, + page, + setPage, +}: Props) => { + return ( + + + + + + + + ); +}; + +export default memo(WorkflowLibraryContent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryList.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryList.tsx new file mode 100644 index 0000000000..b965a010b4 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryList.tsx @@ -0,0 +1,45 @@ +import { Flex, Spacer, Text } from '@chakra-ui/react'; +import IAIIconButton from 'common/components/IAIIconButton'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { FaFolderOpen, FaTrash } from 'react-icons/fa'; +import { paths } from 'services/api/schema'; +import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent'; + +type Props = { + data: paths['/api/v1/workflows/']['get']['responses']['200']['content']['application/json']; +}; + +const WorkflowLibraryList = ({ data }: Props) => { + const { t } = useTranslation(); + + return ( + + + + {data.items.map((w) => ( + + + {w.workflow_id} + + } + aria-label={t('workflows.loadWorkflow')} + tooltip={t('workflows.loadWorkflow')} + /> + } + colorScheme="error" + aria-label={t('workflows.deleteWorkflow')} + tooltip={t('workflows.deleteWorkflow')} + /> + + + ))} + + + + ); +}; + +export default memo(WorkflowLibraryList); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryModal.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryModal.tsx new file mode 100644 index 0000000000..025d748aa0 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryModal.tsx @@ -0,0 +1,43 @@ +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalFooter, + ModalBody, + ModalCloseButton, +} from '@chakra-ui/react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import WorkflowLibraryWrapper from './WorkflowLibraryWrapper'; + +type Props = { + isOpen: boolean; + onClose: () => void; +}; + +const WorkflowLibraryModal = ({ isOpen, onClose }: Props) => { + const { t } = useTranslation(); + return ( + + + + {t('workflows.workflowLibrary')} + + + + + + + + ); +}; + +export default memo(WorkflowLibraryModal); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryPagination.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryPagination.tsx new file mode 100644 index 0000000000..40ee5b5d8b --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryPagination.tsx @@ -0,0 +1,88 @@ +import { ButtonGroup } from '@chakra-ui/react'; +import IAIButton from 'common/components/IAIButton'; +import IAIIconButton from 'common/components/IAIIconButton'; +import { Dispatch, SetStateAction, memo, useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { FaChevronLeft, FaChevronRight } from 'react-icons/fa'; +import { paths } from 'services/api/schema'; + +const PAGES_TO_DISPLAY = 7; + +type PageData = { + page: number; + onClick: () => void; +}; + +type Props = { + page: number; + setPage: Dispatch>; + data: paths['/api/v1/workflows/']['get']['responses']['200']['content']['application/json']; +}; + +const WorkflowLibraryPagination = ({ page, setPage, data }: Props) => { + const { t } = useTranslation(); + + const handlePrevPage = useCallback(() => { + setPage((p) => Math.max(p - 1, 0)); + }, [setPage]); + + const handleNextPage = useCallback(() => { + setPage((p) => Math.min(p + 1, data.pages - 1)); + }, [data.pages, setPage]); + + const pages: PageData[] = useMemo(() => { + const pages = []; + let first = + data.pages > PAGES_TO_DISPLAY + ? Math.max(0, page - Math.floor(PAGES_TO_DISPLAY / 2)) + : 0; + const last = + data.pages > PAGES_TO_DISPLAY + ? Math.min(data.pages, first + PAGES_TO_DISPLAY) + : data.pages; + if (last - first < PAGES_TO_DISPLAY && data.pages > PAGES_TO_DISPLAY) { + first = last - PAGES_TO_DISPLAY; + } + for (let i = first; i < last; i++) { + pages.push({ + page: i, + onClick: () => setPage(i), + }); + } + return pages; + }, [data.pages, page, setPage]); + + if (data.items.length === 0) { + return null; + } + + return ( + + } + /> + {pages.map((p) => ( + + {p.page + 1} + + ))} + } + /> + + ); +}; + +export default memo(WorkflowLibraryPagination); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryWrapper.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryWrapper.tsx new file mode 100644 index 0000000000..913388e86c --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryWrapper.tsx @@ -0,0 +1,31 @@ +import { memo, useState } from 'react'; +import { useListWorkflowsQuery } from 'services/api/endpoints/workflows'; +import WorkflowLibraryContent from './WorkflowLibraryContent'; +import { WorkflowCategory } from './types'; + +const PER_PAGE = 10; + +const WorkflowLibraryWrapper = () => { + const [page, setPage] = useState(0); + const [category, setCategory] = useState('user'); + const { data } = useListWorkflowsQuery({ + page, + per_page: PER_PAGE, + }); + + if (!data) { + return null; + } + + return ( + + ); +}; + +export default memo(WorkflowLibraryWrapper); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/types.ts b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/types.ts new file mode 100644 index 0000000000..b5525908bf --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/WorkflowLibrary/types.ts @@ -0,0 +1 @@ +export type WorkflowCategory = 'user' | 'system'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/EmbedWorkflowCheckbox.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/EmbedWorkflowCheckbox.tsx deleted file mode 100644 index 989791e409..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/EmbedWorkflowCheckbox.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Checkbox, Flex, FormControl, FormLabel } from '@chakra-ui/react'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { useEmbedWorkflow } from 'features/nodes/hooks/useEmbedWorkflow'; -import { useWithWorkflow } from 'features/nodes/hooks/useWithWorkflow'; -import { nodeEmbedWorkflowChanged } from 'features/nodes/store/nodesSlice'; -import { ChangeEvent, memo, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; - -const EmbedWorkflowCheckbox = ({ nodeId }: { nodeId: string }) => { - const { t } = useTranslation(); - const dispatch = useAppDispatch(); - const withWorkflow = useWithWorkflow(nodeId); - const embedWorkflow = useEmbedWorkflow(nodeId); - const handleChange = useCallback( - (e: ChangeEvent) => { - dispatch( - nodeEmbedWorkflowChanged({ - nodeId, - embedWorkflow: e.target.checked, - }) - ); - }, - [dispatch, nodeId] - ); - - if (!withWorkflow) { - return null; - } - - return ( - - - {t('metadata.workflow')} - - - - ); -}; - -export default memo(EmbedWorkflowCheckbox); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx index ef3a85aa74..9a8e790876 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx @@ -1,9 +1,8 @@ import { Flex } from '@chakra-ui/react'; import { useHasImageOutput } from 'features/nodes/hooks/useHasImageOutput'; import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants'; -import { memo } from 'react'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -import EmbedWorkflowCheckbox from './EmbedWorkflowCheckbox'; +import { memo } from 'react'; import SaveToGalleryCheckbox from './SaveToGalleryCheckbox'; import UseCacheCheckbox from './UseCacheCheckbox'; @@ -28,7 +27,6 @@ const InvocationNodeFooter = ({ nodeId }: Props) => { }} > {isCacheEnabled && } - {hasImageOutput && } {hasImageOutput && } ); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopCenterPanel/TopCenterPanel.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopCenterPanel/TopCenterPanel.tsx index 4a10f097cf..bfe5de69aa 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopCenterPanel/TopCenterPanel.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopCenterPanel/TopCenterPanel.tsx @@ -3,6 +3,7 @@ import { memo } from 'react'; import LoadWorkflowButton from './LoadWorkflowButton'; import ResetWorkflowButton from './ResetWorkflowButton'; import DownloadWorkflowButton from './DownloadWorkflowButton'; +import WorkflowLibraryButton from 'features/nodes/components/flow/WorkflowLibrary/WorkflowLibraryButton'; const TopCenterPanel = () => { return ( @@ -18,6 +19,7 @@ const TopCenterPanel = () => { + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx index 6c9176d13a..7237b5abfb 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx @@ -11,17 +11,16 @@ import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; -import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate'; +import NotesTextarea from 'features/nodes/components/flow/nodes/Invocation/NotesTextarea'; +import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent'; import { - InvocationNodeData, + InvocationNode, InvocationTemplate, isInvocationNode, } from 'features/nodes/types/invocation'; +import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate'; import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { Node } from 'reactflow'; -import NotesTextarea from 'features/nodes/components/flow/nodes/Invocation/NotesTextarea'; -import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent'; import EditableNodeTitle from './details/EditableNodeTitle'; const selector = createSelector( @@ -62,7 +61,7 @@ const InspectorDetailsTab = () => { export default memo(InspectorDetailsTab); type ContentProps = { - node: Node; + node: InvocationNode; template: InvocationTemplate; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useEmbedWorkflow.ts b/invokeai/frontend/web/src/features/nodes/hooks/useEmbedWorkflow.ts deleted file mode 100644 index 97ca93ea0b..0000000000 --- a/invokeai/frontend/web/src/features/nodes/hooks/useEmbedWorkflow.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { stateSelector } from 'app/store/store'; -import { useAppSelector } from 'app/store/storeHooks'; -import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; -import { useMemo } from 'react'; -import { isInvocationNode } from 'features/nodes/types/invocation'; - -export const useEmbedWorkflow = (nodeId: string) => { - const selector = useMemo( - () => - createSelector( - stateSelector, - ({ nodes }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return false; - } - return node.data.embedWorkflow; - }, - defaultSelectorOptions - ), - [nodeId] - ); - - const embedWorkflow = useAppSelector(selector); - return embedWorkflow; -}; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useWithWorkflow.ts b/invokeai/frontend/web/src/features/nodes/hooks/useWithWorkflow.ts deleted file mode 100644 index 799fae4e3f..0000000000 --- a/invokeai/frontend/web/src/features/nodes/hooks/useWithWorkflow.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { stateSelector } from 'app/store/store'; -import { useAppSelector } from 'app/store/storeHooks'; -import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; -import { useMemo } from 'react'; -import { isInvocationNode } from 'features/nodes/types/invocation'; - -export const useWithWorkflow = (nodeId: string) => { - const selector = useMemo( - () => - createSelector( - stateSelector, - ({ nodes }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return false; - } - const nodeTemplate = nodes.nodeTemplates[node?.data.type ?? '']; - if (!nodeTemplate) { - return false; - } - return nodeTemplate.withWorkflow; - }, - defaultSelectorOptions - ), - [nodeId] - ); - - const withWorkflow = useAppSelector(selector); - return withWorkflow; -}; diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts index 064dc48195..045e07b153 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts @@ -344,20 +344,6 @@ const nodesSlice = createSlice({ } field.label = label; }, - nodeEmbedWorkflowChanged: ( - state, - action: PayloadAction<{ nodeId: string; embedWorkflow: boolean }> - ) => { - const { nodeId, embedWorkflow } = action.payload; - const nodeIndex = state.nodes.findIndex((n) => n.id === nodeId); - - const node = state.nodes?.[nodeIndex]; - - if (!isInvocationNode(node)) { - return; - } - node.data.embedWorkflow = embedWorkflow; - }, nodeUseCacheChanged: ( state, action: PayloadAction<{ nodeId: string; useCache: boolean }> @@ -984,7 +970,6 @@ export const { nodeAdded, nodeReplaced, nodeEditorReset, - nodeEmbedWorkflowChanged, nodeExclusivelySelected, nodeIsIntermediateChanged, nodeIsOpenChanged, diff --git a/invokeai/frontend/web/src/features/nodes/types/invocation.ts b/invokeai/frontend/web/src/features/nodes/types/invocation.ts index 3af64c7705..9e9fdeb955 100644 --- a/invokeai/frontend/web/src/features/nodes/types/invocation.ts +++ b/invokeai/frontend/web/src/features/nodes/types/invocation.ts @@ -18,7 +18,6 @@ export const zInvocationTemplate = z.object({ inputs: z.record(zFieldInputTemplate), outputs: z.record(zFieldOutputTemplate), outputType: z.string().min(1), - withWorkflow: z.boolean(), version: zSemVer, useCache: z.boolean(), nodePack: z.string().min(1).nullish(), @@ -33,7 +32,6 @@ export const zInvocationNodeData = z.object({ label: z.string(), isOpen: z.boolean(), notes: z.string(), - embedWorkflow: z.boolean(), isIntermediate: z.boolean(), useCache: z.boolean(), version: zSemVer, diff --git a/invokeai/frontend/web/src/features/nodes/types/workflow.ts b/invokeai/frontend/web/src/features/nodes/types/workflow.ts index 7af8a2dd72..05cf539291 100644 --- a/invokeai/frontend/web/src/features/nodes/types/workflow.ts +++ b/invokeai/frontend/web/src/features/nodes/types/workflow.ts @@ -73,6 +73,7 @@ export type WorkflowEdge = z.infer; // #region Workflow export const zWorkflowV2 = z.object({ + id: z.string().min(1).optional(), name: z.string(), author: z.string(), description: z.string(), diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts index 223786730c..c8abe72117 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts @@ -1,14 +1,13 @@ import { NodesState } from 'features/nodes/store/types'; +import { + FieldInputInstance, + isColorFieldInputInstance, +} from 'features/nodes/types/field'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { cloneDeep, omit, reduce } from 'lodash-es'; import { Graph } from 'services/api/types'; import { AnyInvocation } from 'services/events/types'; import { v4 as uuidv4 } from 'uuid'; -import { buildWorkflow } from 'features/nodes/util/workflow/buildWorkflow'; -import { - FieldInputInstance, - isColorFieldInputInstance, -} from 'features/nodes/types/field'; /** * We need to do special handling for some fields @@ -44,7 +43,7 @@ export const buildNodesGraph = (nodesState: NodesState): Graph => { const parsedNodes = filteredNodes.reduce>( (nodesAccumulator, node) => { const { id, data } = node; - const { type, inputs, isIntermediate, embedWorkflow } = data; + const { type, inputs, isIntermediate } = data; // Transform each node's inputs to simple key-value pairs const transformedInputs = reduce( @@ -69,11 +68,6 @@ export const buildNodesGraph = (nodesState: NodesState): Graph => { is_intermediate: isIntermediate, }; - if (embedWorkflow) { - // add the workflow to the node - Object.assign(graphNode, { workflow: buildWorkflow(nodesState) }); - } - // Add it to the nodes object Object.assign(nodesAccumulator, { [id]: graphNode, diff --git a/invokeai/frontend/web/src/features/nodes/util/node/buildInvocationNode.ts b/invokeai/frontend/web/src/features/nodes/util/node/buildInvocationNode.ts index 1da3b23a9a..7c2c845f53 100644 --- a/invokeai/frontend/web/src/features/nodes/util/node/buildInvocationNode.ts +++ b/invokeai/frontend/web/src/features/nodes/util/node/buildInvocationNode.ts @@ -67,7 +67,6 @@ export const buildInvocationNode = ( label: '', notes: '', isOpen: true, - embedWorkflow: false, isIntermediate: type === 'save_image' ? false : true, useCache: template.useCache, inputs, diff --git a/invokeai/frontend/web/src/features/nodes/util/node/nodeUpdate.ts b/invokeai/frontend/web/src/features/nodes/util/node/nodeUpdate.ts index d1913f6e05..a9de06635d 100644 --- a/invokeai/frontend/web/src/features/nodes/util/node/nodeUpdate.ts +++ b/invokeai/frontend/web/src/features/nodes/util/node/nodeUpdate.ts @@ -1,16 +1,15 @@ import { satisfies } from 'compare-versions'; import { NodeUpdateError } from 'features/nodes/types/error'; import { - InvocationNodeData, + InvocationNode, InvocationTemplate, } from 'features/nodes/types/invocation'; import { zParsedSemver } from 'features/nodes/types/semver'; -import { cloneDeep, defaultsDeep } from 'lodash-es'; -import { Node } from 'reactflow'; +import { cloneDeep, keys, defaultsDeep, pick } from 'lodash-es'; import { buildInvocationNode } from './buildInvocationNode'; export const getNeedsUpdate = ( - node: Node, + node: InvocationNode, template: InvocationTemplate ): boolean => { if (node.data.type !== template.type) { @@ -24,7 +23,7 @@ export const getNeedsUpdate = ( */ export const getMayUpdateNode = ( - node: Node, + node: InvocationNode, template: InvocationTemplate ): boolean => { const needsUpdate = getNeedsUpdate(node, template); @@ -45,9 +44,9 @@ export const getMayUpdateNode = ( */ export const updateNode = ( - node: Node, + node: InvocationNode, template: InvocationTemplate -): Node => { +): InvocationNode => { const mayUpdate = getMayUpdateNode(node, template); if (!mayUpdate || node.data.type !== template.type) { @@ -64,5 +63,8 @@ export const updateNode = ( clone.data.version = template.version; defaultsDeep(clone, defaults); // mutates! + // Remove any fields that are not in the template + clone.data.inputs = pick(clone.data.inputs, keys(defaults.data.inputs)); + clone.data.outputs = pick(clone.data.outputs, keys(defaults.data.outputs)); return clone; }; diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts b/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts index b425b11663..94dca71048 100644 --- a/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts +++ b/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts @@ -86,7 +86,6 @@ export const parseSchema = ( const description = schema.description ?? ''; const version = schema.version; const nodePack = schema.node_pack; - let withWorkflow = false; const inputs = reduce( schema.properties, @@ -114,12 +113,6 @@ export const parseSchema = ( try { const fieldType = parseFieldType(property); - if (fieldType.name === 'WorkflowField') { - // This supports workflows, set the flag and skip to next field - withWorkflow = true; - return inputsAccumulator; - } - if (isReservedFieldType(fieldType.name)) { // Skip processing this reserved field return inputsAccumulator; @@ -260,7 +253,6 @@ export const parseSchema = ( inputs, outputs, useCache, - withWorkflow, nodePack, }; diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts b/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts index e6fcee737e..61ddbca6dd 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts +++ b/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts @@ -1,18 +1,18 @@ import { $store } from 'app/store/nanostores/store'; import { RootState } from 'app/store/store'; -import { FieldType } from 'features/nodes/types/field'; -import { InvocationNodeData } from 'features/nodes/types/invocation'; -import { t } from 'i18next'; -import { forEach } from 'lodash-es'; -import { z } from 'zod'; import { WorkflowMigrationError, WorkflowVersionError, } from 'features/nodes/types/error'; +import { FieldType } from 'features/nodes/types/field'; +import { InvocationNodeData } from 'features/nodes/types/invocation'; import { zSemVer } from 'features/nodes/types/semver'; import { FIELD_TYPE_V1_TO_FIELD_TYPE_V2_MAPPING } from 'features/nodes/types/v1/fieldTypeMap'; import { WorkflowV1, zWorkflowV1 } from 'features/nodes/types/v1/workflowV1'; import { WorkflowV2, zWorkflowV2 } from 'features/nodes/types/workflow'; +import { t } from 'i18next'; +import { forEach } from 'lodash-es'; +import { z } from 'zod'; /** * Helper schema to extract the version from a workflow. @@ -25,6 +25,11 @@ const zWorkflowMetaVersion = z.object({ /** * Migrates a workflow from V1 to V2. + * + * Changes include: + * - Field types are now structured + * - Invocation node pack is now saved in the node data + * - Workflow schema version bumped to 2.0.0 */ const migrateV1toV2 = (workflowToMigrate: WorkflowV1): WorkflowV2 => { const invocationTemplates = ($store.get()?.getState() as RootState).nodes @@ -39,7 +44,6 @@ const migrateV1toV2 = (workflowToMigrate: WorkflowV1): WorkflowV2 => { t('nodes.unknownFieldType', { type: input.type }) ); } - // Cast as the V2 type (input.type as unknown as FieldType) = newFieldType; }); forEach(node.data.outputs, (output) => { @@ -50,19 +54,19 @@ const migrateV1toV2 = (workflowToMigrate: WorkflowV1): WorkflowV2 => { t('nodes.unknownFieldType', { type: output.type }) ); } - // Cast as the V2 type (output.type as unknown as FieldType) = newFieldType; }); - // Migrate nodePack + // Add node pack const invocationTemplate = invocationTemplates[node.data.type]; const nodePack = invocationTemplate ? invocationTemplate.nodePack : t('common.unknown'); - // Cast as the V2 type (node.data as unknown as InvocationNodeData).nodePack = nodePack; } }); - (workflowToMigrate.meta.version as WorkflowV2['meta']['version']) = '2.0.0'; + // Bump version + (workflowToMigrate as unknown as WorkflowV2).meta.version = '2.0.0'; + // Parsing strips out any extra properties not in the latest version return zWorkflowV2.parse(workflowToMigrate); }; @@ -73,7 +77,6 @@ export const parseAndMigrateWorkflow = (data: unknown): WorkflowV2 => { const workflowVersionResult = zWorkflowMetaVersion.safeParse(data); if (!workflowVersionResult.success) { - console.log(data); throw new WorkflowVersionError(t('nodes.unableToGetWorkflowVersion')); } diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.ts b/invokeai/frontend/web/src/services/api/endpoints/images.ts index 75b1bb771d..eb48f4a0ae 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/images.ts +++ b/invokeai/frontend/web/src/services/api/endpoints/images.ts @@ -9,7 +9,6 @@ import { } from 'features/gallery/store/types'; import { CoreMetadata, zCoreMetadata } from 'features/nodes/types/metadata'; import { keyBy } from 'lodash-es'; -import { ApiTagDescription, LIST_TAG, api } from '..'; import { components, paths } from 'services/api/schema'; import { DeleteBoardResult, @@ -26,6 +25,7 @@ import { imagesAdapter, imagesSelectors, } from 'services/api/util'; +import { ApiTagDescription, LIST_TAG, api } from '..'; import { boardsApi } from './boards'; export const imagesApi = api.injectEndpoints({ @@ -128,6 +128,16 @@ export const imagesApi = api.injectEndpoints({ }, keepUnusedDataFor: 86400, // 24 hours }), + getImageWorkflow: build.query< + paths['/api/v1/images/i/{image_name}/workflow']['get']['responses']['200']['content']['application/json'], + string + >({ + query: (image_name) => ({ url: `images/i/${image_name}/workflow` }), + providesTags: (result, error, image_name) => [ + { type: 'ImageWorkflow', id: image_name }, + ], + keepUnusedDataFor: 86400, // 24 hours + }), deleteImage: build.mutation({ query: ({ image_name }) => ({ url: `images/i/${image_name}`, @@ -1560,6 +1570,8 @@ export const { useLazyListImagesQuery, useGetImageDTOQuery, useGetImageMetadataQuery, + useGetImageWorkflowQuery, + useLazyGetImageWorkflowQuery, useDeleteImageMutation, useDeleteImagesMutation, useUploadImageMutation, diff --git a/invokeai/frontend/web/src/services/api/endpoints/workflows.ts b/invokeai/frontend/web/src/services/api/endpoints/workflows.ts index e056d63119..6cdb3cee07 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/workflows.ts +++ b/invokeai/frontend/web/src/services/api/endpoints/workflows.ts @@ -1,30 +1,70 @@ -import { logger } from 'app/logging/logger'; -import { WorkflowV2, zWorkflowV2 } from 'features/nodes/types/workflow'; -import { api } from '..'; +import { WorkflowV2 } from 'features/nodes/types/workflow'; import { paths } from 'services/api/schema'; +import { LIST_TAG, api } from '..'; export const workflowsApi = api.injectEndpoints({ endpoints: (build) => ({ - getWorkflow: build.query({ + getWorkflow: build.query< + paths['/api/v1/workflows/i/{workflow_id}']['get']['responses']['200']['content']['application/json'], + string + >({ query: (workflow_id) => `workflows/i/${workflow_id}`, providesTags: (result, error, workflow_id) => [ { type: 'Workflow', id: workflow_id }, ], - transformResponse: ( - response: paths['/api/v1/workflows/i/{workflow_id}']['get']['responses']['200']['content']['application/json'] - ) => { - if (response) { - const result = zWorkflowV2.safeParse(response); - if (result.success) { - return result.data; - } else { - logger('images').warn('Problem parsing workflow'); - } - } - return; - }, + }), + deleteWorkflow: build.mutation({ + query: (workflow_id) => ({ + url: `workflows/i/${workflow_id}`, + method: 'DELETE', + }), + invalidatesTags: [{ type: 'Workflow', id: LIST_TAG }], + }), + createWorkflow: build.mutation< + paths['/api/v1/workflows/']['post']['responses']['200']['content']['application/json'], + WorkflowV2 + >({ + query: (workflow) => ({ + url: 'workflows', + method: 'POST', + body: workflow, + }), + invalidatesTags: [{ type: 'Workflow', id: LIST_TAG }], + }), + updateWorkflow: build.mutation< + paths['/api/v1/workflows/i/{workflow_id}']['patch']['responses']['200']['content']['application/json'], + WorkflowV2 + >({ + query: (workflow) => ({ + url: `workflows/i/${workflow.id}`, + method: 'PATCH', + body: workflow, + }), + invalidatesTags: (response, error, arg) => [ + { type: 'Workflow', id: LIST_TAG }, + { type: 'Workflow', id: arg.id }, + ], + }), + listWorkflows: build.query< + paths['/api/v1/workflows/']['get']['responses']['200']['content']['application/json'], + NonNullable + >({ + query: (params) => ({ + url: 'workflows/', + params, + }), + providesTags: (result, error, params) => [ + { type: 'Workflow', id: LIST_TAG }, + { type: 'Workflow', id: params?.page }, + ], }), }), }); -export const { useGetWorkflowQuery } = workflowsApi; +export const { + useGetWorkflowQuery, + useCreateWorkflowMutation, + useDeleteWorkflowMutation, + useUpdateWorkflowMutation, + useListWorkflowsQuery, +} = workflowsApi; diff --git a/invokeai/frontend/web/src/services/api/index.ts b/invokeai/frontend/web/src/services/api/index.ts index f824afed1f..aaa97ef481 100644 --- a/invokeai/frontend/web/src/services/api/index.ts +++ b/invokeai/frontend/web/src/services/api/index.ts @@ -20,6 +20,7 @@ export const tagTypes = [ 'ImageNameList', 'ImageList', 'ImageMetadata', + 'ImageWorkflow', 'ImageMetadataFromFile', 'IntermediatesCount', 'SessionQueueItem', diff --git a/invokeai/frontend/web/src/services/api/schema.d.ts b/invokeai/frontend/web/src/services/api/schema.d.ts index b4f9db1370..67d0312b75 100644 --- a/invokeai/frontend/web/src/services/api/schema.d.ts +++ b/invokeai/frontend/web/src/services/api/schema.d.ts @@ -159,6 +159,10 @@ export type paths = { */ get: operations["get_image_metadata"]; }; + "/api/v1/images/i/{image_name}/workflow": { + /** Get Image Workflow */ + get: operations["get_image_workflow"]; + }; "/api/v1/images/i/{image_name}/full": { /** * Get Image Full @@ -415,6 +419,28 @@ export type paths = { * @description Gets a workflow */ get: operations["get_workflow"]; + /** + * Delete Workflow + * @description Deletes a workflow + */ + delete: operations["delete_workflow"]; + /** + * Update Workflow + * @description Updates a workflow + */ + patch: operations["update_workflow"]; + }; + "/api/v1/workflows/": { + /** + * List Workflows + * @description Deletes a workflow + */ + get: operations["list_workflows"]; + /** + * Create Workflow + * @description Creates a workflow + */ + post: operations["create_workflow"]; }; }; @@ -527,6 +553,8 @@ export type components = { data?: components["schemas"]["BatchDatum"][][] | null; /** @description The graph to initialize the session with */ graph: components["schemas"]["Graph"]; + /** @description The workflow to initialize the session with */ + workflow?: components["schemas"]["WorkflowWithoutID"] | null; /** * Runs * @description Int stating how many times to iterate through all possible batch indices @@ -600,8 +628,6 @@ export type components = { * @description Creates a blank image and forwards it to the pipeline */ BlankImageInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -795,6 +821,11 @@ export type components = { */ batch_ids: string[]; }; + /** Body_create_workflow */ + Body_create_workflow: { + /** @description The workflow to create */ + workflow: components["schemas"]["Workflow"]; + }; /** Body_delete_images_from_list */ Body_delete_images_from_list: { /** @@ -897,6 +928,11 @@ export type components = { */ image_names: string[]; }; + /** Body_update_workflow */ + Body_update_workflow: { + /** @description The updated workflow */ + workflow: components["schemas"]["Workflow"]; + }; /** Body_upload_image */ Body_upload_image: { /** @@ -1110,8 +1146,6 @@ export type components = { CV2InfillInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -1154,8 +1188,6 @@ export type components = { * @description Canny edge detection for ControlNet */ CannyImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -1360,8 +1392,6 @@ export type components = { ColorCorrectInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -1468,8 +1498,6 @@ export type components = { * @description Generates a color map from the provided image */ ColorMapImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -1671,8 +1699,6 @@ export type components = { * @description Applies content shuffle processing to image */ ContentShuffleImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -2261,8 +2287,6 @@ export type components = { * @description Simple inpaint using opencv. */ CvInpaintInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -2534,8 +2558,6 @@ export type components = { ESRGANInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -2620,6 +2642,13 @@ export type components = { */ priority: number; }; + /** ExposedField */ + ExposedField: { + /** Nodeid */ + nodeId: string; + /** Fieldname */ + fieldName: string; + }; /** * FaceIdentifier * @description Outputs an image with detected face IDs printed on each face. For use with other FaceTools. @@ -2627,8 +2656,6 @@ export type components = { FaceIdentifierInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -2674,8 +2701,6 @@ export type components = { FaceMaskInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -2771,8 +2796,6 @@ export type components = { FaceOffInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -3220,7 +3243,7 @@ export type components = { * @description The nodes in this graph */ nodes?: { - [key: string]: components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SDXLLoraLoaderInvocation"] | components["schemas"]["ONNXLatentsToImageInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["OnnxModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["LinearUIOutputInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ONNXPromptInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["ONNXTextToLatentsInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["RangeInvocation"]; + [key: string]: components["schemas"]["GraphInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["ONNXTextToLatentsInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ONNXPromptInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["OnnxModelLoaderInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ONNXLatentsToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["SDXLLoraLoaderInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LinearUIOutputInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageInvocation"]; }; /** * Edges @@ -3257,7 +3280,7 @@ export type components = { * @description The results of node executions */ results: { - [key: string]: components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["SDXLLoraLoaderOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["String2Output"] | components["schemas"]["VAEOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ONNXModelLoaderOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["SchedulerOutput"]; + [key: string]: components["schemas"]["StringOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["SDXLLoraLoaderOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["ONNXModelLoaderOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["String2Output"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["StringPosNegOutput"]; }; /** * Errors @@ -3331,8 +3354,6 @@ export type components = { * @description Applies HED edge detection to image */ HedImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -3589,8 +3610,6 @@ export type components = { ImageBlurInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -3649,8 +3668,6 @@ export type components = { ImageChannelInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -3691,8 +3708,6 @@ export type components = { ImageChannelMultiplyInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -3744,8 +3759,6 @@ export type components = { ImageChannelOffsetInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -3842,8 +3855,6 @@ export type components = { ImageConvertInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -3884,8 +3895,6 @@ export type components = { ImageCropInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -4005,16 +4014,16 @@ export type components = { * @description Whether this image is starred. */ starred: boolean; + /** + * Has Workflow + * @description Whether this image has a workflow. + */ + has_workflow: boolean; /** * Board Id * @description The id of the board the image belongs to, if one exists. */ board_id?: string | null; - /** - * Workflow Id - * @description The workflow that generated this image. - */ - workflow_id?: string | null; }; /** * ImageField @@ -4034,8 +4043,6 @@ export type components = { ImageHueAdjustmentInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -4075,8 +4082,6 @@ export type components = { ImageInverseLerpInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -4153,8 +4158,6 @@ export type components = { ImageLerpInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -4200,8 +4203,6 @@ export type components = { ImageMultiplyInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -4235,8 +4236,6 @@ export type components = { * @description Add blur to NSFW-flagged images */ ImageNSFWBlurInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -4296,8 +4295,6 @@ export type components = { ImagePasteInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -4381,8 +4378,6 @@ export type components = { * @description Resizes an image to specific dimensions */ ImageResizeInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -4435,8 +4430,6 @@ export type components = { * @description Scales an image by a factor */ ImageScaleInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -4549,8 +4542,6 @@ export type components = { * @description Add an invisible watermark to an image */ ImageWatermarkInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -4608,8 +4599,6 @@ export type components = { InfillColorInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -4653,8 +4642,6 @@ export type components = { InfillPatchMatchInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -4701,8 +4688,6 @@ export type components = { InfillTileInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -4979,6 +4964,7 @@ export type components = { */ type: "iterate_output"; }; + JsonValue: unknown; /** * LaMa Infill * @description Infills transparent areas of an image using the LaMa model @@ -4986,8 +4972,6 @@ export type components = { LaMaInfillInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -5141,8 +5125,6 @@ export type components = { * @description Generates an image from latents. */ LatentsToImageInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -5190,8 +5172,6 @@ export type components = { * @description Applies leres processing to image */ LeresImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -5257,8 +5237,6 @@ export type components = { LinearUIOutputInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -5292,8 +5270,6 @@ export type components = { * @description Applies line art anime processing to image */ LineartAnimeImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -5339,8 +5315,6 @@ export type components = { * @description Applies line art processing to image */ LineartImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -5752,8 +5726,6 @@ export type components = { MaskCombineInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -5789,8 +5761,6 @@ export type components = { MaskEdgeInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -5844,8 +5814,6 @@ export type components = { MaskFromAlphaInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -5883,8 +5851,6 @@ export type components = { * @description Applies mediapipe face processing to image */ MediapipeFaceProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -6118,8 +6084,6 @@ export type components = { * @description Applies Midas depth processing to image */ MidasDepthImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -6165,8 +6129,6 @@ export type components = { * @description Applies MLSD processing to image */ MlsdImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -6406,8 +6368,6 @@ export type components = { * @description Applies NormalBae processing to image */ NormalbaeImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -6453,8 +6413,6 @@ export type components = { * @description Generates an image from latents. */ ONNXLatentsToImageInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -6897,8 +6855,6 @@ export type components = { * @description Applies Openpose processing to image */ OpenposeImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -6945,13 +6901,39 @@ export type components = { */ type: "openpose_image_processor"; }; + /** PaginatedResults[WorkflowRecordDTO] */ + PaginatedResults_WorkflowRecordDTO_: { + /** + * Page + * @description Current Page + */ + page: number; + /** + * Pages + * @description Total number of pages + */ + pages: number; + /** + * Per Page + * @description Number of items per page + */ + per_page: number; + /** + * Total + * @description Total number of items in result + */ + total: number; + /** + * Items + * @description Items + */ + items: components["schemas"]["WorkflowRecordDTO"][]; + }; /** * PIDI Processor * @description Applies PIDI processing to image */ PidiImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -7773,8 +7755,6 @@ export type components = { SaveImageInvocation: { /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** * Id * @description The id of this instance of an invocation. Must be unique among all instances of invocations. @@ -7981,8 +7961,6 @@ export type components = { * @description Applies segment anything processing to image */ SegmentAnythingProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -8099,6 +8077,8 @@ export type components = { field_values?: components["schemas"]["NodeFieldValue"][] | null; /** @description The fully-populated session to be executed */ session: components["schemas"]["GraphExecutionState"]; + /** @description The workflow associated with this queue item */ + workflow?: components["schemas"]["WorkflowWithoutID"] | null; }; /** SessionQueueItemDTO */ SessionQueueItemDTO: { @@ -9158,8 +9138,6 @@ export type components = { * @description Tile resampler processor */ TileResamplerProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -9441,19 +9419,162 @@ export type components = { /** Error Type */ type: string; }; - /** - * WorkflowField - * @description Pydantic model for workflows with custom root of type dict[str, Any]. - * Workflows are stored without a strict schema. - */ - WorkflowField: Record; + /** Workflow */ + Workflow: { + /** + * Name + * @description The name of the workflow. + */ + name: string; + /** + * Author + * @description The author of the workflow. + */ + author: string; + /** + * Description + * @description The description of the workflow. + */ + description: string; + /** + * Version + * @description The version of the workflow. + */ + version: string; + /** + * Contact + * @description The contact of the workflow. + */ + contact: string; + /** + * Tags + * @description The tags of the workflow. + */ + tags: string; + /** + * Notes + * @description The notes of the workflow. + */ + notes: string; + /** + * Exposedfields + * @description The exposed fields of the workflow. + */ + exposedFields: components["schemas"]["ExposedField"][]; + /** @description The meta of the workflow. */ + meta: components["schemas"]["WorkflowMeta"]; + /** + * Nodes + * @description The nodes of the workflow. + */ + nodes: { + [key: string]: components["schemas"]["JsonValue"]; + }[]; + /** + * Edges + * @description The edges of the workflow. + */ + edges: { + [key: string]: components["schemas"]["JsonValue"]; + }[]; + /** + * Id + * @description The id of the workflow. + */ + id?: string; + }; + /** WorkflowMeta */ + WorkflowMeta: { + /** + * Version + * @description The version of the workflow schema. + */ + version: string; + }; + /** WorkflowRecordDTO */ + WorkflowRecordDTO: { + /** + * Workflow Id + * @description The id of the workflow. + */ + workflow_id: string; + /** @description The workflow. */ + workflow: components["schemas"]["Workflow"]; + /** + * Created At + * @description The created timestamp of the workflow. + */ + created_at: string; + /** + * Updated At + * @description The updated timestamp of the workflow. + */ + updated_at: string; + }; + /** WorkflowWithoutID */ + WorkflowWithoutID: { + /** + * Name + * @description The name of the workflow. + */ + name: string; + /** + * Author + * @description The author of the workflow. + */ + author: string; + /** + * Description + * @description The description of the workflow. + */ + description: string; + /** + * Version + * @description The version of the workflow. + */ + version: string; + /** + * Contact + * @description The contact of the workflow. + */ + contact: string; + /** + * Tags + * @description The tags of the workflow. + */ + tags: string; + /** + * Notes + * @description The notes of the workflow. + */ + notes: string; + /** + * Exposedfields + * @description The exposed fields of the workflow. + */ + exposedFields: components["schemas"]["ExposedField"][]; + /** @description The meta of the workflow. */ + meta: components["schemas"]["WorkflowMeta"]; + /** + * Nodes + * @description The nodes of the workflow. + */ + nodes: { + [key: string]: components["schemas"]["JsonValue"]; + }[]; + /** + * Edges + * @description The edges of the workflow. + */ + edges: { + [key: string]: components["schemas"]["JsonValue"]; + }[]; + }; /** * Zoe (Depth) Processor * @description Applies Zoe depth processing to image */ ZoeDepthImageProcessorInvocation: { - /** @description Optional workflow to be saved with the image */ - workflow?: components["schemas"]["WorkflowField"] | null; /** @description Optional metadata to be saved with the image */ metadata?: components["schemas"]["MetadataField"] | null; /** @@ -9692,41 +9813,11 @@ export type components = { */ UIType: "SDXLMainModelField" | "SDXLRefinerModelField" | "ONNXModelField" | "VAEModelField" | "LoRAModelField" | "ControlNetModelField" | "IPAdapterModelField" | "SchedulerField" | "AnyField" | "CollectionField" | "CollectionItemField" | "DEPRECATED_Boolean" | "DEPRECATED_Color" | "DEPRECATED_Conditioning" | "DEPRECATED_Control" | "DEPRECATED_Float" | "DEPRECATED_Image" | "DEPRECATED_Integer" | "DEPRECATED_Latents" | "DEPRECATED_String" | "DEPRECATED_BooleanCollection" | "DEPRECATED_ColorCollection" | "DEPRECATED_ConditioningCollection" | "DEPRECATED_ControlCollection" | "DEPRECATED_FloatCollection" | "DEPRECATED_ImageCollection" | "DEPRECATED_IntegerCollection" | "DEPRECATED_LatentsCollection" | "DEPRECATED_StringCollection" | "DEPRECATED_BooleanPolymorphic" | "DEPRECATED_ColorPolymorphic" | "DEPRECATED_ConditioningPolymorphic" | "DEPRECATED_ControlPolymorphic" | "DEPRECATED_FloatPolymorphic" | "DEPRECATED_ImagePolymorphic" | "DEPRECATED_IntegerPolymorphic" | "DEPRECATED_LatentsPolymorphic" | "DEPRECATED_StringPolymorphic" | "DEPRECATED_MainModel" | "DEPRECATED_UNet" | "DEPRECATED_Vae" | "DEPRECATED_CLIP" | "DEPRECATED_Collection" | "DEPRECATED_CollectionItem" | "DEPRECATED_Enum" | "DEPRECATED_WorkflowField" | "DEPRECATED_IsIntermediate" | "DEPRECATED_BoardField" | "DEPRECATED_MetadataItem" | "DEPRECATED_MetadataItemCollection" | "DEPRECATED_MetadataItemPolymorphic" | "DEPRECATED_MetadataDict"; /** - * StableDiffusionXLModelFormat + * StableDiffusion2ModelFormat * @description An enumeration. * @enum {string} */ - StableDiffusionXLModelFormat: "checkpoint" | "diffusers"; - /** - * StableDiffusion1ModelFormat - * @description An enumeration. - * @enum {string} - */ - StableDiffusion1ModelFormat: "checkpoint" | "diffusers"; - /** - * CLIPVisionModelFormat - * @description An enumeration. - * @enum {string} - */ - CLIPVisionModelFormat: "diffusers"; - /** - * T2IAdapterModelFormat - * @description An enumeration. - * @enum {string} - */ - T2IAdapterModelFormat: "diffusers"; - /** - * ControlNetModelFormat - * @description An enumeration. - * @enum {string} - */ - ControlNetModelFormat: "checkpoint" | "diffusers"; - /** - * IPAdapterModelFormat - * @description An enumeration. - * @enum {string} - */ - IPAdapterModelFormat: "invokeai"; + StableDiffusion2ModelFormat: "checkpoint" | "diffusers"; /** * StableDiffusionOnnxModelFormat * @description An enumeration. @@ -9734,11 +9825,41 @@ export type components = { */ StableDiffusionOnnxModelFormat: "olive" | "onnx"; /** - * StableDiffusion2ModelFormat + * StableDiffusionXLModelFormat * @description An enumeration. * @enum {string} */ - StableDiffusion2ModelFormat: "checkpoint" | "diffusers"; + StableDiffusionXLModelFormat: "checkpoint" | "diffusers"; + /** + * ControlNetModelFormat + * @description An enumeration. + * @enum {string} + */ + ControlNetModelFormat: "checkpoint" | "diffusers"; + /** + * T2IAdapterModelFormat + * @description An enumeration. + * @enum {string} + */ + T2IAdapterModelFormat: "diffusers"; + /** + * CLIPVisionModelFormat + * @description An enumeration. + * @enum {string} + */ + CLIPVisionModelFormat: "diffusers"; + /** + * StableDiffusion1ModelFormat + * @description An enumeration. + * @enum {string} + */ + StableDiffusion1ModelFormat: "checkpoint" | "diffusers"; + /** + * IPAdapterModelFormat + * @description An enumeration. + * @enum {string} + */ + IPAdapterModelFormat: "invokeai"; }; responses: never; parameters: never; @@ -10476,6 +10597,29 @@ export type operations = { }; }; }; + /** Get Image Workflow */ + get_image_workflow: { + parameters: { + path: { + /** @description The name of image whose workflow to get */ + image_name: string; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": components["schemas"]["WorkflowWithoutID"] | null; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; /** * Get Image Full * @description Gets a full-resolution image file @@ -11457,7 +11601,111 @@ export type operations = { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["WorkflowField"]; + "application/json": components["schemas"]["WorkflowRecordDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + /** + * Delete Workflow + * @description Deletes a workflow + */ + delete_workflow: { + parameters: { + path: { + /** @description The workflow to delete */ + workflow_id: string; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + /** + * Update Workflow + * @description Updates a workflow + */ + update_workflow: { + requestBody: { + content: { + "application/json": components["schemas"]["Body_update_workflow"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": components["schemas"]["Workflow"]; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + /** + * List Workflows + * @description Deletes a workflow + */ + list_workflows: { + parameters: { + query?: { + /** @description The page to get */ + page?: number; + /** @description The number of workflows per page */ + per_page?: number; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": components["schemas"]["PaginatedResults_WorkflowRecordDTO_"]; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + /** + * Create Workflow + * @description Creates a workflow + */ + create_workflow: { + requestBody: { + content: { + "application/json": components["schemas"]["Body_create_workflow"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": components["schemas"]["WorkflowRecordDTO"]; }; }; /** @description Validation Error */