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 configReducer from 'features/system/store/configSlice';
|
||||||
import hotkeysReducer from 'features/ui/store/hotkeysSlice';
|
import hotkeysReducer from 'features/ui/store/hotkeysSlice';
|
||||||
import uiReducer from 'features/ui/store/uiSlice';
|
import uiReducer from 'features/ui/store/uiSlice';
|
||||||
|
import dynamicPromptsReducer from 'features/dynamicPrompts/store/slice';
|
||||||
|
|
||||||
import { listenerMiddleware } from './middleware/listenerMiddleware';
|
import { listenerMiddleware } from './middleware/listenerMiddleware';
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ const allReducers = {
|
|||||||
controlNet: controlNetReducer,
|
controlNet: controlNetReducer,
|
||||||
boards: boardsReducer,
|
boards: boardsReducer,
|
||||||
// session: sessionReducer,
|
// session: sessionReducer,
|
||||||
|
dynamicPrompts: dynamicPromptsReducer,
|
||||||
[api.reducerPath]: api.reducer,
|
[api.reducerPath]: api.reducer,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,6 +67,7 @@ const rememberedKeys: (keyof typeof allReducers)[] = [
|
|||||||
'system',
|
'system',
|
||||||
'ui',
|
'ui',
|
||||||
'controlNet',
|
'controlNet',
|
||||||
|
'dynamicPrompts',
|
||||||
// 'boards',
|
// 'boards',
|
||||||
// 'hotkeys',
|
// 'hotkeys',
|
||||||
// 'config',
|
// 'config',
|
||||||
@ -100,3 +103,4 @@ export type AppGetState = typeof store.getState;
|
|||||||
export type RootState = ReturnType<typeof store.getState>;
|
export type RootState = ReturnType<typeof store.getState>;
|
||||||
export type AppThunkDispatch = ThunkDispatch<RootState, any, AnyAction>;
|
export type AppThunkDispatch = ThunkDispatch<RootState, any, AnyAction>;
|
||||||
export type AppDispatch = typeof store.dispatch;
|
export type AppDispatch = typeof store.dispatch;
|
||||||
|
export const stateSelector = (state: RootState) => state;
|
||||||
|
@ -171,6 +171,14 @@ export type AppConfig = {
|
|||||||
fineStep: number;
|
fineStep: number;
|
||||||
coarseStep: number;
|
coarseStep: number;
|
||||||
};
|
};
|
||||||
|
dynamicPrompts: {
|
||||||
|
maxPrompts: {
|
||||||
|
initial: number;
|
||||||
|
min: number;
|
||||||
|
sliderMax: number;
|
||||||
|
inputMax: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,7 +41,15 @@ const IAISwitch = (props: Props) => {
|
|||||||
{...formControlProps}
|
{...formControlProps}
|
||||||
>
|
>
|
||||||
{label && (
|
{label && (
|
||||||
<FormLabel my={1} flexGrow={1} {...formLabelProps}>
|
<FormLabel
|
||||||
|
my={1}
|
||||||
|
flexGrow={1}
|
||||||
|
sx={{
|
||||||
|
cursor: isDisabled ? 'not-allowed' : 'pointer',
|
||||||
|
...formLabelProps?.sx,
|
||||||
|
}}
|
||||||
|
{...formLabelProps}
|
||||||
|
>
|
||||||
{label}
|
{label}
|
||||||
</FormLabel>
|
</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 { 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 { CollectInvocation, ControlNetInvocation } from 'services/api/types';
|
||||||
import { NonNullableGraph } from '../types/types';
|
import { NonNullableGraph } from '../types/types';
|
||||||
import { CONTROL_NET_COLLECT } from './graphBuilders/constants';
|
import { CONTROL_NET_COLLECT } from './graphBuilders/constants';
|
||||||
@ -19,9 +19,9 @@ export const addControlNetToLinearGraph = (
|
|||||||
(c.processorType === 'none' && Boolean(c.controlImage)))
|
(c.processorType === 'none' && Boolean(c.controlImage)))
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add ControlNet
|
if (isControlNetEnabled && Boolean(validControlNets.length)) {
|
||||||
if (isControlNetEnabled && validControlNets.length > 0) {
|
if (validControlNets.length > 1) {
|
||||||
if (size(controlNets) > 1) {
|
// We have multiple controlnets, add ControlNet collector
|
||||||
const controlNetIterateNode: CollectInvocation = {
|
const controlNetIterateNode: CollectInvocation = {
|
||||||
id: CONTROL_NET_COLLECT,
|
id: CONTROL_NET_COLLECT,
|
||||||
type: 'collect',
|
type: 'collect',
|
||||||
@ -36,10 +36,9 @@ export const addControlNetToLinearGraph = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
forEach(controlNets, (controlNet) => {
|
validControlNets.forEach((controlNet) => {
|
||||||
const {
|
const {
|
||||||
controlNetId,
|
controlNetId,
|
||||||
isEnabled,
|
|
||||||
controlImage,
|
controlImage,
|
||||||
processedControlImage,
|
processedControlImage,
|
||||||
beginStepPct,
|
beginStepPct,
|
||||||
@ -50,11 +49,6 @@ export const addControlNetToLinearGraph = (
|
|||||||
weight,
|
weight,
|
||||||
} = controlNet;
|
} = controlNet;
|
||||||
|
|
||||||
if (!isEnabled) {
|
|
||||||
// Skip disabled ControlNets
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const controlNetNode: ControlNetInvocation = {
|
const controlNetNode: ControlNetInvocation = {
|
||||||
id: `control_net_${controlNetId}`,
|
id: `control_net_${controlNetId}`,
|
||||||
type: 'controlnet',
|
type: 'controlnet',
|
||||||
@ -82,7 +76,8 @@ export const addControlNetToLinearGraph = (
|
|||||||
|
|
||||||
graph.nodes[controlNetNode.id] = controlNetNode;
|
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({
|
graph.edges.push({
|
||||||
source: { node_id: controlNetNode.id, field: 'control' },
|
source: { node_id: controlNetNode.id, field: 'control' },
|
||||||
destination: {
|
destination: {
|
||||||
@ -91,6 +86,7 @@ export const addControlNetToLinearGraph = (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// otherwise, link directly to the base node
|
||||||
graph.edges.push({
|
graph.edges.push({
|
||||||
source: { node_id: controlNetNode.id, field: 'control' },
|
source: { node_id: controlNetNode.id, field: 'control' },
|
||||||
destination: {
|
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 {
|
import {
|
||||||
ImageDTO,
|
ImageDTO,
|
||||||
ImageResizeInvocation,
|
ImageResizeInvocation,
|
||||||
|
ImageToLatentsInvocation,
|
||||||
RandomIntInvocation,
|
RandomIntInvocation,
|
||||||
RangeOfSizeInvocation,
|
RangeOfSizeInvocation,
|
||||||
} from 'services/api/types';
|
} from 'services/api/types';
|
||||||
@ -10,7 +11,7 @@ import { log } from 'app/logging/useLogger';
|
|||||||
import {
|
import {
|
||||||
ITERATE,
|
ITERATE,
|
||||||
LATENTS_TO_IMAGE,
|
LATENTS_TO_IMAGE,
|
||||||
MODEL_LOADER,
|
PIPELINE_MODEL_LOADER,
|
||||||
NEGATIVE_CONDITIONING,
|
NEGATIVE_CONDITIONING,
|
||||||
NOISE,
|
NOISE,
|
||||||
POSITIVE_CONDITIONING,
|
POSITIVE_CONDITIONING,
|
||||||
@ -24,6 +25,7 @@ import {
|
|||||||
import { set } from 'lodash-es';
|
import { set } from 'lodash-es';
|
||||||
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
||||||
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
||||||
|
import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph';
|
||||||
|
|
||||||
const moduleLog = log.child({ namespace: 'nodes' });
|
const moduleLog = log.child({ namespace: 'nodes' });
|
||||||
|
|
||||||
@ -75,31 +77,19 @@ export const buildCanvasImageToImageGraph = (
|
|||||||
id: NEGATIVE_CONDITIONING,
|
id: NEGATIVE_CONDITIONING,
|
||||||
prompt: negativePrompt,
|
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]: {
|
[NOISE]: {
|
||||||
type: 'noise',
|
type: 'noise',
|
||||||
id: NOISE,
|
id: NOISE,
|
||||||
},
|
},
|
||||||
[MODEL_LOADER]: {
|
[PIPELINE_MODEL_LOADER]: {
|
||||||
type: 'pipeline_model_loader',
|
type: 'pipeline_model_loader',
|
||||||
id: MODEL_LOADER,
|
id: PIPELINE_MODEL_LOADER,
|
||||||
model,
|
model,
|
||||||
},
|
},
|
||||||
[LATENTS_TO_IMAGE]: {
|
[LATENTS_TO_IMAGE]: {
|
||||||
type: 'l2i',
|
type: 'l2i',
|
||||||
id: LATENTS_TO_IMAGE,
|
id: LATENTS_TO_IMAGE,
|
||||||
},
|
},
|
||||||
[ITERATE]: {
|
|
||||||
type: 'iterate',
|
|
||||||
id: ITERATE,
|
|
||||||
},
|
|
||||||
[LATENTS_TO_LATENTS]: {
|
[LATENTS_TO_LATENTS]: {
|
||||||
type: 'l2l',
|
type: 'l2l',
|
||||||
id: LATENTS_TO_LATENTS,
|
id: LATENTS_TO_LATENTS,
|
||||||
@ -120,7 +110,7 @@ export const buildCanvasImageToImageGraph = (
|
|||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'clip',
|
field: 'clip',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -130,7 +120,7 @@ export const buildCanvasImageToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'clip',
|
field: 'clip',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -140,7 +130,7 @@ export const buildCanvasImageToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'vae',
|
field: 'vae',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -148,26 +138,6 @@ export const buildCanvasImageToImageGraph = (
|
|||||||
field: 'vae',
|
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: {
|
source: {
|
||||||
node_id: LATENTS_TO_LATENTS,
|
node_id: LATENTS_TO_LATENTS,
|
||||||
@ -200,7 +170,7 @@ export const buildCanvasImageToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'vae',
|
field: 'vae',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -210,7 +180,7 @@ export const buildCanvasImageToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'unet',
|
field: 'unet',
|
||||||
},
|
},
|
||||||
destination: {
|
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`
|
// handle `fit`
|
||||||
if (initialImage.width !== width || initialImage.height !== height) {
|
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`
|
// 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 {
|
} else {
|
||||||
// We are not resizing, so we need to set the image on the `IMAGE_TO_LATENTS` node explicitly
|
// 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,
|
image_name: initialImage.image_name,
|
||||||
});
|
};
|
||||||
|
|
||||||
// Pass the image's dimensions to the `NOISE` node
|
// Pass the image's dimensions to the `NOISE` node
|
||||||
graph.edges.push({
|
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);
|
addControlNetToLinearGraph(graph, LATENTS_TO_LATENTS, state);
|
||||||
|
|
||||||
return graph;
|
return graph;
|
||||||
|
@ -9,7 +9,7 @@ import { NonNullableGraph } from 'features/nodes/types/types';
|
|||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
import {
|
import {
|
||||||
ITERATE,
|
ITERATE,
|
||||||
MODEL_LOADER,
|
PIPELINE_MODEL_LOADER,
|
||||||
NEGATIVE_CONDITIONING,
|
NEGATIVE_CONDITIONING,
|
||||||
POSITIVE_CONDITIONING,
|
POSITIVE_CONDITIONING,
|
||||||
RANDOM_INT,
|
RANDOM_INT,
|
||||||
@ -101,9 +101,9 @@ export const buildCanvasInpaintGraph = (
|
|||||||
id: NEGATIVE_CONDITIONING,
|
id: NEGATIVE_CONDITIONING,
|
||||||
prompt: negativePrompt,
|
prompt: negativePrompt,
|
||||||
},
|
},
|
||||||
[MODEL_LOADER]: {
|
[PIPELINE_MODEL_LOADER]: {
|
||||||
type: 'pipeline_model_loader',
|
type: 'pipeline_model_loader',
|
||||||
id: MODEL_LOADER,
|
id: PIPELINE_MODEL_LOADER,
|
||||||
model,
|
model,
|
||||||
},
|
},
|
||||||
[RANGE_OF_SIZE]: {
|
[RANGE_OF_SIZE]: {
|
||||||
@ -142,7 +142,7 @@ export const buildCanvasInpaintGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'clip',
|
field: 'clip',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -152,7 +152,7 @@ export const buildCanvasInpaintGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'clip',
|
field: 'clip',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -162,7 +162,7 @@ export const buildCanvasInpaintGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'unet',
|
field: 'unet',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -172,7 +172,7 @@ export const buildCanvasInpaintGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'vae',
|
field: 'vae',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
|
@ -4,7 +4,7 @@ import { RandomIntInvocation, RangeOfSizeInvocation } from 'services/api/types';
|
|||||||
import {
|
import {
|
||||||
ITERATE,
|
ITERATE,
|
||||||
LATENTS_TO_IMAGE,
|
LATENTS_TO_IMAGE,
|
||||||
MODEL_LOADER,
|
PIPELINE_MODEL_LOADER,
|
||||||
NEGATIVE_CONDITIONING,
|
NEGATIVE_CONDITIONING,
|
||||||
NOISE,
|
NOISE,
|
||||||
POSITIVE_CONDITIONING,
|
POSITIVE_CONDITIONING,
|
||||||
@ -15,6 +15,7 @@ import {
|
|||||||
} from './constants';
|
} from './constants';
|
||||||
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
||||||
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
||||||
|
import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the Canvas tab's Text to Image graph.
|
* Builds the Canvas tab's Text to Image graph.
|
||||||
@ -62,13 +63,6 @@ export const buildCanvasTextToImageGraph = (
|
|||||||
id: NEGATIVE_CONDITIONING,
|
id: NEGATIVE_CONDITIONING,
|
||||||
prompt: negativePrompt,
|
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]: {
|
[NOISE]: {
|
||||||
type: 'noise',
|
type: 'noise',
|
||||||
id: NOISE,
|
id: NOISE,
|
||||||
@ -82,19 +76,15 @@ export const buildCanvasTextToImageGraph = (
|
|||||||
scheduler,
|
scheduler,
|
||||||
steps,
|
steps,
|
||||||
},
|
},
|
||||||
[MODEL_LOADER]: {
|
[PIPELINE_MODEL_LOADER]: {
|
||||||
type: 'pipeline_model_loader',
|
type: 'pipeline_model_loader',
|
||||||
id: MODEL_LOADER,
|
id: PIPELINE_MODEL_LOADER,
|
||||||
model,
|
model,
|
||||||
},
|
},
|
||||||
[LATENTS_TO_IMAGE]: {
|
[LATENTS_TO_IMAGE]: {
|
||||||
type: 'l2i',
|
type: 'l2i',
|
||||||
id: LATENTS_TO_IMAGE,
|
id: LATENTS_TO_IMAGE,
|
||||||
},
|
},
|
||||||
[ITERATE]: {
|
|
||||||
type: 'iterate',
|
|
||||||
id: ITERATE,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
@ -119,7 +109,7 @@ export const buildCanvasTextToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'clip',
|
field: 'clip',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -129,7 +119,7 @@ export const buildCanvasTextToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'clip',
|
field: 'clip',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -139,7 +129,7 @@ export const buildCanvasTextToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'unet',
|
field: 'unet',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -159,7 +149,7 @@ export const buildCanvasTextToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'vae',
|
field: 'vae',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -167,26 +157,6 @@ export const buildCanvasTextToImageGraph = (
|
|||||||
field: 'vae',
|
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: {
|
source: {
|
||||||
node_id: NOISE,
|
node_id: NOISE,
|
||||||
@ -200,27 +170,10 @@ export const buildCanvasTextToImageGraph = (
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// handle seed
|
// add dynamic prompts, mutating `graph`
|
||||||
if (shouldRandomizeSeed) {
|
addDynamicPromptsToGraph(graph, state);
|
||||||
// Random int node to generate the starting seed
|
|
||||||
const randomIntNode: RandomIntInvocation = {
|
|
||||||
id: RANDOM_INT,
|
|
||||||
type: 'rand_int',
|
|
||||||
};
|
|
||||||
|
|
||||||
graph.nodes[RANDOM_INT] = randomIntNode;
|
// add controlnet, mutating `graph`
|
||||||
|
|
||||||
// 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
|
|
||||||
addControlNetToLinearGraph(graph, TEXT_TO_LATENTS, state);
|
addControlNetToLinearGraph(graph, TEXT_TO_LATENTS, state);
|
||||||
|
|
||||||
return graph;
|
return graph;
|
||||||
|
@ -1,28 +1,24 @@
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import {
|
import {
|
||||||
ImageResizeInvocation,
|
ImageResizeInvocation,
|
||||||
RandomIntInvocation,
|
ImageToLatentsInvocation,
|
||||||
RangeOfSizeInvocation,
|
|
||||||
} from 'services/api/types';
|
} from 'services/api/types';
|
||||||
import { NonNullableGraph } from 'features/nodes/types/types';
|
import { NonNullableGraph } from 'features/nodes/types/types';
|
||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
import {
|
import {
|
||||||
ITERATE,
|
|
||||||
LATENTS_TO_IMAGE,
|
LATENTS_TO_IMAGE,
|
||||||
MODEL_LOADER,
|
PIPELINE_MODEL_LOADER,
|
||||||
NEGATIVE_CONDITIONING,
|
NEGATIVE_CONDITIONING,
|
||||||
NOISE,
|
NOISE,
|
||||||
POSITIVE_CONDITIONING,
|
POSITIVE_CONDITIONING,
|
||||||
RANDOM_INT,
|
|
||||||
RANGE_OF_SIZE,
|
|
||||||
IMAGE_TO_IMAGE_GRAPH,
|
IMAGE_TO_IMAGE_GRAPH,
|
||||||
IMAGE_TO_LATENTS,
|
IMAGE_TO_LATENTS,
|
||||||
LATENTS_TO_LATENTS,
|
LATENTS_TO_LATENTS,
|
||||||
RESIZE,
|
RESIZE,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { set } from 'lodash-es';
|
|
||||||
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
||||||
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
||||||
|
import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph';
|
||||||
|
|
||||||
const moduleLog = log.child({ namespace: 'nodes' });
|
const moduleLog = log.child({ namespace: 'nodes' });
|
||||||
|
|
||||||
@ -44,9 +40,6 @@ export const buildLinearImageToImageGraph = (
|
|||||||
shouldFitToWidthHeight,
|
shouldFitToWidthHeight,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
iterations,
|
|
||||||
seed,
|
|
||||||
shouldRandomizeSeed,
|
|
||||||
} = state.generation;
|
} = state.generation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,31 +72,19 @@ export const buildLinearImageToImageGraph = (
|
|||||||
id: NEGATIVE_CONDITIONING,
|
id: NEGATIVE_CONDITIONING,
|
||||||
prompt: negativePrompt,
|
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]: {
|
[NOISE]: {
|
||||||
type: 'noise',
|
type: 'noise',
|
||||||
id: NOISE,
|
id: NOISE,
|
||||||
},
|
},
|
||||||
[MODEL_LOADER]: {
|
[PIPELINE_MODEL_LOADER]: {
|
||||||
type: 'pipeline_model_loader',
|
type: 'pipeline_model_loader',
|
||||||
id: MODEL_LOADER,
|
id: PIPELINE_MODEL_LOADER,
|
||||||
model,
|
model,
|
||||||
},
|
},
|
||||||
[LATENTS_TO_IMAGE]: {
|
[LATENTS_TO_IMAGE]: {
|
||||||
type: 'l2i',
|
type: 'l2i',
|
||||||
id: LATENTS_TO_IMAGE,
|
id: LATENTS_TO_IMAGE,
|
||||||
},
|
},
|
||||||
[ITERATE]: {
|
|
||||||
type: 'iterate',
|
|
||||||
id: ITERATE,
|
|
||||||
},
|
|
||||||
[LATENTS_TO_LATENTS]: {
|
[LATENTS_TO_LATENTS]: {
|
||||||
type: 'l2l',
|
type: 'l2l',
|
||||||
id: LATENTS_TO_LATENTS,
|
id: LATENTS_TO_LATENTS,
|
||||||
@ -124,7 +105,7 @@ export const buildLinearImageToImageGraph = (
|
|||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'clip',
|
field: 'clip',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -134,7 +115,7 @@ export const buildLinearImageToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'clip',
|
field: 'clip',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -144,7 +125,7 @@ export const buildLinearImageToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'vae',
|
field: 'vae',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -152,26 +133,6 @@ export const buildLinearImageToImageGraph = (
|
|||||||
field: 'vae',
|
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: {
|
source: {
|
||||||
node_id: LATENTS_TO_LATENTS,
|
node_id: LATENTS_TO_LATENTS,
|
||||||
@ -204,7 +165,7 @@ export const buildLinearImageToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'vae',
|
field: 'vae',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -214,7 +175,7 @@ export const buildLinearImageToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'unet',
|
field: 'unet',
|
||||||
},
|
},
|
||||||
destination: {
|
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`
|
// handle `fit`
|
||||||
if (
|
if (
|
||||||
shouldFitToWidthHeight &&
|
shouldFitToWidthHeight &&
|
||||||
@ -313,9 +254,9 @@ export const buildLinearImageToImageGraph = (
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// We are not resizing, so we need to set the image on the `IMAGE_TO_LATENTS` node explicitly
|
// 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,
|
image_name: initialImage.imageName,
|
||||||
});
|
};
|
||||||
|
|
||||||
// Pass the image's dimensions to the `NOISE` node
|
// Pass the image's dimensions to the `NOISE` node
|
||||||
graph.edges.push({
|
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);
|
addControlNetToLinearGraph(graph, LATENTS_TO_LATENTS, state);
|
||||||
|
|
||||||
return graph;
|
return graph;
|
||||||
|
@ -1,33 +1,20 @@
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { NonNullableGraph } from 'features/nodes/types/types';
|
import { NonNullableGraph } from 'features/nodes/types/types';
|
||||||
import {
|
import {
|
||||||
BaseModelType,
|
|
||||||
RandomIntInvocation,
|
|
||||||
RangeOfSizeInvocation,
|
|
||||||
} from 'services/api/types';
|
|
||||||
import {
|
|
||||||
ITERATE,
|
|
||||||
LATENTS_TO_IMAGE,
|
LATENTS_TO_IMAGE,
|
||||||
MODEL_LOADER,
|
PIPELINE_MODEL_LOADER,
|
||||||
NEGATIVE_CONDITIONING,
|
NEGATIVE_CONDITIONING,
|
||||||
NOISE,
|
NOISE,
|
||||||
POSITIVE_CONDITIONING,
|
POSITIVE_CONDITIONING,
|
||||||
RANDOM_INT,
|
|
||||||
RANGE_OF_SIZE,
|
|
||||||
TEXT_TO_IMAGE_GRAPH,
|
TEXT_TO_IMAGE_GRAPH,
|
||||||
TEXT_TO_LATENTS,
|
TEXT_TO_LATENTS,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
||||||
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
||||||
|
import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph';
|
||||||
type TextToImageGraphOverrides = {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildLinearTextToImageGraph = (
|
export const buildLinearTextToImageGraph = (
|
||||||
state: RootState,
|
state: RootState
|
||||||
overrides?: TextToImageGraphOverrides
|
|
||||||
): NonNullableGraph => {
|
): NonNullableGraph => {
|
||||||
const {
|
const {
|
||||||
positivePrompt,
|
positivePrompt,
|
||||||
@ -38,9 +25,6 @@ export const buildLinearTextToImageGraph = (
|
|||||||
steps,
|
steps,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
iterations,
|
|
||||||
seed,
|
|
||||||
shouldRandomizeSeed,
|
|
||||||
} = state.generation;
|
} = state.generation;
|
||||||
|
|
||||||
const model = modelIdToPipelineModelField(modelId);
|
const model = modelIdToPipelineModelField(modelId);
|
||||||
@ -68,18 +52,11 @@ export const buildLinearTextToImageGraph = (
|
|||||||
id: NEGATIVE_CONDITIONING,
|
id: NEGATIVE_CONDITIONING,
|
||||||
prompt: negativePrompt,
|
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]: {
|
[NOISE]: {
|
||||||
type: 'noise',
|
type: 'noise',
|
||||||
id: NOISE,
|
id: NOISE,
|
||||||
width: overrides?.width || width,
|
width,
|
||||||
height: overrides?.height || height,
|
height,
|
||||||
},
|
},
|
||||||
[TEXT_TO_LATENTS]: {
|
[TEXT_TO_LATENTS]: {
|
||||||
type: 't2l',
|
type: 't2l',
|
||||||
@ -88,19 +65,15 @@ export const buildLinearTextToImageGraph = (
|
|||||||
scheduler,
|
scheduler,
|
||||||
steps,
|
steps,
|
||||||
},
|
},
|
||||||
[MODEL_LOADER]: {
|
[PIPELINE_MODEL_LOADER]: {
|
||||||
type: 'pipeline_model_loader',
|
type: 'pipeline_model_loader',
|
||||||
id: MODEL_LOADER,
|
id: PIPELINE_MODEL_LOADER,
|
||||||
model,
|
model,
|
||||||
},
|
},
|
||||||
[LATENTS_TO_IMAGE]: {
|
[LATENTS_TO_IMAGE]: {
|
||||||
type: 'l2i',
|
type: 'l2i',
|
||||||
id: LATENTS_TO_IMAGE,
|
id: LATENTS_TO_IMAGE,
|
||||||
},
|
},
|
||||||
[ITERATE]: {
|
|
||||||
type: 'iterate',
|
|
||||||
id: ITERATE,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
@ -125,7 +98,7 @@ export const buildLinearTextToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'clip',
|
field: 'clip',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -135,7 +108,7 @@ export const buildLinearTextToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'clip',
|
field: 'clip',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -145,7 +118,7 @@ export const buildLinearTextToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'unet',
|
field: 'unet',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -165,7 +138,7 @@ export const buildLinearTextToImageGraph = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: {
|
source: {
|
||||||
node_id: MODEL_LOADER,
|
node_id: PIPELINE_MODEL_LOADER,
|
||||||
field: 'vae',
|
field: 'vae',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
@ -173,26 +146,6 @@ export const buildLinearTextToImageGraph = (
|
|||||||
field: 'vae',
|
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: {
|
source: {
|
||||||
node_id: NOISE,
|
node_id: NOISE,
|
||||||
@ -206,27 +159,10 @@ export const buildLinearTextToImageGraph = (
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// handle seed
|
// add dynamic prompts, mutating `graph`
|
||||||
if (shouldRandomizeSeed) {
|
addDynamicPromptsToGraph(graph, state);
|
||||||
// Random int node to generate the starting seed
|
|
||||||
const randomIntNode: RandomIntInvocation = {
|
|
||||||
id: RANDOM_INT,
|
|
||||||
type: 'rand_int',
|
|
||||||
};
|
|
||||||
|
|
||||||
graph.nodes[RANDOM_INT] = randomIntNode;
|
// add controlnet, mutating `graph`
|
||||||
|
|
||||||
// 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
|
|
||||||
addControlNetToLinearGraph(graph, TEXT_TO_LATENTS, state);
|
addControlNetToLinearGraph(graph, TEXT_TO_LATENTS, state);
|
||||||
|
|
||||||
return graph;
|
return graph;
|
||||||
|
@ -7,12 +7,13 @@ export const NOISE = 'noise';
|
|||||||
export const RANDOM_INT = 'rand_int';
|
export const RANDOM_INT = 'rand_int';
|
||||||
export const RANGE_OF_SIZE = 'range_of_size';
|
export const RANGE_OF_SIZE = 'range_of_size';
|
||||||
export const ITERATE = 'iterate';
|
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 IMAGE_TO_LATENTS = 'image_to_latents';
|
||||||
export const LATENTS_TO_LATENTS = 'latents_to_latents';
|
export const LATENTS_TO_LATENTS = 'latents_to_latents';
|
||||||
export const RESIZE = 'resize_image';
|
export const RESIZE = 'resize_image';
|
||||||
export const INPAINT = 'inpaint';
|
export const INPAINT = 'inpaint';
|
||||||
export const CONTROL_NET_COLLECT = 'control_net_collect';
|
export const CONTROL_NET_COLLECT = 'control_net_collect';
|
||||||
|
export const DYNAMIC_PROMPT = 'dynamic_prompt';
|
||||||
|
|
||||||
// friendly graph ids
|
// friendly graph ids
|
||||||
export const TEXT_TO_IMAGE_GRAPH = 'text_to_image_graph';
|
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 { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAINumberInput from 'common/components/IAINumberInput';
|
import IAINumberInput from 'common/components/IAINumberInput';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
@ -10,15 +11,14 @@ import { uiSelector } from 'features/ui/store/uiSelectors';
|
|||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector([stateSelector], (state) => {
|
||||||
[generationSelector, configSelector, uiSelector, hotkeysSelector],
|
|
||||||
(generation, config, ui, hotkeys) => {
|
|
||||||
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
|
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
|
||||||
config.sd.iterations;
|
state.config.sd.iterations;
|
||||||
const { iterations } = generation;
|
const { iterations } = state.generation;
|
||||||
const { shouldUseSliders } = ui;
|
const { shouldUseSliders } = state.ui;
|
||||||
|
const isDisabled = state.dynamicPrompts.isEnabled;
|
||||||
|
|
||||||
const step = hotkeys.shift ? fineStep : coarseStep;
|
const step = state.hotkeys.shift ? fineStep : coarseStep;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
iterations,
|
iterations,
|
||||||
@ -28,9 +28,9 @@ const selector = createSelector(
|
|||||||
inputMax,
|
inputMax,
|
||||||
step,
|
step,
|
||||||
shouldUseSliders,
|
shouldUseSliders,
|
||||||
|
isDisabled,
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const ParamIterations = () => {
|
const ParamIterations = () => {
|
||||||
const {
|
const {
|
||||||
@ -41,6 +41,7 @@ const ParamIterations = () => {
|
|||||||
inputMax,
|
inputMax,
|
||||||
step,
|
step,
|
||||||
shouldUseSliders,
|
shouldUseSliders,
|
||||||
|
isDisabled,
|
||||||
} = useAppSelector(selector);
|
} = useAppSelector(selector);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -58,6 +59,7 @@ const ParamIterations = () => {
|
|||||||
|
|
||||||
return shouldUseSliders ? (
|
return shouldUseSliders ? (
|
||||||
<IAISlider
|
<IAISlider
|
||||||
|
isDisabled={isDisabled}
|
||||||
label={t('parameters.images')}
|
label={t('parameters.images')}
|
||||||
step={step}
|
step={step}
|
||||||
min={min}
|
min={min}
|
||||||
@ -72,6 +74,7 @@ const ParamIterations = () => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<IAINumberInput
|
<IAINumberInput
|
||||||
|
isDisabled={isDisabled}
|
||||||
label={t('parameters.images')}
|
label={t('parameters.images')}
|
||||||
step={step}
|
step={step}
|
||||||
min={min}
|
min={min}
|
||||||
|
@ -60,6 +60,14 @@ export const initialConfigState: AppConfig = {
|
|||||||
fineStep: 0.01,
|
fineStep: 0.01,
|
||||||
coarseStep: 0.05,
|
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 ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
||||||
import ImageToImageTabCoreParameters from './ImageToImageTabCoreParameters';
|
import ImageToImageTabCoreParameters from './ImageToImageTabCoreParameters';
|
||||||
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
||||||
|
import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse';
|
||||||
|
|
||||||
const ImageToImageTabParameters = () => {
|
const ImageToImageTabParameters = () => {
|
||||||
return (
|
return (
|
||||||
@ -16,6 +17,7 @@ const ImageToImageTabParameters = () => {
|
|||||||
<ParamNegativeConditioning />
|
<ParamNegativeConditioning />
|
||||||
<ProcessButtons />
|
<ProcessButtons />
|
||||||
<ImageToImageTabCoreParameters />
|
<ImageToImageTabCoreParameters />
|
||||||
|
<ParamDynamicPromptsCollapse />
|
||||||
<ParamControlNetCollapse />
|
<ParamControlNetCollapse />
|
||||||
<ParamVariationCollapse />
|
<ParamVariationCollapse />
|
||||||
<ParamNoiseCollapse />
|
<ParamNoiseCollapse />
|
||||||
|
@ -9,6 +9,7 @@ import ParamHiresCollapse from 'features/parameters/components/Parameters/Hires/
|
|||||||
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
||||||
import TextToImageTabCoreParameters from './TextToImageTabCoreParameters';
|
import TextToImageTabCoreParameters from './TextToImageTabCoreParameters';
|
||||||
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
||||||
|
import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse';
|
||||||
|
|
||||||
const TextToImageTabParameters = () => {
|
const TextToImageTabParameters = () => {
|
||||||
return (
|
return (
|
||||||
@ -17,6 +18,7 @@ const TextToImageTabParameters = () => {
|
|||||||
<ParamNegativeConditioning />
|
<ParamNegativeConditioning />
|
||||||
<ProcessButtons />
|
<ProcessButtons />
|
||||||
<TextToImageTabCoreParameters />
|
<TextToImageTabCoreParameters />
|
||||||
|
<ParamDynamicPromptsCollapse />
|
||||||
<ParamControlNetCollapse />
|
<ParamControlNetCollapse />
|
||||||
<ParamVariationCollapse />
|
<ParamVariationCollapse />
|
||||||
<ParamNoiseCollapse />
|
<ParamNoiseCollapse />
|
||||||
|
@ -8,6 +8,7 @@ import { memo } from 'react';
|
|||||||
import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning';
|
import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning';
|
||||||
import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning';
|
import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning';
|
||||||
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
||||||
|
import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse';
|
||||||
|
|
||||||
const UnifiedCanvasParameters = () => {
|
const UnifiedCanvasParameters = () => {
|
||||||
return (
|
return (
|
||||||
@ -16,6 +17,7 @@ const UnifiedCanvasParameters = () => {
|
|||||||
<ParamNegativeConditioning />
|
<ParamNegativeConditioning />
|
||||||
<ProcessButtons />
|
<ProcessButtons />
|
||||||
<UnifiedCanvasCoreParameters />
|
<UnifiedCanvasCoreParameters />
|
||||||
|
<ParamDynamicPromptsCollapse />
|
||||||
<ParamControlNetCollapse />
|
<ParamControlNetCollapse />
|
||||||
<ParamVariationCollapse />
|
<ParamVariationCollapse />
|
||||||
<ParamSymmetryCollapse />
|
<ParamSymmetryCollapse />
|
||||||
|
@ -15,6 +15,7 @@ export const imagesApi = api.injectEndpoints({
|
|||||||
}
|
}
|
||||||
return tags;
|
return tags;
|
||||||
},
|
},
|
||||||
|
keepUnusedDataFor: 86400, // 24 hours
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -47,6 +47,15 @@ export type InpaintInvocation = Invocation<'InpaintInvocation'>;
|
|||||||
export type ImageResizeInvocation = Invocation<'ImageResizeInvocation'>;
|
export type ImageResizeInvocation = Invocation<'ImageResizeInvocation'>;
|
||||||
export type RandomIntInvocation = Invocation<'RandomIntInvocation'>;
|
export type RandomIntInvocation = Invocation<'RandomIntInvocation'>;
|
||||||
export type CompelInvocation = Invocation<'CompelInvocation'>;
|
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
|
// ControlNet Nodes
|
||||||
export type ControlNetInvocation = Invocation<'ControlNetInvocation'>;
|
export type ControlNetInvocation = Invocation<'ControlNetInvocation'>;
|
||||||
|
Loading…
Reference in New Issue
Block a user