mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): add dynamic prompts to t2i tab
- add param accordion for dynamic prompts - update graphs
This commit is contained in:
parent
9cfac4175f
commit
6390af229d
@ -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<typeof store.getState>;
|
||||
export type AppThunkDispatch = ThunkDispatch<RootState, any, AnyAction>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
export const stateSelector = (state: RootState) => state;
|
||||
|
@ -171,6 +171,14 @@ export type AppConfig = {
|
||||
fineStep: number;
|
||||
coarseStep: number;
|
||||
};
|
||||
dynamicPrompts: {
|
||||
maxPrompts: {
|
||||
initial: number;
|
||||
min: number;
|
||||
sliderMax: number;
|
||||
inputMax: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -41,7 +41,15 @@ const IAISwitch = (props: Props) => {
|
||||
{...formControlProps}
|
||||
>
|
||||
{label && (
|
||||
<FormLabel my={1} flexGrow={1} {...formLabelProps}>
|
||||
<FormLabel
|
||||
my={1}
|
||||
flexGrow={1}
|
||||
sx={{
|
||||
cursor: isDisabled ? 'not-allowed' : 'pointer',
|
||||
...formLabelProps?.sx,
|
||||
}}
|
||||
{...formLabelProps}
|
||||
>
|
||||
{label}
|
||||
</FormLabel>
|
||||
)}
|
||||
|
@ -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 (
|
||||
<IAICollapse
|
||||
isOpen={isEnabled}
|
||||
onToggle={handleToggleIsEnabled}
|
||||
label="Dynamic Prompts"
|
||||
withSwitch
|
||||
>
|
||||
<Flex sx={{ gap: 2, flexDir: 'column' }}>
|
||||
<ParamDynamicPromptsMaxPrompts />
|
||||
<ParamDynamicPromptsCombinatorial />
|
||||
</Flex>
|
||||
</IAICollapse>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParamDynamicPromptsCollapse;
|
@ -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 (
|
||||
<IAISwitch
|
||||
label="Combinatorial Generation"
|
||||
isChecked={combinatorial}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParamDynamicPromptsCombinatorial;
|
@ -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 (
|
||||
<IAISlider
|
||||
label="Max Prompts"
|
||||
min={min}
|
||||
max={sliderMax}
|
||||
value={maxPrompts}
|
||||
onChange={handleChange}
|
||||
sliderNumberInputProps={{ max: inputMax }}
|
||||
withSliderMarks
|
||||
withInput
|
||||
inputReadOnly
|
||||
withReset
|
||||
handleReset={handleReset}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParamDynamicPromptsMaxPrompts;
|
@ -0,0 +1 @@
|
||||
//
|
@ -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<number>) => {
|
||||
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;
|
@ -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: {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
@ -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;
|
||||
|
@ -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: {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
@ -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, 'deep'> = {}
|
||||
): 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;
|
||||
};
|
@ -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, 'deep'> = {}
|
||||
): 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<string, ImageToImageInvocation>;
|
||||
edge: Edge;
|
||||
};
|
||||
|
||||
export const buildHiResNode = (
|
||||
baseNode: Record<string, TextToImageInvocation>,
|
||||
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,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
@ -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, 'deep'> = {}
|
||||
): 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;
|
||||
};
|
@ -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,
|
||||
};
|
||||
};
|
@ -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,
|
||||
};
|
||||
};
|
@ -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, 'deep'> = {}
|
||||
): 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<TextToImageInvocation> = {
|
||||
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;
|
||||
};
|
@ -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 ? (
|
||||
<IAISlider
|
||||
isDisabled={isDisabled}
|
||||
label={t('parameters.images')}
|
||||
step={step}
|
||||
min={min}
|
||||
@ -72,6 +74,7 @@ const ParamIterations = () => {
|
||||
/>
|
||||
) : (
|
||||
<IAINumberInput
|
||||
isDisabled={isDisabled}
|
||||
label={t('parameters.images')}
|
||||
step={step}
|
||||
min={min}
|
||||
|
@ -60,6 +60,14 @@ export const initialConfigState: AppConfig = {
|
||||
fineStep: 0.01,
|
||||
coarseStep: 0.05,
|
||||
},
|
||||
dynamicPrompts: {
|
||||
maxPrompts: {
|
||||
initial: 100,
|
||||
min: 1,
|
||||
sliderMax: 1000,
|
||||
inputMax: 10000,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,7 @@ import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Sym
|
||||
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
||||
import ImageToImageTabCoreParameters from './ImageToImageTabCoreParameters';
|
||||
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
||||
import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse';
|
||||
|
||||
const ImageToImageTabParameters = () => {
|
||||
return (
|
||||
@ -16,6 +17,7 @@ const ImageToImageTabParameters = () => {
|
||||
<ParamNegativeConditioning />
|
||||
<ProcessButtons />
|
||||
<ImageToImageTabCoreParameters />
|
||||
<ParamDynamicPromptsCollapse />
|
||||
<ParamControlNetCollapse />
|
||||
<ParamVariationCollapse />
|
||||
<ParamNoiseCollapse />
|
||||
|
@ -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 = () => {
|
||||
<ParamNegativeConditioning />
|
||||
<ProcessButtons />
|
||||
<TextToImageTabCoreParameters />
|
||||
<ParamDynamicPromptsCollapse />
|
||||
<ParamControlNetCollapse />
|
||||
<ParamVariationCollapse />
|
||||
<ParamNoiseCollapse />
|
||||
|
@ -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 = () => {
|
||||
<ParamNegativeConditioning />
|
||||
<ProcessButtons />
|
||||
<UnifiedCanvasCoreParameters />
|
||||
<ParamDynamicPromptsCollapse />
|
||||
<ParamControlNetCollapse />
|
||||
<ParamVariationCollapse />
|
||||
<ParamSymmetryCollapse />
|
||||
|
@ -15,6 +15,7 @@ export const imagesApi = api.injectEndpoints({
|
||||
}
|
||||
return tags;
|
||||
},
|
||||
keepUnusedDataFor: 86400, // 24 hours
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
@ -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'>;
|
||||
|
Loading…
Reference in New Issue
Block a user