From 6390af229d8d025a2769a857fb80a30ff3e262ef Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 26 Jun 2023 16:20:57 +1000 Subject: [PATCH] feat(ui): add dynamic prompts to t2i tab - add param accordion for dynamic prompts - update graphs --- invokeai/frontend/web/src/app/store/store.ts | 4 + .../frontend/web/src/app/types/invokeai.ts | 8 + .../web/src/common/components/IAISwitch.tsx | 10 +- .../ParamDynamicPromptsCollapse.tsx | 45 ++++++ .../ParamDynamicPromptsCombinatorial.tsx | 36 +++++ .../ParamDynamicPromptsMaxPrompts.tsx | 53 ++++++ .../dynamicPrompts/store/selectors.ts | 1 + .../features/dynamicPrompts/store/slice.ts | 50 ++++++ .../nodes/util/addControlNetToLinearGraph.ts | 20 +-- .../graphBuilders/addDynamicPromptsToGraph.ts | 153 ++++++++++++++++++ .../buildCanvasImageToImageGraph.ts | 79 ++------- .../graphBuilders/buildCanvasInpaintGraph.ts | 14 +- .../buildCanvasTextToImageGraph.ts | 69 ++------ .../buildLinearImageToImageGraph.ts | 88 ++-------- .../buildLinearTextToImageGraph.ts | 92 ++--------- .../nodes/util/graphBuilders/constants.ts | 3 +- .../util/nodeBuilders/buildCompelNode.ts | 26 --- .../nodeBuilders/buildImageToImageNode.ts | 107 ------------ .../util/nodeBuilders/buildInpaintNode.ts | 48 ------ .../util/nodeBuilders/buildIterateNode.ts | 13 -- .../nodes/util/nodeBuilders/buildRangeNode.ts | 26 --- .../util/nodeBuilders/buildTextToImageNode.ts | 45 ------ .../Parameters/Core/ParamIterations.tsx | 41 ++--- .../src/features/system/store/configSlice.ts | 8 + .../ImageToImageTabParameters.tsx | 2 + .../TextToImage/TextToImageTabParameters.tsx | 2 + .../UnifiedCanvas/UnifiedCanvasParameters.tsx | 2 + .../web/src/services/api/endpoints/images.ts | 1 + .../frontend/web/src/services/api/types.d.ts | 9 ++ 29 files changed, 479 insertions(+), 576 deletions(-) create mode 100644 invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCollapse.tsx create mode 100644 invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCombinatorial.tsx create mode 100644 invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx create mode 100644 invokeai/frontend/web/src/features/dynamicPrompts/store/selectors.ts create mode 100644 invokeai/frontend/web/src/features/dynamicPrompts/store/slice.ts create mode 100644 invokeai/frontend/web/src/features/nodes/util/graphBuilders/addDynamicPromptsToGraph.ts delete mode 100644 invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildCompelNode.ts delete mode 100644 invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildImageToImageNode.ts delete mode 100644 invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildInpaintNode.ts delete mode 100644 invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildIterateNode.ts delete mode 100644 invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildRangeNode.ts delete mode 100644 invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildTextToImageNode.ts diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index 8202c4fa76..e92a422d68 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -22,6 +22,7 @@ import boardsReducer from 'features/gallery/store/boardSlice'; import configReducer from 'features/system/store/configSlice'; import hotkeysReducer from 'features/ui/store/hotkeysSlice'; import uiReducer from 'features/ui/store/uiSlice'; +import dynamicPromptsReducer from 'features/dynamicPrompts/store/slice'; import { listenerMiddleware } from './middleware/listenerMiddleware'; @@ -48,6 +49,7 @@ const allReducers = { controlNet: controlNetReducer, boards: boardsReducer, // session: sessionReducer, + dynamicPrompts: dynamicPromptsReducer, [api.reducerPath]: api.reducer, }; @@ -65,6 +67,7 @@ const rememberedKeys: (keyof typeof allReducers)[] = [ 'system', 'ui', 'controlNet', + 'dynamicPrompts', // 'boards', // 'hotkeys', // 'config', @@ -100,3 +103,4 @@ export type AppGetState = typeof store.getState; export type RootState = ReturnType; export type AppThunkDispatch = ThunkDispatch; export type AppDispatch = typeof store.dispatch; +export const stateSelector = (state: RootState) => state; diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index 4532783ae6..a89ba01130 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -171,6 +171,14 @@ export type AppConfig = { fineStep: number; coarseStep: number; }; + dynamicPrompts: { + maxPrompts: { + initial: number; + min: number; + sliderMax: number; + inputMax: number; + }; + }; }; }; diff --git a/invokeai/frontend/web/src/common/components/IAISwitch.tsx b/invokeai/frontend/web/src/common/components/IAISwitch.tsx index 33c46c4aeb..54a3b30a4f 100644 --- a/invokeai/frontend/web/src/common/components/IAISwitch.tsx +++ b/invokeai/frontend/web/src/common/components/IAISwitch.tsx @@ -41,7 +41,15 @@ const IAISwitch = (props: Props) => { {...formControlProps} > {label && ( - + {label} )} diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCollapse.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCollapse.tsx new file mode 100644 index 0000000000..eeaf1b81ec --- /dev/null +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCollapse.tsx @@ -0,0 +1,45 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { stateSelector } from 'app/store/store'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; +import IAICollapse from 'common/components/IAICollapse'; +import { useCallback } from 'react'; +import { isEnabledToggled } from '../store/slice'; +import ParamDynamicPromptsMaxPrompts from './ParamDynamicPromptsMaxPrompts'; +import ParamDynamicPromptsCombinatorial from './ParamDynamicPromptsCombinatorial'; +import { Flex } from '@chakra-ui/react'; + +const selector = createSelector( + stateSelector, + (state) => { + const { isEnabled } = state.dynamicPrompts; + + return { isEnabled }; + }, + defaultSelectorOptions +); + +const ParamDynamicPromptsCollapse = () => { + const dispatch = useAppDispatch(); + const { isEnabled } = useAppSelector(selector); + + const handleToggleIsEnabled = useCallback(() => { + dispatch(isEnabledToggled()); + }, [dispatch]); + + return ( + + + + + + + ); +}; + +export default ParamDynamicPromptsCollapse; diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCombinatorial.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCombinatorial.tsx new file mode 100644 index 0000000000..30c2240c37 --- /dev/null +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCombinatorial.tsx @@ -0,0 +1,36 @@ +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { combinatorialToggled } from '../store/slice'; +import { createSelector } from '@reduxjs/toolkit'; +import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; +import { useCallback } from 'react'; +import { stateSelector } from 'app/store/store'; +import IAISwitch from 'common/components/IAISwitch'; + +const selector = createSelector( + stateSelector, + (state) => { + const { combinatorial } = state.dynamicPrompts; + + return { combinatorial }; + }, + defaultSelectorOptions +); + +const ParamDynamicPromptsCombinatorial = () => { + const { combinatorial } = useAppSelector(selector); + const dispatch = useAppDispatch(); + + const handleChange = useCallback(() => { + dispatch(combinatorialToggled()); + }, [dispatch]); + + return ( + + ); +}; + +export default ParamDynamicPromptsCombinatorial; diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx new file mode 100644 index 0000000000..ab56abaa35 --- /dev/null +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx @@ -0,0 +1,53 @@ +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import IAISlider from 'common/components/IAISlider'; +import { maxPromptsChanged, maxPromptsReset } from '../store/slice'; +import { createSelector } from '@reduxjs/toolkit'; +import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; +import { useCallback } from 'react'; +import { stateSelector } from 'app/store/store'; + +const selector = createSelector( + stateSelector, + (state) => { + const { maxPrompts } = state.dynamicPrompts; + const { min, sliderMax, inputMax } = + state.config.sd.dynamicPrompts.maxPrompts; + + return { maxPrompts, min, sliderMax, inputMax }; + }, + defaultSelectorOptions +); + +const ParamDynamicPromptsMaxPrompts = () => { + const { maxPrompts, min, sliderMax, inputMax } = useAppSelector(selector); + const dispatch = useAppDispatch(); + + const handleChange = useCallback( + (v: number) => { + dispatch(maxPromptsChanged(v)); + }, + [dispatch] + ); + + const handleReset = useCallback(() => { + dispatch(maxPromptsReset()); + }, [dispatch]); + + return ( + + ); +}; + +export default ParamDynamicPromptsMaxPrompts; diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/store/selectors.ts b/invokeai/frontend/web/src/features/dynamicPrompts/store/selectors.ts new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/invokeai/frontend/web/src/features/dynamicPrompts/store/selectors.ts @@ -0,0 +1 @@ +// diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/store/slice.ts b/invokeai/frontend/web/src/features/dynamicPrompts/store/slice.ts new file mode 100644 index 0000000000..8c33feb20c --- /dev/null +++ b/invokeai/frontend/web/src/features/dynamicPrompts/store/slice.ts @@ -0,0 +1,50 @@ +import { PayloadAction, createSlice } from '@reduxjs/toolkit'; +import { RootState } from 'app/store/store'; + +export interface DynamicPromptsState { + isEnabled: boolean; + maxPrompts: number; + combinatorial: boolean; +} + +export const initialDynamicPromptsState: DynamicPromptsState = { + isEnabled: false, + maxPrompts: 100, + combinatorial: true, +}; + +const initialState: DynamicPromptsState = initialDynamicPromptsState; + +export const dynamicPromptsSlice = createSlice({ + name: 'dynamicPrompts', + initialState, + reducers: { + maxPromptsChanged: (state, action: PayloadAction) => { + state.maxPrompts = action.payload; + }, + maxPromptsReset: (state) => { + state.maxPrompts = initialDynamicPromptsState.maxPrompts; + }, + combinatorialToggled: (state) => { + state.combinatorial = !state.combinatorial; + }, + isEnabledToggled: (state) => { + state.isEnabled = !state.isEnabled; + }, + }, + extraReducers: (builder) => { + // + }, +}); + +export const { + isEnabledToggled, + maxPromptsChanged, + maxPromptsReset, + combinatorialToggled, +} = dynamicPromptsSlice.actions; + +export default dynamicPromptsSlice.reducer; + +export const dynamicPromptsSelector = (state: RootState) => + state.dynamicPrompts; diff --git a/invokeai/frontend/web/src/features/nodes/util/addControlNetToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/addControlNetToLinearGraph.ts index 13dd1f6ffc..11ceb23763 100644 --- a/invokeai/frontend/web/src/features/nodes/util/addControlNetToLinearGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/addControlNetToLinearGraph.ts @@ -1,5 +1,5 @@ import { RootState } from 'app/store/store'; -import { filter, forEach, size } from 'lodash-es'; +import { filter } from 'lodash-es'; import { CollectInvocation, ControlNetInvocation } from 'services/api/types'; import { NonNullableGraph } from '../types/types'; import { CONTROL_NET_COLLECT } from './graphBuilders/constants'; @@ -19,9 +19,9 @@ export const addControlNetToLinearGraph = ( (c.processorType === 'none' && Boolean(c.controlImage))) ); - // Add ControlNet - if (isControlNetEnabled && validControlNets.length > 0) { - if (size(controlNets) > 1) { + if (isControlNetEnabled && Boolean(validControlNets.length)) { + if (validControlNets.length > 1) { + // We have multiple controlnets, add ControlNet collector const controlNetIterateNode: CollectInvocation = { id: CONTROL_NET_COLLECT, type: 'collect', @@ -36,10 +36,9 @@ export const addControlNetToLinearGraph = ( }); } - forEach(controlNets, (controlNet) => { + validControlNets.forEach((controlNet) => { const { controlNetId, - isEnabled, controlImage, processedControlImage, beginStepPct, @@ -50,11 +49,6 @@ export const addControlNetToLinearGraph = ( weight, } = controlNet; - if (!isEnabled) { - // Skip disabled ControlNets - return; - } - const controlNetNode: ControlNetInvocation = { id: `control_net_${controlNetId}`, type: 'controlnet', @@ -82,7 +76,8 @@ export const addControlNetToLinearGraph = ( graph.nodes[controlNetNode.id] = controlNetNode; - if (size(controlNets) > 1) { + if (validControlNets.length > 1) { + // if we have multiple controlnets, link to the collector graph.edges.push({ source: { node_id: controlNetNode.id, field: 'control' }, destination: { @@ -91,6 +86,7 @@ export const addControlNetToLinearGraph = ( }, }); } else { + // otherwise, link directly to the base node graph.edges.push({ source: { node_id: controlNetNode.id, field: 'control' }, destination: { diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addDynamicPromptsToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addDynamicPromptsToGraph.ts new file mode 100644 index 0000000000..23abb815a9 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addDynamicPromptsToGraph.ts @@ -0,0 +1,153 @@ +import { RootState } from 'app/store/store'; +import { NonNullableGraph } from 'features/nodes/types/types'; +import { + DynamicPromptInvocation, + IterateInvocation, + NoiseInvocation, + RandomIntInvocation, + RangeOfSizeInvocation, +} from 'services/api/types'; +import { + DYNAMIC_PROMPT, + ITERATE, + NOISE, + POSITIVE_CONDITIONING, + RANDOM_INT, + RANGE_OF_SIZE, +} from './constants'; +import { unset } from 'lodash-es'; + +export const addDynamicPromptsToGraph = ( + graph: NonNullableGraph, + state: RootState +): void => { + const { positivePrompt, iterations, seed, shouldRandomizeSeed } = + state.generation; + + const { + combinatorial, + isEnabled: isDynamicPromptsEnabled, + maxPrompts, + } = state.dynamicPrompts; + + if (isDynamicPromptsEnabled) { + // iteration is handled via dynamic prompts + unset(graph.nodes[POSITIVE_CONDITIONING], 'prompt'); + + const dynamicPromptNode: DynamicPromptInvocation = { + id: DYNAMIC_PROMPT, + type: 'dynamic_prompt', + max_prompts: maxPrompts, + combinatorial, + prompt: positivePrompt, + }; + + const iterateNode: IterateInvocation = { + id: ITERATE, + type: 'iterate', + }; + + graph.nodes[DYNAMIC_PROMPT] = dynamicPromptNode; + graph.nodes[ITERATE] = iterateNode; + + // connect dynamic prompts to compel nodes + graph.edges.push( + { + source: { + node_id: DYNAMIC_PROMPT, + field: 'prompt_collection', + }, + destination: { + node_id: ITERATE, + field: 'collection', + }, + }, + { + source: { + node_id: ITERATE, + field: 'item', + }, + destination: { + node_id: POSITIVE_CONDITIONING, + field: 'prompt', + }, + } + ); + + if (shouldRandomizeSeed) { + // Random int node to generate the starting seed + const randomIntNode: RandomIntInvocation = { + id: RANDOM_INT, + type: 'rand_int', + }; + + graph.nodes[RANDOM_INT] = randomIntNode; + + // Connect random int to the start of the range of size so the range starts on the random first seed + graph.edges.push({ + source: { node_id: RANDOM_INT, field: 'a' }, + destination: { node_id: NOISE, field: 'seed' }, + }); + } else { + // User specified seed, so set the start of the range of size to the seed + (graph.nodes[NOISE] as NoiseInvocation).seed = seed; + } + } else { + const rangeOfSizeNode: RangeOfSizeInvocation = { + id: RANGE_OF_SIZE, + type: 'range_of_size', + size: iterations, + step: 1, + }; + + const iterateNode: IterateInvocation = { + id: ITERATE, + type: 'iterate', + }; + + graph.nodes[ITERATE] = iterateNode; + graph.nodes[RANGE_OF_SIZE] = rangeOfSizeNode; + + graph.edges.push({ + source: { + node_id: RANGE_OF_SIZE, + field: 'collection', + }, + destination: { + node_id: ITERATE, + field: 'collection', + }, + }); + + graph.edges.push({ + source: { + node_id: ITERATE, + field: 'item', + }, + destination: { + node_id: NOISE, + field: 'seed', + }, + }); + + // handle seed + if (shouldRandomizeSeed) { + // Random int node to generate the starting seed + const randomIntNode: RandomIntInvocation = { + id: RANDOM_INT, + type: 'rand_int', + }; + + graph.nodes[RANDOM_INT] = randomIntNode; + + // Connect random int to the start of the range of size so the range starts on the random first seed + graph.edges.push({ + source: { node_id: RANDOM_INT, field: 'a' }, + destination: { node_id: RANGE_OF_SIZE, field: 'start' }, + }); + } else { + // User specified seed, so set the start of the range of size to the seed + rangeOfSizeNode.start = seed; + } + } +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts index cf46b1226d..49bab291f7 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts @@ -2,6 +2,7 @@ import { RootState } from 'app/store/store'; import { ImageDTO, ImageResizeInvocation, + ImageToLatentsInvocation, RandomIntInvocation, RangeOfSizeInvocation, } from 'services/api/types'; @@ -10,7 +11,7 @@ import { log } from 'app/logging/useLogger'; import { ITERATE, LATENTS_TO_IMAGE, - MODEL_LOADER, + PIPELINE_MODEL_LOADER, NEGATIVE_CONDITIONING, NOISE, POSITIVE_CONDITIONING, @@ -24,6 +25,7 @@ import { import { set } from 'lodash-es'; import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph'; import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField'; +import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; const moduleLog = log.child({ namespace: 'nodes' }); @@ -75,31 +77,19 @@ export const buildCanvasImageToImageGraph = ( id: NEGATIVE_CONDITIONING, prompt: negativePrompt, }, - [RANGE_OF_SIZE]: { - type: 'range_of_size', - id: RANGE_OF_SIZE, - // seed - must be connected manually - // start: 0, - size: iterations, - step: 1, - }, [NOISE]: { type: 'noise', id: NOISE, }, - [MODEL_LOADER]: { + [PIPELINE_MODEL_LOADER]: { type: 'pipeline_model_loader', - id: MODEL_LOADER, + id: PIPELINE_MODEL_LOADER, model, }, [LATENTS_TO_IMAGE]: { type: 'l2i', id: LATENTS_TO_IMAGE, }, - [ITERATE]: { - type: 'iterate', - id: ITERATE, - }, [LATENTS_TO_LATENTS]: { type: 'l2l', id: LATENTS_TO_LATENTS, @@ -120,7 +110,7 @@ export const buildCanvasImageToImageGraph = ( edges: [ { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'clip', }, destination: { @@ -130,7 +120,7 @@ export const buildCanvasImageToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'clip', }, destination: { @@ -140,7 +130,7 @@ export const buildCanvasImageToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'vae', }, destination: { @@ -148,26 +138,6 @@ export const buildCanvasImageToImageGraph = ( field: 'vae', }, }, - { - source: { - node_id: RANGE_OF_SIZE, - field: 'collection', - }, - destination: { - node_id: ITERATE, - field: 'collection', - }, - }, - { - source: { - node_id: ITERATE, - field: 'item', - }, - destination: { - node_id: NOISE, - field: 'seed', - }, - }, { source: { node_id: LATENTS_TO_LATENTS, @@ -200,7 +170,7 @@ export const buildCanvasImageToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'vae', }, destination: { @@ -210,7 +180,7 @@ export const buildCanvasImageToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'unet', }, destination: { @@ -241,26 +211,6 @@ export const buildCanvasImageToImageGraph = ( ], }; - // handle seed - if (shouldRandomizeSeed) { - // Random int node to generate the starting seed - const randomIntNode: RandomIntInvocation = { - id: RANDOM_INT, - type: 'rand_int', - }; - - graph.nodes[RANDOM_INT] = randomIntNode; - - // Connect random int to the start of the range of size so the range starts on the random first seed - graph.edges.push({ - source: { node_id: RANDOM_INT, field: 'a' }, - destination: { node_id: RANGE_OF_SIZE, field: 'start' }, - }); - } else { - // User specified seed, so set the start of the range of size to the seed - (graph.nodes[RANGE_OF_SIZE] as RangeOfSizeInvocation).start = seed; - } - // handle `fit` if (initialImage.width !== width || initialImage.height !== height) { // The init image needs to be resized to the specified width and height before being passed to `IMAGE_TO_LATENTS` @@ -306,9 +256,9 @@ export const buildCanvasImageToImageGraph = ( }); } else { // We are not resizing, so we need to set the image on the `IMAGE_TO_LATENTS` node explicitly - set(graph.nodes[IMAGE_TO_LATENTS], 'image', { + (graph.nodes[IMAGE_TO_LATENTS] as ImageToLatentsInvocation).image = { image_name: initialImage.image_name, - }); + }; // Pass the image's dimensions to the `NOISE` node graph.edges.push({ @@ -327,7 +277,10 @@ export const buildCanvasImageToImageGraph = ( }); } - // add controlnet + // add dynamic prompts, mutating `graph` + addDynamicPromptsToGraph(graph, state); + + // add controlnet, mutating `graph` addControlNetToLinearGraph(graph, LATENTS_TO_LATENTS, state); return graph; diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts index eb2399771a..74bd12a742 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts @@ -9,7 +9,7 @@ import { NonNullableGraph } from 'features/nodes/types/types'; import { log } from 'app/logging/useLogger'; import { ITERATE, - MODEL_LOADER, + PIPELINE_MODEL_LOADER, NEGATIVE_CONDITIONING, POSITIVE_CONDITIONING, RANDOM_INT, @@ -101,9 +101,9 @@ export const buildCanvasInpaintGraph = ( id: NEGATIVE_CONDITIONING, prompt: negativePrompt, }, - [MODEL_LOADER]: { + [PIPELINE_MODEL_LOADER]: { type: 'pipeline_model_loader', - id: MODEL_LOADER, + id: PIPELINE_MODEL_LOADER, model, }, [RANGE_OF_SIZE]: { @@ -142,7 +142,7 @@ export const buildCanvasInpaintGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'clip', }, destination: { @@ -152,7 +152,7 @@ export const buildCanvasInpaintGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'clip', }, destination: { @@ -162,7 +162,7 @@ export const buildCanvasInpaintGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'unet', }, destination: { @@ -172,7 +172,7 @@ export const buildCanvasInpaintGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'vae', }, destination: { diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts index 251ad94165..b15b2cd192 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts @@ -4,7 +4,7 @@ import { RandomIntInvocation, RangeOfSizeInvocation } from 'services/api/types'; import { ITERATE, LATENTS_TO_IMAGE, - MODEL_LOADER, + PIPELINE_MODEL_LOADER, NEGATIVE_CONDITIONING, NOISE, POSITIVE_CONDITIONING, @@ -15,6 +15,7 @@ import { } from './constants'; import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph'; import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField'; +import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; /** * Builds the Canvas tab's Text to Image graph. @@ -62,13 +63,6 @@ export const buildCanvasTextToImageGraph = ( id: NEGATIVE_CONDITIONING, prompt: negativePrompt, }, - [RANGE_OF_SIZE]: { - type: 'range_of_size', - id: RANGE_OF_SIZE, - // start: 0, // seed - must be connected manually - size: iterations, - step: 1, - }, [NOISE]: { type: 'noise', id: NOISE, @@ -82,19 +76,15 @@ export const buildCanvasTextToImageGraph = ( scheduler, steps, }, - [MODEL_LOADER]: { + [PIPELINE_MODEL_LOADER]: { type: 'pipeline_model_loader', - id: MODEL_LOADER, + id: PIPELINE_MODEL_LOADER, model, }, [LATENTS_TO_IMAGE]: { type: 'l2i', id: LATENTS_TO_IMAGE, }, - [ITERATE]: { - type: 'iterate', - id: ITERATE, - }, }, edges: [ { @@ -119,7 +109,7 @@ export const buildCanvasTextToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'clip', }, destination: { @@ -129,7 +119,7 @@ export const buildCanvasTextToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'clip', }, destination: { @@ -139,7 +129,7 @@ export const buildCanvasTextToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'unet', }, destination: { @@ -159,7 +149,7 @@ export const buildCanvasTextToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'vae', }, destination: { @@ -167,26 +157,6 @@ export const buildCanvasTextToImageGraph = ( field: 'vae', }, }, - { - source: { - node_id: RANGE_OF_SIZE, - field: 'collection', - }, - destination: { - node_id: ITERATE, - field: 'collection', - }, - }, - { - source: { - node_id: ITERATE, - field: 'item', - }, - destination: { - node_id: NOISE, - field: 'seed', - }, - }, { source: { node_id: NOISE, @@ -200,27 +170,10 @@ export const buildCanvasTextToImageGraph = ( ], }; - // handle seed - if (shouldRandomizeSeed) { - // Random int node to generate the starting seed - const randomIntNode: RandomIntInvocation = { - id: RANDOM_INT, - type: 'rand_int', - }; + // add dynamic prompts, mutating `graph` + addDynamicPromptsToGraph(graph, state); - graph.nodes[RANDOM_INT] = randomIntNode; - - // Connect random int to the start of the range of size so the range starts on the random first seed - graph.edges.push({ - source: { node_id: RANDOM_INT, field: 'a' }, - destination: { node_id: RANGE_OF_SIZE, field: 'start' }, - }); - } else { - // User specified seed, so set the start of the range of size to the seed - (graph.nodes[RANGE_OF_SIZE] as RangeOfSizeInvocation).start = seed; - } - - // add controlnet + // add controlnet, mutating `graph` addControlNetToLinearGraph(graph, TEXT_TO_LATENTS, state); return graph; diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts index d488accd0a..15d5a431a2 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts @@ -1,28 +1,24 @@ import { RootState } from 'app/store/store'; import { ImageResizeInvocation, - RandomIntInvocation, - RangeOfSizeInvocation, + ImageToLatentsInvocation, } from 'services/api/types'; import { NonNullableGraph } from 'features/nodes/types/types'; import { log } from 'app/logging/useLogger'; import { - ITERATE, LATENTS_TO_IMAGE, - MODEL_LOADER, + PIPELINE_MODEL_LOADER, NEGATIVE_CONDITIONING, NOISE, POSITIVE_CONDITIONING, - RANDOM_INT, - RANGE_OF_SIZE, IMAGE_TO_IMAGE_GRAPH, IMAGE_TO_LATENTS, LATENTS_TO_LATENTS, RESIZE, } from './constants'; -import { set } from 'lodash-es'; import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph'; import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField'; +import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; const moduleLog = log.child({ namespace: 'nodes' }); @@ -44,9 +40,6 @@ export const buildLinearImageToImageGraph = ( shouldFitToWidthHeight, width, height, - iterations, - seed, - shouldRandomizeSeed, } = state.generation; /** @@ -79,31 +72,19 @@ export const buildLinearImageToImageGraph = ( id: NEGATIVE_CONDITIONING, prompt: negativePrompt, }, - [RANGE_OF_SIZE]: { - type: 'range_of_size', - id: RANGE_OF_SIZE, - // seed - must be connected manually - // start: 0, - size: iterations, - step: 1, - }, [NOISE]: { type: 'noise', id: NOISE, }, - [MODEL_LOADER]: { + [PIPELINE_MODEL_LOADER]: { type: 'pipeline_model_loader', - id: MODEL_LOADER, + id: PIPELINE_MODEL_LOADER, model, }, [LATENTS_TO_IMAGE]: { type: 'l2i', id: LATENTS_TO_IMAGE, }, - [ITERATE]: { - type: 'iterate', - id: ITERATE, - }, [LATENTS_TO_LATENTS]: { type: 'l2l', id: LATENTS_TO_LATENTS, @@ -124,7 +105,7 @@ export const buildLinearImageToImageGraph = ( edges: [ { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'clip', }, destination: { @@ -134,7 +115,7 @@ export const buildLinearImageToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'clip', }, destination: { @@ -144,7 +125,7 @@ export const buildLinearImageToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'vae', }, destination: { @@ -152,26 +133,6 @@ export const buildLinearImageToImageGraph = ( field: 'vae', }, }, - { - source: { - node_id: RANGE_OF_SIZE, - field: 'collection', - }, - destination: { - node_id: ITERATE, - field: 'collection', - }, - }, - { - source: { - node_id: ITERATE, - field: 'item', - }, - destination: { - node_id: NOISE, - field: 'seed', - }, - }, { source: { node_id: LATENTS_TO_LATENTS, @@ -204,7 +165,7 @@ export const buildLinearImageToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'vae', }, destination: { @@ -214,7 +175,7 @@ export const buildLinearImageToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'unet', }, destination: { @@ -245,26 +206,6 @@ export const buildLinearImageToImageGraph = ( ], }; - // handle seed - if (shouldRandomizeSeed) { - // Random int node to generate the starting seed - const randomIntNode: RandomIntInvocation = { - id: RANDOM_INT, - type: 'rand_int', - }; - - graph.nodes[RANDOM_INT] = randomIntNode; - - // Connect random int to the start of the range of size so the range starts on the random first seed - graph.edges.push({ - source: { node_id: RANDOM_INT, field: 'a' }, - destination: { node_id: RANGE_OF_SIZE, field: 'start' }, - }); - } else { - // User specified seed, so set the start of the range of size to the seed - (graph.nodes[RANGE_OF_SIZE] as RangeOfSizeInvocation).start = seed; - } - // handle `fit` if ( shouldFitToWidthHeight && @@ -313,9 +254,9 @@ export const buildLinearImageToImageGraph = ( }); } else { // We are not resizing, so we need to set the image on the `IMAGE_TO_LATENTS` node explicitly - set(graph.nodes[IMAGE_TO_LATENTS], 'image', { + (graph.nodes[IMAGE_TO_LATENTS] as ImageToLatentsInvocation).image = { image_name: initialImage.imageName, - }); + }; // Pass the image's dimensions to the `NOISE` node graph.edges.push({ @@ -334,7 +275,10 @@ export const buildLinearImageToImageGraph = ( }); } - // add controlnet + // add dynamic prompts, mutating `graph` + addDynamicPromptsToGraph(graph, state); + + // add controlnet, mutating `graph` addControlNetToLinearGraph(graph, LATENTS_TO_LATENTS, state); return graph; diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts index b8bdb1efd0..216c5c8c67 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts @@ -1,33 +1,20 @@ import { RootState } from 'app/store/store'; import { NonNullableGraph } from 'features/nodes/types/types'; import { - BaseModelType, - RandomIntInvocation, - RangeOfSizeInvocation, -} from 'services/api/types'; -import { - ITERATE, LATENTS_TO_IMAGE, - MODEL_LOADER, + PIPELINE_MODEL_LOADER, NEGATIVE_CONDITIONING, NOISE, POSITIVE_CONDITIONING, - RANDOM_INT, - RANGE_OF_SIZE, TEXT_TO_IMAGE_GRAPH, TEXT_TO_LATENTS, } from './constants'; import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph'; import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField'; - -type TextToImageGraphOverrides = { - width: number; - height: number; -}; +import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; export const buildLinearTextToImageGraph = ( - state: RootState, - overrides?: TextToImageGraphOverrides + state: RootState ): NonNullableGraph => { const { positivePrompt, @@ -38,9 +25,6 @@ export const buildLinearTextToImageGraph = ( steps, width, height, - iterations, - seed, - shouldRandomizeSeed, } = state.generation; const model = modelIdToPipelineModelField(modelId); @@ -68,18 +52,11 @@ export const buildLinearTextToImageGraph = ( id: NEGATIVE_CONDITIONING, prompt: negativePrompt, }, - [RANGE_OF_SIZE]: { - type: 'range_of_size', - id: RANGE_OF_SIZE, - // start: 0, // seed - must be connected manually - size: iterations, - step: 1, - }, [NOISE]: { type: 'noise', id: NOISE, - width: overrides?.width || width, - height: overrides?.height || height, + width, + height, }, [TEXT_TO_LATENTS]: { type: 't2l', @@ -88,19 +65,15 @@ export const buildLinearTextToImageGraph = ( scheduler, steps, }, - [MODEL_LOADER]: { + [PIPELINE_MODEL_LOADER]: { type: 'pipeline_model_loader', - id: MODEL_LOADER, + id: PIPELINE_MODEL_LOADER, model, }, [LATENTS_TO_IMAGE]: { type: 'l2i', id: LATENTS_TO_IMAGE, }, - [ITERATE]: { - type: 'iterate', - id: ITERATE, - }, }, edges: [ { @@ -125,7 +98,7 @@ export const buildLinearTextToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'clip', }, destination: { @@ -135,7 +108,7 @@ export const buildLinearTextToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'clip', }, destination: { @@ -145,7 +118,7 @@ export const buildLinearTextToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'unet', }, destination: { @@ -165,7 +138,7 @@ export const buildLinearTextToImageGraph = ( }, { source: { - node_id: MODEL_LOADER, + node_id: PIPELINE_MODEL_LOADER, field: 'vae', }, destination: { @@ -173,26 +146,6 @@ export const buildLinearTextToImageGraph = ( field: 'vae', }, }, - { - source: { - node_id: RANGE_OF_SIZE, - field: 'collection', - }, - destination: { - node_id: ITERATE, - field: 'collection', - }, - }, - { - source: { - node_id: ITERATE, - field: 'item', - }, - destination: { - node_id: NOISE, - field: 'seed', - }, - }, { source: { node_id: NOISE, @@ -206,27 +159,10 @@ export const buildLinearTextToImageGraph = ( ], }; - // handle seed - if (shouldRandomizeSeed) { - // Random int node to generate the starting seed - const randomIntNode: RandomIntInvocation = { - id: RANDOM_INT, - type: 'rand_int', - }; + // add dynamic prompts, mutating `graph` + addDynamicPromptsToGraph(graph, state); - graph.nodes[RANDOM_INT] = randomIntNode; - - // Connect random int to the start of the range of size so the range starts on the random first seed - graph.edges.push({ - source: { node_id: RANDOM_INT, field: 'a' }, - destination: { node_id: RANGE_OF_SIZE, field: 'start' }, - }); - } else { - // User specified seed, so set the start of the range of size to the seed - (graph.nodes[RANGE_OF_SIZE] as RangeOfSizeInvocation).start = seed; - } - - // add controlnet + // add controlnet, mutating `graph` addControlNetToLinearGraph(graph, TEXT_TO_LATENTS, state); return graph; diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts index 7d4469bc41..d6ab33a6ea 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts @@ -7,12 +7,13 @@ export const NOISE = 'noise'; export const RANDOM_INT = 'rand_int'; export const RANGE_OF_SIZE = 'range_of_size'; export const ITERATE = 'iterate'; -export const MODEL_LOADER = 'pipeline_model_loader'; +export const PIPELINE_MODEL_LOADER = 'pipeline_model_loader'; export const IMAGE_TO_LATENTS = 'image_to_latents'; export const LATENTS_TO_LATENTS = 'latents_to_latents'; export const RESIZE = 'resize_image'; export const INPAINT = 'inpaint'; export const CONTROL_NET_COLLECT = 'control_net_collect'; +export const DYNAMIC_PROMPT = 'dynamic_prompt'; // friendly graph ids export const TEXT_TO_IMAGE_GRAPH = 'text_to_image_graph'; diff --git a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildCompelNode.ts b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildCompelNode.ts deleted file mode 100644 index 8499c21f0b..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildCompelNode.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { RootState } from 'app/store/store'; -import { CompelInvocation } from 'services/api/types'; -import { O } from 'ts-toolbelt'; - -export const buildCompelNode = ( - prompt: string, - state: RootState, - overrides: O.Partial = {} -): CompelInvocation => { - const nodeId = uuidv4(); - const { generation } = state; - - const { model } = generation; - - const compelNode: CompelInvocation = { - id: nodeId, - type: 'compel', - prompt, - model, - }; - - Object.assign(compelNode, overrides); - - return compelNode; -}; diff --git a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildImageToImageNode.ts b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildImageToImageNode.ts deleted file mode 100644 index 0e0e498370..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildImageToImageNode.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { RootState } from 'app/store/store'; -import { - Edge, - ImageToImageInvocation, - TextToImageInvocation, -} from 'services/api/types'; -import { O } from 'ts-toolbelt'; -import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; - -export const buildImg2ImgNode = ( - state: RootState, - overrides: O.Partial = {} -): ImageToImageInvocation => { - const nodeId = uuidv4(); - const { generation } = state; - - const activeTabName = activeTabNameSelector(state); - - const { - positivePrompt: prompt, - negativePrompt: negativePrompt, - seed, - steps, - width, - height, - cfgScale, - scheduler, - model, - img2imgStrength: strength, - shouldFitToWidthHeight: fit, - shouldRandomizeSeed, - initialImage, - } = generation; - - // const initialImage = initialImageSelector(state); - - const imageToImageNode: ImageToImageInvocation = { - id: nodeId, - type: 'img2img', - prompt: `${prompt} [${negativePrompt}]`, - steps, - width, - height, - cfg_scale: cfgScale, - scheduler, - model, - strength, - fit, - }; - - // on Canvas tab, we do not manually specific init image - if (activeTabName !== 'unifiedCanvas') { - if (!initialImage) { - // TODO: handle this more better - throw 'no initial image'; - } - - imageToImageNode.image = { - image_name: initialImage.imageName, - }; - } - - if (!shouldRandomizeSeed) { - imageToImageNode.seed = seed; - } - - Object.assign(imageToImageNode, overrides); - - return imageToImageNode; -}; - -type hiresReturnType = { - node: Record; - edge: Edge; -}; - -export const buildHiResNode = ( - baseNode: Record, - strength?: number -): hiresReturnType => { - const nodeId = uuidv4(); - const baseNodeId = Object.keys(baseNode)[0]; - const baseNodeValues = Object.values(baseNode)[0]; - - return { - node: { - [nodeId]: { - ...baseNodeValues, - id: nodeId, - type: 'img2img', - strength, - fit: true, - }, - }, - edge: { - source: { - field: 'image', - node_id: baseNodeId, - }, - destination: { - field: 'image', - node_id: nodeId, - }, - }, - }; -}; diff --git a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildInpaintNode.ts b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildInpaintNode.ts deleted file mode 100644 index 91b5128931..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildInpaintNode.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { RootState } from 'app/store/store'; -import { InpaintInvocation } from 'services/api/types'; -import { O } from 'ts-toolbelt'; - -export const buildInpaintNode = ( - state: RootState, - overrides: O.Partial = {} -): InpaintInvocation => { - const nodeId = uuidv4(); - - const { - positivePrompt: prompt, - negativePrompt: negativePrompt, - seed, - steps, - width, - height, - cfgScale, - scheduler, - model, - img2imgStrength: strength, - shouldFitToWidthHeight: fit, - shouldRandomizeSeed, - } = state.generation; - - const inpaintNode: InpaintInvocation = { - id: nodeId, - type: 'inpaint', - prompt: `${prompt} [${negativePrompt}]`, - steps, - width, - height, - cfg_scale: cfgScale, - scheduler, - model, - strength, - fit, - }; - - if (!shouldRandomizeSeed) { - inpaintNode.seed = seed; - } - - Object.assign(inpaintNode, overrides); - - return inpaintNode; -}; diff --git a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildIterateNode.ts b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildIterateNode.ts deleted file mode 100644 index 12e4e0e6e3..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildIterateNode.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; - -import { IterateInvocation } from 'services/api/types'; - -export const buildIterateNode = (): IterateInvocation => { - const nodeId = uuidv4(); - return { - id: nodeId, - type: 'iterate', - // collection: [], - // index: 0, - }; -}; diff --git a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildRangeNode.ts b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildRangeNode.ts deleted file mode 100644 index a18a210f20..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildRangeNode.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; - -import { RootState } from 'app/store/store'; -import { RandomRangeInvocation, RangeInvocation } from 'services/api/types'; - -export const buildRangeNode = ( - state: RootState -): RangeInvocation | RandomRangeInvocation => { - const nodeId = uuidv4(); - const { shouldRandomizeSeed, iterations, seed } = state.generation; - - if (shouldRandomizeSeed) { - return { - id: nodeId, - type: 'random_range', - size: iterations, - }; - } - - return { - id: nodeId, - type: 'range', - start: seed, - stop: seed + iterations, - }; -}; diff --git a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildTextToImageNode.ts b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildTextToImageNode.ts deleted file mode 100644 index efe5f50d9c..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildTextToImageNode.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { RootState } from 'app/store/store'; -import { TextToImageInvocation } from 'services/api/types'; -import { O } from 'ts-toolbelt'; - -export const buildTxt2ImgNode = ( - state: RootState, - overrides: O.Partial = {} -): TextToImageInvocation => { - const nodeId = uuidv4(); - const { generation } = state; - - const { - positivePrompt: prompt, - negativePrompt: negativePrompt, - seed, - steps, - width, - height, - cfgScale: cfg_scale, - scheduler, - shouldRandomizeSeed, - model, - } = generation; - - const textToImageNode: NonNullable = { - id: nodeId, - type: 'txt2img', - prompt: `${prompt} [${negativePrompt}]`, - steps, - width, - height, - cfg_scale, - scheduler, - model, - }; - - if (!shouldRandomizeSeed) { - textToImageNode.seed = seed; - } - - Object.assign(textToImageNode, overrides); - - return textToImageNode; -}; diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamIterations.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamIterations.tsx index 5a5b782c04..8dce097d82 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamIterations.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamIterations.tsx @@ -1,4 +1,5 @@ import { createSelector } from '@reduxjs/toolkit'; +import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAINumberInput from 'common/components/IAINumberInput'; import IAISlider from 'common/components/IAISlider'; @@ -10,27 +11,26 @@ import { uiSelector } from 'features/ui/store/uiSelectors'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createSelector( - [generationSelector, configSelector, uiSelector, hotkeysSelector], - (generation, config, ui, hotkeys) => { - const { initial, min, sliderMax, inputMax, fineStep, coarseStep } = - config.sd.iterations; - const { iterations } = generation; - const { shouldUseSliders } = ui; +const selector = createSelector([stateSelector], (state) => { + const { initial, min, sliderMax, inputMax, fineStep, coarseStep } = + state.config.sd.iterations; + const { iterations } = state.generation; + const { shouldUseSliders } = state.ui; + const isDisabled = state.dynamicPrompts.isEnabled; - const step = hotkeys.shift ? fineStep : coarseStep; + const step = state.hotkeys.shift ? fineStep : coarseStep; - return { - iterations, - initial, - min, - sliderMax, - inputMax, - step, - shouldUseSliders, - }; - } -); + return { + iterations, + initial, + min, + sliderMax, + inputMax, + step, + shouldUseSliders, + isDisabled, + }; +}); const ParamIterations = () => { const { @@ -41,6 +41,7 @@ const ParamIterations = () => { inputMax, step, shouldUseSliders, + isDisabled, } = useAppSelector(selector); const dispatch = useAppDispatch(); const { t } = useTranslation(); @@ -58,6 +59,7 @@ const ParamIterations = () => { return shouldUseSliders ? ( { /> ) : ( { return ( @@ -16,6 +17,7 @@ const ImageToImageTabParameters = () => { + diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx index a28fa71407..bcc6c91ae6 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx @@ -9,6 +9,7 @@ import ParamHiresCollapse from 'features/parameters/components/Parameters/Hires/ import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse'; import TextToImageTabCoreParameters from './TextToImageTabCoreParameters'; import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse'; +import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse'; const TextToImageTabParameters = () => { return ( @@ -17,6 +18,7 @@ const TextToImageTabParameters = () => { + diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasParameters.tsx index 8e17ff066c..061ebb962e 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasParameters.tsx @@ -8,6 +8,7 @@ import { memo } from 'react'; import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning'; import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning'; import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse'; +import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse'; const UnifiedCanvasParameters = () => { return ( @@ -16,6 +17,7 @@ const UnifiedCanvasParameters = () => { + diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.ts b/invokeai/frontend/web/src/services/api/endpoints/images.ts index 0094af4e39..5090fc4fc1 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/images.ts +++ b/invokeai/frontend/web/src/services/api/endpoints/images.ts @@ -15,6 +15,7 @@ export const imagesApi = api.injectEndpoints({ } return tags; }, + keepUnusedDataFor: 86400, // 24 hours }), }), }); diff --git a/invokeai/frontend/web/src/services/api/types.d.ts b/invokeai/frontend/web/src/services/api/types.d.ts index 5fa6870bc3..a995d9c298 100644 --- a/invokeai/frontend/web/src/services/api/types.d.ts +++ b/invokeai/frontend/web/src/services/api/types.d.ts @@ -47,6 +47,15 @@ export type InpaintInvocation = Invocation<'InpaintInvocation'>; export type ImageResizeInvocation = Invocation<'ImageResizeInvocation'>; export type RandomIntInvocation = Invocation<'RandomIntInvocation'>; export type CompelInvocation = Invocation<'CompelInvocation'>; +export type DynamicPromptInvocation = Invocation<'DynamicPromptInvocation'>; +export type NoiseInvocation = Invocation<'NoiseInvocation'>; +export type TextToLatentsInvocation = Invocation<'TextToLatentsInvocation'>; +export type LatentsToLatentsInvocation = + Invocation<'LatentsToLatentsInvocation'>; +export type ImageToLatentsInvocation = Invocation<'ImageToLatentsInvocation'>; +export type LatentsToImageInvocation = Invocation<'LatentsToImageInvocation'>; +export type PipelineModelLoaderInvocation = + Invocation<'PipelineModelLoaderInvocation'>; // ControlNet Nodes export type ControlNetInvocation = Invocation<'ControlNetInvocation'>;