mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
Merge remote-tracking branch 'origin/main' into feat/upscale4x
This commit is contained in:
@@ -211,7 +211,7 @@ def invoke_api():
|
||||
port = find_port(app_config.port)
|
||||
if port != app_config.port:
|
||||
logger.warn(f"Port {app_config.port} in use, using port {port}")
|
||||
|
||||
|
||||
# Start our own event loop for eventing usage
|
||||
loop = asyncio.new_event_loop()
|
||||
config = uvicorn.Config(
|
||||
@@ -229,7 +229,7 @@ def invoke_api():
|
||||
l.handlers.clear()
|
||||
for ch in logger.handlers:
|
||||
l.addHandler(ch)
|
||||
|
||||
|
||||
loop.run_until_complete(server.serve())
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ class LoRAModel(ModelBase):
|
||||
|
||||
@classproperty
|
||||
def save_to_config(cls) -> bool:
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def detect_format(cls, path: str):
|
||||
|
||||
@@ -340,6 +340,7 @@
|
||||
"allModels": "All Models",
|
||||
"checkpointModels": "Checkpoints",
|
||||
"diffusersModels": "Diffusers",
|
||||
"loraModels": "LoRAs",
|
||||
"safetensorModels": "SafeTensors",
|
||||
"modelAdded": "Model Added",
|
||||
"modelUpdated": "Model Updated",
|
||||
|
||||
@@ -148,7 +148,7 @@ const ParamPositiveConditioning = () => {
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: shouldPinParametersPanel ? 6 : 0,
|
||||
top: shouldPinParametersPanel ? 5 : 0,
|
||||
insetInlineEnd: 0,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning';
|
||||
import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning';
|
||||
|
||||
export default function ParamPromptArea() {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<ParamPositiveConditioning />
|
||||
<ParamNegativeConditioning />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { components } from 'services/api/schema';
|
||||
|
||||
export const MODEL_TYPE_MAP = {
|
||||
'sd-1': 'Stable Diffusion 1.x',
|
||||
'sd-2': 'Stable Diffusion 2.x',
|
||||
@@ -5,6 +7,13 @@ export const MODEL_TYPE_MAP = {
|
||||
'sdxl-refiner': 'Stable Diffusion XL Refiner',
|
||||
};
|
||||
|
||||
export const MODEL_TYPE_SHORT_MAP = {
|
||||
'sd-1': 'SD1',
|
||||
'sd-2': 'SD2',
|
||||
sdxl: 'SDXL',
|
||||
'sdxl-refiner': 'SDXLR',
|
||||
};
|
||||
|
||||
export const clipSkipMap = {
|
||||
'sd-1': {
|
||||
maxClip: 12,
|
||||
@@ -23,3 +32,12 @@ export const clipSkipMap = {
|
||||
markers: [0, 1, 2, 3, 5, 10, 15, 20, 24],
|
||||
},
|
||||
};
|
||||
|
||||
type LoRAModelFormatMap = {
|
||||
[key in components['schemas']['LoRAModelFormat']]: string;
|
||||
};
|
||||
|
||||
export const LORA_MODEL_FORMAT_MAP: LoRAModelFormatMap = {
|
||||
lycoris: 'LyCORIS',
|
||||
diffusers: 'Diffusers',
|
||||
};
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { FaLink } from 'react-icons/fa';
|
||||
import { setShouldConcatSDXLStylePrompt } from '../store/sdxlSlice';
|
||||
|
||||
export default function ParamSDXLConcatButton() {
|
||||
const shouldConcatSDXLStylePrompt = useAppSelector(
|
||||
(state: RootState) => state.sdxl.shouldConcatSDXLStylePrompt
|
||||
);
|
||||
|
||||
const shouldPinParametersPanel = useAppSelector(
|
||||
(state: RootState) => state.ui.shouldPinParametersPanel
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleShouldConcatPromptChange = () => {
|
||||
dispatch(setShouldConcatSDXLStylePrompt(!shouldConcatSDXLStylePrompt));
|
||||
};
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Concat"
|
||||
tooltip="Concatenates Basic Prompt with Style (Recommended)"
|
||||
variant="outline"
|
||||
isChecked={shouldConcatSDXLStylePrompt}
|
||||
onClick={handleShouldConcatPromptChange}
|
||||
icon={<FaLink />}
|
||||
size="xs"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
insetInlineEnd: 1,
|
||||
top: shouldPinParametersPanel ? 12 : 20,
|
||||
border: 'none',
|
||||
color: shouldConcatSDXLStylePrompt ? 'accent.500' : 'base.500',
|
||||
_hover: {
|
||||
bg: 'none',
|
||||
},
|
||||
}}
|
||||
></IAIIconButton>
|
||||
);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { setShouldConcatSDXLStylePrompt } from '../store/sdxlSlice';
|
||||
|
||||
export default function ParamSDXLConcatPrompt() {
|
||||
const shouldConcatSDXLStylePrompt = useAppSelector(
|
||||
(state: RootState) => state.sdxl.shouldConcatSDXLStylePrompt
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleShouldConcatPromptChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(setShouldConcatSDXLStylePrompt(e.target.checked));
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
px: 2,
|
||||
}}
|
||||
>
|
||||
<IAISwitch
|
||||
label="Concat Style Prompt"
|
||||
tooltip="Concatenates Basic Prompt with Style (Recommended)"
|
||||
isChecked={shouldConcatSDXLStylePrompt}
|
||||
onChange={handleShouldConcatPromptChange}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning';
|
||||
import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import ParamSDXLConcatButton from './ParamSDXLConcatButton';
|
||||
import ParamSDXLNegativeStyleConditioning from './ParamSDXLNegativeStyleConditioning';
|
||||
import ParamSDXLPositiveStyleConditioning from './ParamSDXLPositiveStyleConditioning';
|
||||
import SDXLConcatLink from './SDXLConcatLink';
|
||||
|
||||
export default function ParamSDXLPromptArea() {
|
||||
const shouldPinParametersPanel = useAppSelector(
|
||||
(state: RootState) => state.ui.shouldPinParametersPanel
|
||||
);
|
||||
|
||||
const shouldConcatSDXLStylePrompt = useAppSelector(
|
||||
(state: RootState) => state.sdxl.shouldConcatSDXLStylePrompt
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<AnimatePresence>
|
||||
{shouldConcatSDXLStylePrompt && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
w: 'full',
|
||||
top: shouldPinParametersPanel ? '119px' : '175px',
|
||||
}}
|
||||
>
|
||||
<SDXLConcatLink />
|
||||
</Box>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<AnimatePresence>
|
||||
{shouldConcatSDXLStylePrompt && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
w: 'full',
|
||||
top: shouldPinParametersPanel ? '263px' : '319px',
|
||||
}}
|
||||
>
|
||||
<SDXLConcatLink />
|
||||
</Box>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<ParamPositiveConditioning />
|
||||
<ParamSDXLConcatButton />
|
||||
<ParamSDXLPositiveStyleConditioning />
|
||||
<ParamNegativeConditioning />
|
||||
<ParamSDXLNegativeStyleConditioning />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { CSSObject } from '@emotion/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { FaLink } from 'react-icons/fa';
|
||||
|
||||
const sharedConcatLinkStyle: CSSObject = {
|
||||
position: 'absolute',
|
||||
bg: 'none',
|
||||
w: 'full',
|
||||
minH: 2,
|
||||
borderRadius: 0,
|
||||
borderLeft: 'none',
|
||||
borderRight: 'none',
|
||||
zIndex: 2,
|
||||
maskImage:
|
||||
'radial-gradient(circle at center, black, black 65%, black 30%, black 15%, transparent)',
|
||||
};
|
||||
|
||||
export default function SDXLConcatLink() {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
h: 0.5,
|
||||
placeContent: 'center',
|
||||
gap: 2,
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
as={motion.div}
|
||||
initial={{
|
||||
scaleX: 0,
|
||||
borderWidth: 0,
|
||||
display: 'none',
|
||||
}}
|
||||
animate={{
|
||||
display: ['block', 'block', 'block', 'none'],
|
||||
scaleX: [0, 0.25, 0.5, 1],
|
||||
borderWidth: [0, 3, 3, 0],
|
||||
transition: { duration: 0.37, times: [0, 0.25, 0.5, 1] },
|
||||
}}
|
||||
sx={{
|
||||
top: '1px',
|
||||
borderTop: 'none',
|
||||
borderColor: 'base.400',
|
||||
zIndex: 2,
|
||||
...sharedConcatLinkStyle,
|
||||
_dark: {
|
||||
borderColor: 'accent.500',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
as={motion.div}
|
||||
initial={{
|
||||
opacity: 0,
|
||||
scale: 0,
|
||||
}}
|
||||
animate={{
|
||||
opacity: [0, 1, 1, 1],
|
||||
scale: [0, 0.75, 1.5, 1],
|
||||
transition: { duration: 0.42, times: [0, 0.25, 0.5, 1] },
|
||||
}}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
scale: 0,
|
||||
}}
|
||||
sx={{
|
||||
zIndex: 3,
|
||||
position: 'absolute',
|
||||
left: '48%',
|
||||
top: '3px',
|
||||
p: 1,
|
||||
borderRadius: 4,
|
||||
bg: 'accent.400',
|
||||
color: 'base.50',
|
||||
_dark: {
|
||||
bg: 'accent.500',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<FaLink size={12} />
|
||||
</Box>
|
||||
<Box
|
||||
as={motion.div}
|
||||
initial={{
|
||||
scaleX: 0,
|
||||
borderWidth: 0,
|
||||
display: 'none',
|
||||
}}
|
||||
animate={{
|
||||
display: ['block', 'block', 'block', 'none'],
|
||||
scaleX: [0, 0.25, 0.5, 1],
|
||||
borderWidth: [0, 3, 3, 0],
|
||||
transition: { duration: 0.37, times: [0, 0.25, 0.5, 1] },
|
||||
}}
|
||||
sx={{
|
||||
top: '17px',
|
||||
borderBottom: 'none',
|
||||
borderColor: 'base.400',
|
||||
...sharedConcatLinkStyle,
|
||||
_dark: {
|
||||
borderColor: 'accent.500',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -1,34 +1,14 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse';
|
||||
import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning';
|
||||
import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning';
|
||||
import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/ParamNoiseCollapse';
|
||||
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
|
||||
import ParamSDXLConcatPrompt from './ParamSDXLConcatPrompt';
|
||||
import ParamSDXLNegativeStyleConditioning from './ParamSDXLNegativeStyleConditioning';
|
||||
import ParamSDXLPositiveStyleConditioning from './ParamSDXLPositiveStyleConditioning';
|
||||
import ParamSDXLPromptArea from './ParamSDXLPromptArea';
|
||||
import ParamSDXLRefinerCollapse from './ParamSDXLRefinerCollapse';
|
||||
import SDXLImageToImageTabCoreParameters from './SDXLImageToImageTabCoreParameters';
|
||||
|
||||
const SDXLImageToImageTabParameters = () => {
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
p: 2,
|
||||
borderRadius: 4,
|
||||
bg: 'base.100',
|
||||
_dark: { bg: 'base.850' },
|
||||
}}
|
||||
>
|
||||
<ParamPositiveConditioning />
|
||||
<ParamSDXLPositiveStyleConditioning />
|
||||
<ParamNegativeConditioning />
|
||||
<ParamSDXLNegativeStyleConditioning />
|
||||
<ParamSDXLConcatPrompt />
|
||||
</Flex>
|
||||
<ParamSDXLPromptArea />
|
||||
<ProcessButtons />
|
||||
<SDXLImageToImageTabCoreParameters />
|
||||
<ParamSDXLRefinerCollapse />
|
||||
|
||||
@@ -1,34 +1,14 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse';
|
||||
import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning';
|
||||
import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning';
|
||||
import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/ParamNoiseCollapse';
|
||||
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
|
||||
import TextToImageTabCoreParameters from 'features/ui/components/tabs/TextToImage/TextToImageTabCoreParameters';
|
||||
import ParamSDXLConcatPrompt from './ParamSDXLConcatPrompt';
|
||||
import ParamSDXLNegativeStyleConditioning from './ParamSDXLNegativeStyleConditioning';
|
||||
import ParamSDXLPositiveStyleConditioning from './ParamSDXLPositiveStyleConditioning';
|
||||
import ParamSDXLPromptArea from './ParamSDXLPromptArea';
|
||||
import ParamSDXLRefinerCollapse from './ParamSDXLRefinerCollapse';
|
||||
|
||||
const SDXLTextToImageTabParameters = () => {
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
p: 2,
|
||||
borderRadius: 4,
|
||||
bg: 'base.100',
|
||||
_dark: { bg: 'base.850' },
|
||||
}}
|
||||
>
|
||||
<ParamPositiveConditioning />
|
||||
<ParamSDXLPositiveStyleConditioning />
|
||||
<ParamNegativeConditioning />
|
||||
<ParamSDXLNegativeStyleConditioning />
|
||||
<ParamSDXLConcatPrompt />
|
||||
</Flex>
|
||||
<ParamSDXLPromptArea />
|
||||
<ProcessButtons />
|
||||
<TextToImageTabCoreParameters />
|
||||
<ParamSDXLRefinerCollapse />
|
||||
|
||||
@@ -2,20 +2,18 @@ import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/Para
|
||||
import ParamLoraCollapse from 'features/lora/components/ParamLoraCollapse';
|
||||
import ParamAdvancedCollapse from 'features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse';
|
||||
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
||||
import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning';
|
||||
import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning';
|
||||
import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/ParamNoiseCollapse';
|
||||
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
||||
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
||||
// import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
||||
import ParamPromptArea from 'features/parameters/components/Parameters/Prompt/ParamPromptArea';
|
||||
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
|
||||
import ImageToImageTabCoreParameters from './ImageToImageTabCoreParameters';
|
||||
|
||||
const ImageToImageTabParameters = () => {
|
||||
return (
|
||||
<>
|
||||
<ParamPositiveConditioning />
|
||||
<ParamNegativeConditioning />
|
||||
<ParamPromptArea />
|
||||
<ProcessButtons />
|
||||
<ImageToImageTabCoreParameters />
|
||||
<ParamControlNetCollapse />
|
||||
|
||||
@@ -3,20 +3,31 @@ import { Flex, Text } from '@chakra-ui/react';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
MainModelConfigEntity,
|
||||
DiffusersModelConfigEntity,
|
||||
LoRAModelConfigEntity,
|
||||
useGetMainModelsQuery,
|
||||
useGetLoRAModelsQuery,
|
||||
} from 'services/api/endpoints/models';
|
||||
import CheckpointModelEdit from './ModelManagerPanel/CheckpointModelEdit';
|
||||
import DiffusersModelEdit from './ModelManagerPanel/DiffusersModelEdit';
|
||||
import LoRAModelEdit from './ModelManagerPanel/LoRAModelEdit';
|
||||
import ModelList from './ModelManagerPanel/ModelList';
|
||||
import { ALL_BASE_MODELS } from 'services/api/constants';
|
||||
|
||||
export default function ModelManagerPanel() {
|
||||
const [selectedModelId, setSelectedModelId] = useState<string>();
|
||||
const { model } = useGetMainModelsQuery(ALL_BASE_MODELS, {
|
||||
const { mainModel } = useGetMainModelsQuery(ALL_BASE_MODELS, {
|
||||
selectFromResult: ({ data }) => ({
|
||||
model: selectedModelId ? data?.entities[selectedModelId] : undefined,
|
||||
mainModel: selectedModelId ? data?.entities[selectedModelId] : undefined,
|
||||
}),
|
||||
});
|
||||
const { loraModel } = useGetLoRAModelsQuery(undefined, {
|
||||
selectFromResult: ({ data }) => ({
|
||||
loraModel: selectedModelId ? data?.entities[selectedModelId] : undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
const model = mainModel ? mainModel : loraModel;
|
||||
|
||||
return (
|
||||
<Flex sx={{ gap: 8, w: 'full', h: 'full' }}>
|
||||
@@ -30,7 +41,7 @@ export default function ModelManagerPanel() {
|
||||
}
|
||||
|
||||
type ModelEditProps = {
|
||||
model: MainModelConfigEntity | undefined;
|
||||
model: MainModelConfigEntity | LoRAModelConfigEntity | undefined;
|
||||
};
|
||||
|
||||
const ModelEdit = (props: ModelEditProps) => {
|
||||
@@ -41,7 +52,16 @@ const ModelEdit = (props: ModelEditProps) => {
|
||||
}
|
||||
|
||||
if (model?.model_format === 'diffusers') {
|
||||
return <DiffusersModelEdit key={model.id} model={model} />;
|
||||
return (
|
||||
<DiffusersModelEdit
|
||||
key={model.id}
|
||||
model={model as DiffusersModelConfigEntity}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (model?.model_type === 'lora') {
|
||||
return <LoRAModelEdit key={model.id} model={model} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import { Divider, Flex, Text } from '@chakra-ui/react';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { makeToast } from 'features/system/util/makeToast';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIMantineTextInput from 'common/components/IAIMantineInput';
|
||||
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
LORA_MODEL_FORMAT_MAP,
|
||||
MODEL_TYPE_MAP,
|
||||
} from 'features/parameters/types/constants';
|
||||
import {
|
||||
LoRAModelConfigEntity,
|
||||
useUpdateLoRAModelsMutation,
|
||||
} from 'services/api/endpoints/models';
|
||||
import { LoRAModelConfig } from 'services/api/types';
|
||||
import BaseModelSelect from '../shared/BaseModelSelect';
|
||||
|
||||
type LoRAModelEditProps = {
|
||||
model: LoRAModelConfigEntity;
|
||||
};
|
||||
|
||||
export default function LoRAModelEdit(props: LoRAModelEditProps) {
|
||||
const isBusy = useAppSelector(selectIsBusy);
|
||||
|
||||
const { model } = props;
|
||||
|
||||
const [updateLoRAModel, { isLoading }] = useUpdateLoRAModelsMutation();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const loraEditForm = useForm<LoRAModelConfig>({
|
||||
initialValues: {
|
||||
model_name: model.model_name ? model.model_name : '',
|
||||
base_model: model.base_model,
|
||||
model_type: 'lora',
|
||||
path: model.path ? model.path : '',
|
||||
description: model.description ? model.description : '',
|
||||
model_format: model.model_format,
|
||||
},
|
||||
validate: {
|
||||
path: (value) =>
|
||||
value.trim().length === 0 ? 'Must provide a path' : null,
|
||||
},
|
||||
});
|
||||
|
||||
const editModelFormSubmitHandler = useCallback(
|
||||
(values: LoRAModelConfig) => {
|
||||
const responseBody = {
|
||||
base_model: model.base_model,
|
||||
model_name: model.model_name,
|
||||
body: values,
|
||||
};
|
||||
|
||||
updateLoRAModel(responseBody)
|
||||
.unwrap()
|
||||
.then((payload) => {
|
||||
loraEditForm.setValues(payload as LoRAModelConfig);
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: t('modelManager.modelUpdated'),
|
||||
status: 'success',
|
||||
})
|
||||
)
|
||||
);
|
||||
})
|
||||
.catch((_) => {
|
||||
loraEditForm.reset();
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: t('modelManager.modelUpdateFailed'),
|
||||
status: 'error',
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
},
|
||||
[
|
||||
dispatch,
|
||||
loraEditForm,
|
||||
model.base_model,
|
||||
model.model_name,
|
||||
t,
|
||||
updateLoRAModel,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex flexDirection="column" rowGap={4} width="100%">
|
||||
<Flex flexDirection="column">
|
||||
<Text fontSize="lg" fontWeight="bold">
|
||||
{model.model_name}
|
||||
</Text>
|
||||
<Text fontSize="sm" color="base.400">
|
||||
{MODEL_TYPE_MAP[model.base_model]} Model ⋅{' '}
|
||||
{LORA_MODEL_FORMAT_MAP[model.model_format]} format
|
||||
</Text>
|
||||
</Flex>
|
||||
<Divider />
|
||||
|
||||
<form
|
||||
onSubmit={loraEditForm.onSubmit((values) =>
|
||||
editModelFormSubmitHandler(values)
|
||||
)}
|
||||
>
|
||||
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
||||
<IAIMantineTextInput
|
||||
label={t('modelManager.name')}
|
||||
{...loraEditForm.getInputProps('model_name')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
label={t('modelManager.description')}
|
||||
{...loraEditForm.getInputProps('description')}
|
||||
/>
|
||||
<BaseModelSelect {...loraEditForm.getInputProps('base_model')} />
|
||||
<IAIMantineTextInput
|
||||
label={t('modelManager.modelLocation')}
|
||||
{...loraEditForm.getInputProps('path')}
|
||||
/>
|
||||
<IAIButton
|
||||
type="submit"
|
||||
isDisabled={isBusy || isLoading}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
{t('modelManager.updateModel')}
|
||||
</IAIButton>
|
||||
</Flex>
|
||||
</form>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
MainModelConfigEntity,
|
||||
useGetMainModelsQuery,
|
||||
useGetLoRAModelsQuery,
|
||||
LoRAModelConfigEntity,
|
||||
} from 'services/api/endpoints/models';
|
||||
import ModelListItem from './ModelListItem';
|
||||
import { ALL_BASE_MODELS } from 'services/api/constants';
|
||||
@@ -20,22 +22,42 @@ type ModelListProps = {
|
||||
|
||||
type ModelFormat = 'images' | 'checkpoint' | 'diffusers';
|
||||
|
||||
type ModelType = 'main' | 'lora';
|
||||
|
||||
type CombinedModelFormat = ModelFormat | 'lora';
|
||||
|
||||
const ModelList = (props: ModelListProps) => {
|
||||
const { selectedModelId, setSelectedModelId } = props;
|
||||
const { t } = useTranslation();
|
||||
const [nameFilter, setNameFilter] = useState<string>('');
|
||||
const [modelFormatFilter, setModelFormatFilter] =
|
||||
useState<ModelFormat>('images');
|
||||
useState<CombinedModelFormat>('images');
|
||||
|
||||
const { filteredDiffusersModels } = useGetMainModelsQuery(ALL_BASE_MODELS, {
|
||||
selectFromResult: ({ data }) => ({
|
||||
filteredDiffusersModels: modelsFilter(data, 'diffusers', nameFilter),
|
||||
filteredDiffusersModels: modelsFilter(
|
||||
data,
|
||||
'main',
|
||||
'diffusers',
|
||||
nameFilter
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
const { filteredCheckpointModels } = useGetMainModelsQuery(ALL_BASE_MODELS, {
|
||||
selectFromResult: ({ data }) => ({
|
||||
filteredCheckpointModels: modelsFilter(data, 'checkpoint', nameFilter),
|
||||
filteredCheckpointModels: modelsFilter(
|
||||
data,
|
||||
'main',
|
||||
'checkpoint',
|
||||
nameFilter
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
const { filteredLoraModels } = useGetLoRAModelsQuery(undefined, {
|
||||
selectFromResult: ({ data }) => ({
|
||||
filteredLoraModels: modelsFilter(data, 'lora', undefined, nameFilter),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -68,6 +90,13 @@ const ModelList = (props: ModelListProps) => {
|
||||
>
|
||||
{t('modelManager.checkpointModels')}
|
||||
</IAIButton>
|
||||
<IAIButton
|
||||
size="sm"
|
||||
onClick={() => setModelFormatFilter('lora')}
|
||||
isChecked={modelFormatFilter === 'lora'}
|
||||
>
|
||||
{t('modelManager.loraModels')}
|
||||
</IAIButton>
|
||||
</ButtonGroup>
|
||||
|
||||
<IAIInput
|
||||
@@ -118,6 +147,24 @@ const ModelList = (props: ModelListProps) => {
|
||||
</Flex>
|
||||
</StyledModelContainer>
|
||||
)}
|
||||
{['images', 'lora'].includes(modelFormatFilter) &&
|
||||
filteredLoraModels.length > 0 && (
|
||||
<StyledModelContainer>
|
||||
<Flex sx={{ gap: 2, flexDir: 'column' }}>
|
||||
<Text variant="subtext" fontSize="sm">
|
||||
LoRAs
|
||||
</Text>
|
||||
{filteredLoraModels.map((model) => (
|
||||
<ModelListItem
|
||||
key={model.id}
|
||||
model={model}
|
||||
isSelected={selectedModelId === model.id}
|
||||
setSelectedModelId={setSelectedModelId}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
</StyledModelContainer>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
@@ -126,12 +173,13 @@ const ModelList = (props: ModelListProps) => {
|
||||
|
||||
export default ModelList;
|
||||
|
||||
const modelsFilter = (
|
||||
data: EntityState<MainModelConfigEntity> | undefined,
|
||||
model_format: ModelFormat,
|
||||
const modelsFilter = <T extends MainModelConfigEntity | LoRAModelConfigEntity>(
|
||||
data: EntityState<T> | undefined,
|
||||
model_type: ModelType,
|
||||
model_format: ModelFormat | undefined,
|
||||
nameFilter: string
|
||||
) => {
|
||||
const filteredModels: MainModelConfigEntity[] = [];
|
||||
const filteredModels: T[] = [];
|
||||
forEach(data?.entities, (model) => {
|
||||
if (!model) {
|
||||
return;
|
||||
@@ -141,9 +189,11 @@ const modelsFilter = (
|
||||
.toLowerCase()
|
||||
.includes(nameFilter.toLowerCase());
|
||||
|
||||
const matchesFormat = model.model_format === model_format;
|
||||
const matchesFormat =
|
||||
model_format === undefined || model.model_format === model_format;
|
||||
const matchesType = model.model_type === model_type;
|
||||
|
||||
if (matchesFilter && matchesFormat) {
|
||||
if (matchesFilter && matchesFormat && matchesType) {
|
||||
filteredModels.push(model);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,29 +9,26 @@ import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MODEL_TYPE_SHORT_MAP } from 'features/parameters/types/constants';
|
||||
import {
|
||||
MainModelConfigEntity,
|
||||
LoRAModelConfigEntity,
|
||||
useDeleteMainModelsMutation,
|
||||
useDeleteLoRAModelsMutation,
|
||||
} from 'services/api/endpoints/models';
|
||||
|
||||
type ModelListItemProps = {
|
||||
model: MainModelConfigEntity;
|
||||
model: MainModelConfigEntity | LoRAModelConfigEntity;
|
||||
isSelected: boolean;
|
||||
setSelectedModelId: (v: string | undefined) => void;
|
||||
};
|
||||
|
||||
const modelBaseTypeMap = {
|
||||
'sd-1': 'SD1',
|
||||
'sd-2': 'SD2',
|
||||
sdxl: 'SDXL',
|
||||
'sdxl-refiner': 'SDXLR',
|
||||
};
|
||||
|
||||
export default function ModelListItem(props: ModelListItemProps) {
|
||||
const isBusy = useAppSelector(selectIsBusy);
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const [deleteMainModel] = useDeleteMainModelsMutation();
|
||||
const [deleteLoRAModel] = useDeleteLoRAModelsMutation();
|
||||
|
||||
const { model, isSelected, setSelectedModelId } = props;
|
||||
|
||||
@@ -40,7 +37,10 @@ export default function ModelListItem(props: ModelListItemProps) {
|
||||
}, [model.id, setSelectedModelId]);
|
||||
|
||||
const handleModelDelete = useCallback(() => {
|
||||
deleteMainModel(model)
|
||||
const method = { main: deleteMainModel, lora: deleteLoRAModel }[
|
||||
model.model_type
|
||||
];
|
||||
method(model)
|
||||
.unwrap()
|
||||
.then((_) => {
|
||||
dispatch(
|
||||
@@ -60,14 +60,21 @@ export default function ModelListItem(props: ModelListItemProps) {
|
||||
title: `${t('modelManager.modelDeleteFailed')}: ${
|
||||
model.model_name
|
||||
}`,
|
||||
status: 'success',
|
||||
status: 'error',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
setSelectedModelId(undefined);
|
||||
}, [deleteMainModel, model, setSelectedModelId, dispatch, t]);
|
||||
}, [
|
||||
deleteMainModel,
|
||||
deleteLoRAModel,
|
||||
model,
|
||||
setSelectedModelId,
|
||||
dispatch,
|
||||
t,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Flex sx={{ gap: 2, alignItems: 'center', w: 'full' }}>
|
||||
@@ -100,8 +107,8 @@ export default function ModelListItem(props: ModelListItemProps) {
|
||||
<Flex gap={4} alignItems="center">
|
||||
<Badge minWidth={14} p={0.5} fontSize="sm" variant="solid">
|
||||
{
|
||||
modelBaseTypeMap[
|
||||
model.base_model as keyof typeof modelBaseTypeMap
|
||||
MODEL_TYPE_SHORT_MAP[
|
||||
model.base_model as keyof typeof MODEL_TYPE_SHORT_MAP
|
||||
]
|
||||
}
|
||||
</Badge>
|
||||
|
||||
@@ -2,20 +2,18 @@ import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/Para
|
||||
import ParamLoraCollapse from 'features/lora/components/ParamLoraCollapse';
|
||||
import ParamAdvancedCollapse from 'features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse';
|
||||
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
||||
import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning';
|
||||
import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning';
|
||||
import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/ParamNoiseCollapse';
|
||||
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
||||
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
||||
// import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
||||
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
|
||||
import ParamPromptArea from '../../../../parameters/components/Parameters/Prompt/ParamPromptArea';
|
||||
import TextToImageTabCoreParameters from './TextToImageTabCoreParameters';
|
||||
|
||||
const TextToImageTabParameters = () => {
|
||||
return (
|
||||
<>
|
||||
<ParamPositiveConditioning />
|
||||
<ParamNegativeConditioning />
|
||||
<ParamPromptArea />
|
||||
<ProcessButtons />
|
||||
<TextToImageTabCoreParameters />
|
||||
<ParamControlNetCollapse />
|
||||
|
||||
@@ -4,18 +4,16 @@ import ParamAdvancedCollapse from 'features/parameters/components/Parameters/Adv
|
||||
import ParamInfillAndScalingCollapse from 'features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamInfillAndScalingCollapse';
|
||||
import ParamSeamCorrectionCollapse from 'features/parameters/components/Parameters/Canvas/SeamCorrection/ParamSeamCorrectionCollapse';
|
||||
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
||||
import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning';
|
||||
import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning';
|
||||
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
||||
// import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
||||
import ParamPromptArea from 'features/parameters/components/Parameters/Prompt/ParamPromptArea';
|
||||
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
|
||||
import UnifiedCanvasCoreParameters from './UnifiedCanvasCoreParameters';
|
||||
|
||||
const UnifiedCanvasParameters = () => {
|
||||
return (
|
||||
<>
|
||||
<ParamPositiveConditioning />
|
||||
<ParamNegativeConditioning />
|
||||
<ParamPromptArea />
|
||||
<ProcessButtons />
|
||||
<UnifiedCanvasCoreParameters />
|
||||
<ParamControlNetCollapse />
|
||||
|
||||
@@ -52,9 +52,17 @@ type UpdateMainModelArg = {
|
||||
body: MainModelConfig;
|
||||
};
|
||||
|
||||
type UpdateLoRAModelArg = {
|
||||
base_model: BaseModelType;
|
||||
model_name: string;
|
||||
body: LoRAModelConfig;
|
||||
};
|
||||
|
||||
type UpdateMainModelResponse =
|
||||
paths['/api/v1/models/{base_model}/{model_type}/{model_name}']['patch']['responses']['200']['content']['application/json'];
|
||||
|
||||
type UpdateLoRAModelResponse = UpdateMainModelResponse;
|
||||
|
||||
type DeleteMainModelArg = {
|
||||
base_model: BaseModelType;
|
||||
model_name: string;
|
||||
@@ -62,6 +70,10 @@ type DeleteMainModelArg = {
|
||||
|
||||
type DeleteMainModelResponse = void;
|
||||
|
||||
type DeleteLoRAModelArg = DeleteMainModelArg;
|
||||
|
||||
type DeleteLoRAModelResponse = void;
|
||||
|
||||
type ConvertMainModelArg = {
|
||||
base_model: BaseModelType;
|
||||
model_name: string;
|
||||
@@ -320,6 +332,31 @@ export const modelsApi = api.injectEndpoints({
|
||||
);
|
||||
},
|
||||
}),
|
||||
updateLoRAModels: build.mutation<
|
||||
UpdateLoRAModelResponse,
|
||||
UpdateLoRAModelArg
|
||||
>({
|
||||
query: ({ base_model, model_name, body }) => {
|
||||
return {
|
||||
url: `models/${base_model}/lora/${model_name}`,
|
||||
method: 'PATCH',
|
||||
body: body,
|
||||
};
|
||||
},
|
||||
invalidatesTags: [{ type: 'LoRAModel', id: LIST_TAG }],
|
||||
}),
|
||||
deleteLoRAModels: build.mutation<
|
||||
DeleteLoRAModelResponse,
|
||||
DeleteLoRAModelArg
|
||||
>({
|
||||
query: ({ base_model, model_name }) => {
|
||||
return {
|
||||
url: `models/${base_model}/lora/${model_name}`,
|
||||
method: 'DELETE',
|
||||
};
|
||||
},
|
||||
invalidatesTags: [{ type: 'LoRAModel', id: LIST_TAG }],
|
||||
}),
|
||||
getControlNetModels: build.query<
|
||||
EntityState<ControlNetModelConfigEntity>,
|
||||
void
|
||||
@@ -467,6 +504,8 @@ export const {
|
||||
useAddMainModelsMutation,
|
||||
useConvertMainModelsMutation,
|
||||
useMergeMainModelsMutation,
|
||||
useDeleteLoRAModelsMutation,
|
||||
useUpdateLoRAModelsMutation,
|
||||
useSyncModelsMutation,
|
||||
useGetModelsInFolderQuery,
|
||||
useGetCheckpointConfigsQuery,
|
||||
|
||||
@@ -13,6 +13,15 @@ const invokeAI = defineStyle((props) => ({
|
||||
var(--invokeai-colors-base-200) 70%,
|
||||
var(--invokeai-colors-base-200) 100%)`,
|
||||
},
|
||||
_disabled: {
|
||||
'::-webkit-resizer': {
|
||||
backgroundImage: `linear-gradient(135deg,
|
||||
var(--invokeai-colors-base-50) 0%,
|
||||
var(--invokeai-colors-base-50) 70%,
|
||||
var(--invokeai-colors-base-200) 70%,
|
||||
var(--invokeai-colors-base-200) 100%)`,
|
||||
},
|
||||
},
|
||||
_dark: {
|
||||
'::-webkit-resizer': {
|
||||
backgroundImage: `linear-gradient(135deg,
|
||||
@@ -21,6 +30,15 @@ const invokeAI = defineStyle((props) => ({
|
||||
var(--invokeai-colors-base-800) 70%,
|
||||
var(--invokeai-colors-base-800) 100%)`,
|
||||
},
|
||||
_disabled: {
|
||||
'::-webkit-resizer': {
|
||||
backgroundImage: `linear-gradient(135deg,
|
||||
var(--invokeai-colors-base-900) 0%,
|
||||
var(--invokeai-colors-base-900) 70%,
|
||||
var(--invokeai-colors-base-800) 70%,
|
||||
var(--invokeai-colors-base-800) 100%)`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user