mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into psychedelicious-patch-1
This commit is contained in:
commit
04c0700762
@ -63,20 +63,35 @@ async def update_model(
|
||||
) -> UpdateModelResponse:
|
||||
""" Update model contents with a new config. If the model name or base fields are changed, then the model is renamed. """
|
||||
logger = ApiDependencies.invoker.services.logger
|
||||
|
||||
|
||||
try:
|
||||
previous_info = ApiDependencies.invoker.services.model_manager.list_model(
|
||||
model_name=model_name,
|
||||
base_model=base_model,
|
||||
model_type=model_type,
|
||||
)
|
||||
|
||||
# rename operation requested
|
||||
if info.model_name != model_name or info.base_model != base_model:
|
||||
result = ApiDependencies.invoker.services.model_manager.rename_model(
|
||||
ApiDependencies.invoker.services.model_manager.rename_model(
|
||||
base_model = base_model,
|
||||
model_type = model_type,
|
||||
model_name = model_name,
|
||||
new_name = info.model_name,
|
||||
new_base = info.base_model,
|
||||
)
|
||||
logger.debug(f'renaming result = {result}')
|
||||
logger.info(f'Successfully renamed {base_model}/{model_name}=>{info.base_model}/{info.model_name}')
|
||||
# update information to support an update of attributes
|
||||
model_name = info.model_name
|
||||
base_model = info.base_model
|
||||
new_info = ApiDependencies.invoker.services.model_manager.list_model(
|
||||
model_name=model_name,
|
||||
base_model=base_model,
|
||||
model_type=model_type,
|
||||
)
|
||||
if new_info.get('path') != previous_info.get('path'): # model manager moved model path during rename - don't overwrite it
|
||||
info.path = new_info.get('path')
|
||||
|
||||
ApiDependencies.invoker.services.model_manager.update_model(
|
||||
model_name=model_name,
|
||||
|
@ -568,6 +568,9 @@ class ModelManager(object):
|
||||
model_type=cur_model_type,
|
||||
)
|
||||
|
||||
# expose paths as absolute to help web UI
|
||||
if path := model_dict.get('path'):
|
||||
model_dict['path'] = str(self.app_config.root_path / path)
|
||||
models.append(model_dict)
|
||||
|
||||
return models
|
||||
@ -635,6 +638,10 @@ class ModelManager(object):
|
||||
The returned dict has the same format as the dict returned by
|
||||
model_info().
|
||||
"""
|
||||
# relativize paths as they go in - this makes it easier to move the root directory around
|
||||
if path := model_attributes.get('path'):
|
||||
if Path(path).is_relative_to(self.app_config.root_path):
|
||||
model_attributes['path'] = str(Path(path).relative_to(self.app_config.root_path))
|
||||
|
||||
model_class = MODEL_CLASSES[base_model][model_type]
|
||||
model_config = model_class.create_config(**model_attributes)
|
||||
@ -700,7 +707,7 @@ class ModelManager(object):
|
||||
|
||||
# if this is a model file/directory that we manage ourselves, we need to move it
|
||||
if old_path.is_relative_to(self.app_config.models_path):
|
||||
new_path = self.app_config.root_path / 'models' / new_base.value / model_type.value / new_name
|
||||
new_path = self.app_config.root_path / 'models' / BaseModelType(new_base).value / ModelType(model_type).value / new_name
|
||||
move(old_path, new_path)
|
||||
model_cfg.path = str(new_path.relative_to(self.app_config.root_path))
|
||||
|
||||
|
@ -16,6 +16,7 @@ from .base import (
|
||||
calc_model_size_by_data,
|
||||
classproperty,
|
||||
InvalidModelException,
|
||||
ModelNotFoundException,
|
||||
)
|
||||
from invokeai.app.services.config import InvokeAIAppConfig
|
||||
from diffusers.utils import is_safetensors_available
|
||||
|
@ -399,6 +399,8 @@
|
||||
"deleteModel": "Delete Model",
|
||||
"deleteConfig": "Delete Config",
|
||||
"deleteMsg1": "Are you sure you want to delete this model from InvokeAI?",
|
||||
"modelDeleted": "Model Deleted",
|
||||
"modelDeleteFailed": "Failed to delete model",
|
||||
"deleteMsg2": "This WILL delete the model from disk if it is in the InvokeAI root folder. If you are using a custom location, then the model WILL NOT be deleted from disk.",
|
||||
"formMessageDiffusersModelLocation": "Diffusers Model Location",
|
||||
"formMessageDiffusersModelLocationDesc": "Please enter at least one.",
|
||||
@ -408,11 +410,13 @@
|
||||
"convertToDiffusers": "Convert To Diffusers",
|
||||
"convertToDiffusersHelpText1": "This model will be converted to the 🧨 Diffusers format.",
|
||||
"convertToDiffusersHelpText2": "This process will replace your Model Manager entry with the Diffusers version of the same model.",
|
||||
"convertToDiffusersHelpText3": "Your checkpoint file on the disk will NOT be deleted or modified in anyway. You can add your checkpoint to the Model Manager again if you want to.",
|
||||
"convertToDiffusersHelpText3": "Your checkpoint file on disk WILL be deleted if it is in InvokeAI root folder. If it is in a custom location, then it WILL NOT be deleted.",
|
||||
"convertToDiffusersHelpText4": "This is a one time process only. It might take around 30s-60s depending on the specifications of your computer.",
|
||||
"convertToDiffusersHelpText5": "Please make sure you have enough disk space. Models generally vary between 2GB-7GB in size.",
|
||||
"convertToDiffusersHelpText6": "Do you wish to convert this model?",
|
||||
"convertToDiffusersSaveLocation": "Save Location",
|
||||
"noCustomLocationProvided": "No Custom Location Provided",
|
||||
"convertingModelBegin": "Converting Model. Please wait.",
|
||||
"v1": "v1",
|
||||
"v2_base": "v2 (512px)",
|
||||
"v2_768": "v2 (768px)",
|
||||
@ -450,7 +454,8 @@
|
||||
"none": "none",
|
||||
"addDifference": "Add Difference",
|
||||
"pickModelType": "Pick Model Type",
|
||||
"selectModel": "Select Model"
|
||||
"selectModel": "Select Model",
|
||||
"importModels": "Import Models"
|
||||
},
|
||||
"parameters": {
|
||||
"general": "General",
|
||||
|
@ -21,6 +21,7 @@ import generationReducer from 'features/parameters/store/generationSlice';
|
||||
import postprocessingReducer from 'features/parameters/store/postprocessingSlice';
|
||||
import configReducer from 'features/system/store/configSlice';
|
||||
import systemReducer from 'features/system/store/systemSlice';
|
||||
import modelmanagerReducer from 'features/ui/components/tabs/ModelManager/store/modelManagerSlice';
|
||||
import hotkeysReducer from 'features/ui/store/hotkeysSlice';
|
||||
import uiReducer from 'features/ui/store/uiSlice';
|
||||
|
||||
@ -49,6 +50,7 @@ const allReducers = {
|
||||
dynamicPrompts: dynamicPromptsReducer,
|
||||
imageDeletion: imageDeletionReducer,
|
||||
lora: loraReducer,
|
||||
modelmanager: modelmanagerReducer,
|
||||
[api.reducerPath]: api.reducer,
|
||||
};
|
||||
|
||||
@ -67,6 +69,7 @@ const rememberedKeys: (keyof typeof allReducers)[] = [
|
||||
'controlNet',
|
||||
'dynamicPrompts',
|
||||
'lora',
|
||||
'modelmanager',
|
||||
];
|
||||
|
||||
export const store = configureStore({
|
||||
|
@ -8,19 +8,34 @@ import {
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { stopPastePropagation } from 'common/util/stopPastePropagation';
|
||||
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
||||
import { ChangeEvent, KeyboardEvent, memo, useCallback } from 'react';
|
||||
import {
|
||||
CSSProperties,
|
||||
ChangeEvent,
|
||||
KeyboardEvent,
|
||||
memo,
|
||||
useCallback,
|
||||
} from 'react';
|
||||
|
||||
interface IAIInputProps extends InputProps {
|
||||
label?: string;
|
||||
labelPos?: 'top' | 'side';
|
||||
value?: string;
|
||||
size?: string;
|
||||
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||
formControlProps?: Omit<FormControlProps, 'isInvalid' | 'isDisabled'>;
|
||||
}
|
||||
|
||||
const labelPosVerticalStyle: CSSProperties = {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
};
|
||||
|
||||
const IAIInput = (props: IAIInputProps) => {
|
||||
const {
|
||||
label = '',
|
||||
labelPos = 'top',
|
||||
isDisabled = false,
|
||||
isInvalid,
|
||||
formControlProps,
|
||||
@ -51,6 +66,7 @@ const IAIInput = (props: IAIInputProps) => {
|
||||
isInvalid={isInvalid}
|
||||
isDisabled={isDisabled}
|
||||
{...formControlProps}
|
||||
style={labelPos === 'side' ? labelPosVerticalStyle : undefined}
|
||||
>
|
||||
{label !== '' && <FormLabel>{label}</FormLabel>}
|
||||
<Input
|
||||
|
@ -36,6 +36,7 @@ export default function IAIMantineTextInput(props: IAIMantineTextInputProps) {
|
||||
label: {
|
||||
color: mode(base700, base300)(colorMode),
|
||||
fontWeight: 'normal',
|
||||
marginBottom: 4,
|
||||
},
|
||||
})}
|
||||
{...rest}
|
||||
|
@ -9,14 +9,14 @@ export type IAISelectDataType = {
|
||||
tooltip?: string;
|
||||
};
|
||||
|
||||
type IAISelectProps = Omit<SelectProps, 'label'> & {
|
||||
export type IAISelectProps = Omit<SelectProps, 'label'> & {
|
||||
tooltip?: string;
|
||||
inputRef?: RefObject<HTMLInputElement>;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
const IAIMantineSelect = (props: IAISelectProps) => {
|
||||
const { tooltip, inputRef, label, disabled, ...rest } = props;
|
||||
const { tooltip, inputRef, label, disabled, required, ...rest } = props;
|
||||
|
||||
const styles = useMantineSelectStyles();
|
||||
|
||||
@ -25,7 +25,7 @@ const IAIMantineSelect = (props: IAISelectProps) => {
|
||||
<Select
|
||||
label={
|
||||
label ? (
|
||||
<FormControl isDisabled={disabled}>
|
||||
<FormControl isRequired={required} isDisabled={disabled}>
|
||||
<FormLabel>{label}</FormLabel>
|
||||
</FormControl>
|
||||
) : undefined
|
||||
|
@ -16,14 +16,13 @@ import {
|
||||
ASSETS_CATEGORIES,
|
||||
IMAGE_CATEGORIES,
|
||||
IMAGE_LIMIT,
|
||||
selectImagesAll,
|
||||
} from 'features/gallery//store/gallerySlice';
|
||||
import { selectFilteredImages } from 'features/gallery/store/gallerySelectors';
|
||||
import { VirtuosoGrid } from 'react-virtuoso';
|
||||
import { receivedPageOfImages } from 'services/api/thunks/image';
|
||||
import { useListBoardImagesQuery } from '../../../../services/api/endpoints/boardImages';
|
||||
import ImageGridItemContainer from './ImageGridItemContainer';
|
||||
import ImageGridListContainer from './ImageGridListContainer';
|
||||
import { useListBoardImagesQuery } from '../../../../services/api/endpoints/boardImages';
|
||||
|
||||
const selector = createSelector(
|
||||
[stateSelector, selectFilteredImages],
|
||||
@ -180,7 +179,6 @@ const GalleryImageGrid = () => {
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
console.log({ selectedBoardId });
|
||||
|
||||
if (status !== 'rejected') {
|
||||
return (
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { UseToastOptions } from '@chakra-ui/react';
|
||||
import { PayloadAction, createSlice } from '@reduxjs/toolkit';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
|
||||
import { InvokeLogLevel } from 'app/logging/useLogger';
|
||||
import { userInvoked } from 'app/store/actions';
|
||||
import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice';
|
||||
import { TFuncKey, t } from 'i18next';
|
||||
import { t } from 'i18next';
|
||||
import { LogLevelName } from 'roarr';
|
||||
import { imageUploaded } from 'services/api/thunks/image';
|
||||
import {
|
||||
@ -44,8 +43,6 @@ export interface SystemState {
|
||||
isCancelable: boolean;
|
||||
enableImageDebugging: boolean;
|
||||
toastQueue: UseToastOptions[];
|
||||
searchFolder: string | null;
|
||||
foundModels: InvokeAI.FoundModel[] | null;
|
||||
/**
|
||||
* The current progress image
|
||||
*/
|
||||
@ -79,7 +76,7 @@ export interface SystemState {
|
||||
*/
|
||||
consoleLogLevel: InvokeLogLevel;
|
||||
shouldLogToConsole: boolean;
|
||||
statusTranslationKey: TFuncKey;
|
||||
statusTranslationKey: any;
|
||||
/**
|
||||
* When a session is canceled, its ID is stored here until a new session is created.
|
||||
*/
|
||||
@ -106,8 +103,6 @@ export const initialSystemState: SystemState = {
|
||||
isCancelable: true,
|
||||
enableImageDebugging: false,
|
||||
toastQueue: [],
|
||||
searchFolder: null,
|
||||
foundModels: null,
|
||||
progressImage: null,
|
||||
shouldAntialiasProgressImage: false,
|
||||
sessionId: null,
|
||||
@ -132,7 +127,7 @@ export const systemSlice = createSlice({
|
||||
setIsProcessing: (state, action: PayloadAction<boolean>) => {
|
||||
state.isProcessing = action.payload;
|
||||
},
|
||||
setCurrentStatus: (state, action: PayloadAction<TFuncKey>) => {
|
||||
setCurrentStatus: (state, action: any) => {
|
||||
state.statusTranslationKey = action.payload;
|
||||
},
|
||||
setShouldConfirmOnDelete: (state, action: PayloadAction<boolean>) => {
|
||||
@ -153,15 +148,6 @@ export const systemSlice = createSlice({
|
||||
clearToastQueue: (state) => {
|
||||
state.toastQueue = [];
|
||||
},
|
||||
setSearchFolder: (state, action: PayloadAction<string | null>) => {
|
||||
state.searchFolder = action.payload;
|
||||
},
|
||||
setFoundModels: (
|
||||
state,
|
||||
action: PayloadAction<InvokeAI.FoundModel[] | null>
|
||||
) => {
|
||||
state.foundModels = action.payload;
|
||||
},
|
||||
/**
|
||||
* A cancel was scheduled
|
||||
*/
|
||||
@ -426,8 +412,6 @@ export const {
|
||||
setEnableImageDebugging,
|
||||
addToast,
|
||||
clearToastQueue,
|
||||
setSearchFolder,
|
||||
setFoundModels,
|
||||
cancelScheduled,
|
||||
scheduledCancelAborted,
|
||||
cancelTypeChanged,
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react';
|
||||
import i18n from 'i18n';
|
||||
import { ReactNode, memo } from 'react';
|
||||
import AddModelsPanel from './subpanels/AddModelsPanel';
|
||||
import ImportModelsPanel from './subpanels/ImportModelsPanel';
|
||||
import MergeModelsPanel from './subpanels/MergeModelsPanel';
|
||||
import ModelManagerPanel from './subpanels/ModelManagerPanel';
|
||||
|
||||
type ModelManagerTabName = 'modelManager' | 'addModels' | 'mergeModels';
|
||||
type ModelManagerTabName = 'modelManager' | 'importModels' | 'mergeModels';
|
||||
|
||||
type ModelManagerTabInfo = {
|
||||
id: ModelManagerTabName;
|
||||
@ -20,9 +20,9 @@ const tabs: ModelManagerTabInfo[] = [
|
||||
content: <ModelManagerPanel />,
|
||||
},
|
||||
{
|
||||
id: 'addModels',
|
||||
label: i18n.t('modelManager.addModel'),
|
||||
content: <AddModelsPanel />,
|
||||
id: 'importModels',
|
||||
label: i18n.t('modelManager.importModels'),
|
||||
content: <ImportModelsPanel />,
|
||||
},
|
||||
{
|
||||
id: 'mergeModels',
|
||||
@ -46,7 +46,7 @@ const ModelManagerTab = () => {
|
||||
</Tab>
|
||||
))}
|
||||
</TabList>
|
||||
<TabPanels sx={{ w: 'full', h: 'full', p: 4 }}>
|
||||
<TabPanels sx={{ w: 'full', h: 'full' }}>
|
||||
{tabs.map((tab) => (
|
||||
<TabPanel sx={{ w: 'full', h: 'full' }} key={tab.id}>
|
||||
{tab.content}
|
||||
|
@ -0,0 +1,29 @@
|
||||
import { PayloadAction, createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
type ModelManagerState = {
|
||||
searchFolder: string | null;
|
||||
advancedAddScanModel: string | null;
|
||||
};
|
||||
|
||||
const initialModelManagerState: ModelManagerState = {
|
||||
searchFolder: null,
|
||||
advancedAddScanModel: null,
|
||||
};
|
||||
|
||||
export const modelManagerSlice = createSlice({
|
||||
name: 'modelmanager',
|
||||
initialState: initialModelManagerState,
|
||||
reducers: {
|
||||
setSearchFolder: (state, action: PayloadAction<string | null>) => {
|
||||
state.searchFolder = action.payload;
|
||||
},
|
||||
setAdvancedAddScanModel: (state, action: PayloadAction<string | null>) => {
|
||||
state.advancedAddScanModel = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setSearchFolder, setAdvancedAddScanModel } =
|
||||
modelManagerSlice.actions;
|
||||
|
||||
export default modelManagerSlice.reducer;
|
@ -0,0 +1,3 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
|
||||
export const modelmanagerSelector = (state: RootState) => state.modelmanager;
|
@ -1,43 +0,0 @@
|
||||
import { Divider, Flex, useColorMode } from '@chakra-ui/react';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { setAddNewModelUIOption } from 'features/ui/store/uiSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import AddCheckpointModel from './AddModelsPanel/AddCheckpointModel';
|
||||
import AddDiffusersModel from './AddModelsPanel/AddDiffusersModel';
|
||||
|
||||
export default function AddModelsPanel() {
|
||||
const addNewModelUIOption = useAppSelector(
|
||||
(state: RootState) => state.ui.addNewModelUIOption
|
||||
);
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Flex flexDirection="column" gap={4}>
|
||||
<Flex columnGap={4}>
|
||||
<IAIButton
|
||||
onClick={() => dispatch(setAddNewModelUIOption('ckpt'))}
|
||||
isChecked={addNewModelUIOption == 'ckpt'}
|
||||
>
|
||||
{t('modelManager.addCheckpointModel')}
|
||||
</IAIButton>
|
||||
<IAIButton
|
||||
onClick={() => dispatch(setAddNewModelUIOption('diffusers'))}
|
||||
isChecked={addNewModelUIOption == 'diffusers'}
|
||||
>
|
||||
{t('modelManager.addDiffuserModel')}
|
||||
</IAIButton>
|
||||
</Flex>
|
||||
|
||||
<Divider />
|
||||
|
||||
{addNewModelUIOption == 'ckpt' && <AddCheckpointModel />}
|
||||
{addNewModelUIOption == 'diffusers' && <AddDiffusersModel />}
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -1,337 +0,0 @@
|
||||
import {
|
||||
Flex,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Text,
|
||||
VStack,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIInput from 'common/components/IAIInput';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import React from 'react';
|
||||
|
||||
// import { addNewModel } from 'app/socketio/actions';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
|
||||
import { Field, Formik } from 'formik';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { InvokeModelConfigProps } from 'app/types/invokeai';
|
||||
import IAIForm from 'common/components/IAIForm';
|
||||
import { IAIFormItemWrapper } from 'common/components/IAIForms/IAIFormItemWrapper';
|
||||
import { setAddNewModelUIOption } from 'features/ui/store/uiSlice';
|
||||
import type { FieldInputProps, FormikProps } from 'formik';
|
||||
import SearchModels from './SearchModels';
|
||||
|
||||
const MIN_MODEL_SIZE = 64;
|
||||
const MAX_MODEL_SIZE = 2048;
|
||||
|
||||
export default function AddCheckpointModel() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isProcessing = useAppSelector(
|
||||
(state: RootState) => state.system.isProcessing
|
||||
);
|
||||
|
||||
function hasWhiteSpace(s: string) {
|
||||
return /\s/.test(s);
|
||||
}
|
||||
|
||||
function baseValidation(value: string) {
|
||||
let error;
|
||||
if (hasWhiteSpace(value)) error = t('modelManager.cannotUseSpaces');
|
||||
return error;
|
||||
}
|
||||
|
||||
const addModelFormValues: InvokeModelConfigProps = {
|
||||
name: '',
|
||||
description: '',
|
||||
config: 'configs/stable-diffusion/v1-inference.yaml',
|
||||
weights: '',
|
||||
vae: '',
|
||||
width: 512,
|
||||
height: 512,
|
||||
format: 'ckpt',
|
||||
default: false,
|
||||
};
|
||||
|
||||
const addModelFormSubmitHandler = (values: InvokeModelConfigProps) => {
|
||||
dispatch(addNewModel(values));
|
||||
dispatch(setAddNewModelUIOption(null));
|
||||
};
|
||||
|
||||
const [addManually, setAddmanually] = React.useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<VStack gap={2} alignItems="flex-start">
|
||||
<Flex columnGap={4}>
|
||||
<IAISimpleCheckbox
|
||||
isChecked={!addManually}
|
||||
label={t('modelManager.scanForModels')}
|
||||
onChange={() => setAddmanually(!addManually)}
|
||||
/>
|
||||
<IAISimpleCheckbox
|
||||
label={t('modelManager.addManually')}
|
||||
isChecked={addManually}
|
||||
onChange={() => setAddmanually(!addManually)}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
{addManually ? (
|
||||
<Formik
|
||||
initialValues={addModelFormValues}
|
||||
onSubmit={addModelFormSubmitHandler}
|
||||
>
|
||||
{({ handleSubmit, errors, touched }) => (
|
||||
<IAIForm onSubmit={handleSubmit} sx={{ w: 'full' }}>
|
||||
<VStack rowGap={2}>
|
||||
<Text fontSize={20} fontWeight="bold" alignSelf="start">
|
||||
{t('modelManager.manual')}
|
||||
</Text>
|
||||
{/* Name */}
|
||||
<IAIFormItemWrapper>
|
||||
<FormControl
|
||||
isInvalid={!!errors.name && touched.name}
|
||||
isRequired
|
||||
>
|
||||
<FormLabel htmlFor="name" fontSize="sm">
|
||||
{t('modelManager.name')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
validate={baseValidation}
|
||||
width="full"
|
||||
/>
|
||||
{!!errors.name && touched.name ? (
|
||||
<FormErrorMessage>{errors.name}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.nameValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
|
||||
{/* Description */}
|
||||
<IAIFormItemWrapper>
|
||||
<FormControl
|
||||
isInvalid={!!errors.description && touched.description}
|
||||
isRequired
|
||||
>
|
||||
<FormLabel htmlFor="description" fontSize="sm">
|
||||
{t('modelManager.description')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="description"
|
||||
name="description"
|
||||
type="text"
|
||||
width="full"
|
||||
/>
|
||||
{!!errors.description && touched.description ? (
|
||||
<FormErrorMessage>
|
||||
{errors.description}
|
||||
</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.descriptionValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
|
||||
{/* Config */}
|
||||
<IAIFormItemWrapper>
|
||||
<FormControl
|
||||
isInvalid={!!errors.config && touched.config}
|
||||
isRequired
|
||||
>
|
||||
<FormLabel htmlFor="config" fontSize="sm">
|
||||
{t('modelManager.config')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="config"
|
||||
name="config"
|
||||
type="text"
|
||||
width="full"
|
||||
/>
|
||||
{!!errors.config && touched.config ? (
|
||||
<FormErrorMessage>{errors.config}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.configValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
|
||||
{/* Weights */}
|
||||
<IAIFormItemWrapper>
|
||||
<FormControl
|
||||
isInvalid={!!errors.weights && touched.weights}
|
||||
isRequired
|
||||
>
|
||||
<FormLabel htmlFor="config" fontSize="sm">
|
||||
{t('modelManager.modelLocation')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="weights"
|
||||
name="weights"
|
||||
type="text"
|
||||
width="full"
|
||||
/>
|
||||
{!!errors.weights && touched.weights ? (
|
||||
<FormErrorMessage>{errors.weights}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.modelLocationValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
|
||||
{/* VAE */}
|
||||
<IAIFormItemWrapper>
|
||||
<FormControl isInvalid={!!errors.vae && touched.vae}>
|
||||
<FormLabel htmlFor="vae" fontSize="sm">
|
||||
{t('modelManager.vaeLocation')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="vae"
|
||||
name="vae"
|
||||
type="text"
|
||||
width="full"
|
||||
/>
|
||||
{!!errors.vae && touched.vae ? (
|
||||
<FormErrorMessage>{errors.vae}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.vaeLocationValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
|
||||
<HStack width="100%">
|
||||
{/* Width */}
|
||||
<IAIFormItemWrapper>
|
||||
<FormControl isInvalid={!!errors.width && touched.width}>
|
||||
<FormLabel htmlFor="width" fontSize="sm">
|
||||
{t('modelManager.width')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field id="width" name="width">
|
||||
{({
|
||||
field,
|
||||
form,
|
||||
}: {
|
||||
field: FieldInputProps<number>;
|
||||
form: FormikProps<InvokeModelConfigProps>;
|
||||
}) => (
|
||||
<IAINumberInput
|
||||
id="width"
|
||||
name="width"
|
||||
min={MIN_MODEL_SIZE}
|
||||
max={MAX_MODEL_SIZE}
|
||||
step={64}
|
||||
value={form.values.width}
|
||||
onChange={(value) =>
|
||||
form.setFieldValue(field.name, Number(value))
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
{!!errors.width && touched.width ? (
|
||||
<FormErrorMessage>{errors.width}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.widthValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
|
||||
{/* Height */}
|
||||
<IAIFormItemWrapper>
|
||||
<FormControl isInvalid={!!errors.height && touched.height}>
|
||||
<FormLabel htmlFor="height" fontSize="sm">
|
||||
{t('modelManager.height')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field id="height" name="height">
|
||||
{({
|
||||
field,
|
||||
form,
|
||||
}: {
|
||||
field: FieldInputProps<number>;
|
||||
form: FormikProps<InvokeModelConfigProps>;
|
||||
}) => (
|
||||
<IAINumberInput
|
||||
id="height"
|
||||
name="height"
|
||||
min={MIN_MODEL_SIZE}
|
||||
max={MAX_MODEL_SIZE}
|
||||
step={64}
|
||||
value={form.values.height}
|
||||
onChange={(value) =>
|
||||
form.setFieldValue(field.name, Number(value))
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
{!!errors.height && touched.height ? (
|
||||
<FormErrorMessage>{errors.height}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.heightValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
</HStack>
|
||||
|
||||
<IAIButton
|
||||
type="submit"
|
||||
className="modal-close-btn"
|
||||
isLoading={isProcessing}
|
||||
>
|
||||
{t('modelManager.addModel')}
|
||||
</IAIButton>
|
||||
</VStack>
|
||||
</IAIForm>
|
||||
)}
|
||||
</Formik>
|
||||
) : (
|
||||
<SearchModels />
|
||||
)}
|
||||
</VStack>
|
||||
);
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
import {
|
||||
Flex,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Text,
|
||||
VStack,
|
||||
} from '@chakra-ui/react';
|
||||
import { InvokeDiffusersModelConfigProps } from 'app/types/invokeai';
|
||||
// import { addNewModel } from 'app/socketio/actions';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIInput from 'common/components/IAIInput';
|
||||
import { setAddNewModelUIOption } from 'features/ui/store/uiSlice';
|
||||
import { Field, Formik } from 'formik';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { RootState } from 'app/store/store';
|
||||
import IAIForm from 'common/components/IAIForm';
|
||||
import { IAIFormItemWrapper } from 'common/components/IAIForms/IAIFormItemWrapper';
|
||||
|
||||
export default function AddDiffusersModel() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isProcessing = useAppSelector(
|
||||
(state: RootState) => state.system.isProcessing
|
||||
);
|
||||
|
||||
function hasWhiteSpace(s: string) {
|
||||
return /\s/.test(s);
|
||||
}
|
||||
|
||||
function baseValidation(value: string) {
|
||||
let error;
|
||||
if (hasWhiteSpace(value)) error = t('modelManager.cannotUseSpaces');
|
||||
return error;
|
||||
}
|
||||
|
||||
const addModelFormValues: InvokeDiffusersModelConfigProps = {
|
||||
name: '',
|
||||
description: '',
|
||||
repo_id: '',
|
||||
path: '',
|
||||
format: 'diffusers',
|
||||
default: false,
|
||||
vae: {
|
||||
repo_id: '',
|
||||
path: '',
|
||||
},
|
||||
};
|
||||
|
||||
const addModelFormSubmitHandler = (
|
||||
values: InvokeDiffusersModelConfigProps
|
||||
) => {
|
||||
const diffusersModelToAdd = values;
|
||||
|
||||
if (values.path === '') delete diffusersModelToAdd.path;
|
||||
if (values.repo_id === '') delete diffusersModelToAdd.repo_id;
|
||||
if (values.vae.path === '') delete diffusersModelToAdd.vae.path;
|
||||
if (values.vae.repo_id === '') delete diffusersModelToAdd.vae.repo_id;
|
||||
|
||||
dispatch(addNewModel(diffusersModelToAdd));
|
||||
dispatch(setAddNewModelUIOption(null));
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex overflow="scroll" maxHeight={window.innerHeight - 270} width="100%">
|
||||
<Formik
|
||||
initialValues={addModelFormValues}
|
||||
onSubmit={addModelFormSubmitHandler}
|
||||
>
|
||||
{({ handleSubmit, errors, touched }) => (
|
||||
<IAIForm onSubmit={handleSubmit} w="full">
|
||||
<VStack rowGap={2}>
|
||||
<IAIFormItemWrapper>
|
||||
{/* Name */}
|
||||
<FormControl
|
||||
isInvalid={!!errors.name && touched.name}
|
||||
isRequired
|
||||
>
|
||||
<FormLabel htmlFor="name" fontSize="sm">
|
||||
{t('modelManager.name')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
validate={baseValidation}
|
||||
isRequired
|
||||
/>
|
||||
{!!errors.name && touched.name ? (
|
||||
<FormErrorMessage>{errors.name}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.nameValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
|
||||
<IAIFormItemWrapper>
|
||||
{/* Description */}
|
||||
<FormControl
|
||||
isInvalid={!!errors.description && touched.description}
|
||||
isRequired
|
||||
>
|
||||
<FormLabel htmlFor="description" fontSize="sm">
|
||||
{t('modelManager.description')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="description"
|
||||
name="description"
|
||||
type="text"
|
||||
isRequired
|
||||
/>
|
||||
{!!errors.description && touched.description ? (
|
||||
<FormErrorMessage>{errors.description}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.descriptionValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
|
||||
<IAIFormItemWrapper>
|
||||
<Text fontWeight="bold" fontSize="sm">
|
||||
{t('modelManager.formMessageDiffusersModelLocation')}
|
||||
</Text>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
fontStyle: 'italic',
|
||||
}}
|
||||
variant="subtext"
|
||||
>
|
||||
{t('modelManager.formMessageDiffusersModelLocationDesc')}
|
||||
</Text>
|
||||
|
||||
{/* Path */}
|
||||
<FormControl isInvalid={!!errors.path && touched.path}>
|
||||
<FormLabel htmlFor="path" fontSize="sm">
|
||||
{t('modelManager.modelLocation')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field as={IAIInput} id="path" name="path" type="text" />
|
||||
{!!errors.path && touched.path ? (
|
||||
<FormErrorMessage>{errors.path}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.modelLocationValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
|
||||
{/* Repo ID */}
|
||||
<FormControl isInvalid={!!errors.repo_id && touched.repo_id}>
|
||||
<FormLabel htmlFor="repo_id" fontSize="sm">
|
||||
{t('modelManager.repo_id')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="repo_id"
|
||||
name="repo_id"
|
||||
type="text"
|
||||
/>
|
||||
{!!errors.repo_id && touched.repo_id ? (
|
||||
<FormErrorMessage>{errors.repo_id}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.repoIDValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
|
||||
<IAIFormItemWrapper>
|
||||
{/* VAE Path */}
|
||||
<Text fontWeight="bold">
|
||||
{t('modelManager.formMessageDiffusersVAELocation')}
|
||||
</Text>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
fontStyle: 'italic',
|
||||
}}
|
||||
variant="subtext"
|
||||
>
|
||||
{t('modelManager.formMessageDiffusersVAELocationDesc')}
|
||||
</Text>
|
||||
<FormControl
|
||||
isInvalid={!!errors.vae?.path && touched.vae?.path}
|
||||
>
|
||||
<FormLabel htmlFor="vae.path" fontSize="sm">
|
||||
{t('modelManager.vaeLocation')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="vae.path"
|
||||
name="vae.path"
|
||||
type="text"
|
||||
/>
|
||||
{!!errors.vae?.path && touched.vae?.path ? (
|
||||
<FormErrorMessage>{errors.vae?.path}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.vaeLocationValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
|
||||
{/* VAE Repo ID */}
|
||||
<FormControl
|
||||
isInvalid={!!errors.vae?.repo_id && touched.vae?.repo_id}
|
||||
>
|
||||
<FormLabel htmlFor="vae.repo_id" fontSize="sm">
|
||||
{t('modelManager.vaeRepoID')}
|
||||
</FormLabel>
|
||||
<VStack alignItems="start">
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="vae.repo_id"
|
||||
name="vae.repo_id"
|
||||
type="text"
|
||||
/>
|
||||
{!!errors.vae?.repo_id && touched.vae?.repo_id ? (
|
||||
<FormErrorMessage>{errors.vae?.repo_id}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText margin={0}>
|
||||
{t('modelManager.vaeRepoIDValidationMsg')}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</VStack>
|
||||
</FormControl>
|
||||
</IAIFormItemWrapper>
|
||||
|
||||
<IAIButton type="submit" isLoading={isProcessing}>
|
||||
{t('modelManager.addModel')}
|
||||
</IAIButton>
|
||||
</VStack>
|
||||
</IAIForm>
|
||||
)}
|
||||
</Formik>
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
import { ButtonGroup, Flex } from '@chakra-ui/react';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { useState } from 'react';
|
||||
import AdvancedAddModels from './AdvancedAddModels';
|
||||
import SimpleAddModels from './SimpleAddModels';
|
||||
|
||||
export default function AddModels() {
|
||||
const [addModelMode, setAddModelMode] = useState<'simple' | 'advanced'>(
|
||||
'simple'
|
||||
);
|
||||
return (
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
width="100%"
|
||||
overflow="scroll"
|
||||
maxHeight={window.innerHeight - 250}
|
||||
gap={4}
|
||||
>
|
||||
<ButtonGroup isAttached>
|
||||
<IAIButton
|
||||
size="sm"
|
||||
isChecked={addModelMode == 'simple'}
|
||||
onClick={() => setAddModelMode('simple')}
|
||||
>
|
||||
Simple
|
||||
</IAIButton>
|
||||
<IAIButton
|
||||
size="sm"
|
||||
isChecked={addModelMode == 'advanced'}
|
||||
onClick={() => setAddModelMode('advanced')}
|
||||
>
|
||||
Advanced
|
||||
</IAIButton>
|
||||
</ButtonGroup>
|
||||
<Flex
|
||||
sx={{
|
||||
p: 4,
|
||||
borderRadius: 4,
|
||||
background: 'base.200',
|
||||
_dark: { background: 'base.800' },
|
||||
}}
|
||||
>
|
||||
{addModelMode === 'simple' && <SimpleAddModels />}
|
||||
{addModelMode === 'advanced' && <AdvancedAddModels />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { makeToast } from 'app/components/Toaster';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIMantineTextInput from 'common/components/IAIMantineInput';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAddMainModelsMutation } from 'services/api/endpoints/models';
|
||||
import { CheckpointModelConfig } from 'services/api/types';
|
||||
import { setAdvancedAddScanModel } from '../../store/modelManagerSlice';
|
||||
import BaseModelSelect from '../shared/BaseModelSelect';
|
||||
import CheckpointConfigsSelect from '../shared/CheckpointConfigsSelect';
|
||||
import ModelVariantSelect from '../shared/ModelVariantSelect';
|
||||
|
||||
type AdvancedAddCheckpointProps = {
|
||||
model_path?: string;
|
||||
};
|
||||
|
||||
export default function AdvancedAddCheckpoint(
|
||||
props: AdvancedAddCheckpointProps
|
||||
) {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { model_path } = props;
|
||||
|
||||
const advancedAddCheckpointForm = useForm<CheckpointModelConfig>({
|
||||
initialValues: {
|
||||
model_name: model_path
|
||||
? model_path.split('\\').splice(-1)[0].split('.')[0]
|
||||
: '',
|
||||
base_model: 'sd-1',
|
||||
model_type: 'main',
|
||||
path: model_path ? model_path : '',
|
||||
description: '',
|
||||
model_format: 'checkpoint',
|
||||
error: undefined,
|
||||
vae: '',
|
||||
variant: 'normal',
|
||||
config: 'configs\\stable-diffusion\\v1-inference.yaml',
|
||||
},
|
||||
});
|
||||
|
||||
const [addMainModel] = useAddMainModelsMutation();
|
||||
|
||||
const [useCustomConfig, setUseCustomConfig] = useState<boolean>(false);
|
||||
|
||||
const advancedAddCheckpointFormHandler = (values: CheckpointModelConfig) => {
|
||||
addMainModel({
|
||||
body: values,
|
||||
})
|
||||
.unwrap()
|
||||
.then((_) => {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: `Model Added: ${values.model_name}`,
|
||||
status: 'success',
|
||||
})
|
||||
)
|
||||
);
|
||||
advancedAddCheckpointForm.reset();
|
||||
|
||||
// Close Advanced Panel in Scan Models tab
|
||||
if (model_path) {
|
||||
dispatch(setAdvancedAddScanModel(null));
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error) {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: 'Model Add Failed',
|
||||
status: 'error',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={advancedAddCheckpointForm.onSubmit((v) =>
|
||||
advancedAddCheckpointFormHandler(v)
|
||||
)}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Flex flexDirection="column" gap={2}>
|
||||
<IAIMantineTextInput
|
||||
label="Model Name"
|
||||
required
|
||||
{...advancedAddCheckpointForm.getInputProps('model_name')}
|
||||
/>
|
||||
<BaseModelSelect
|
||||
{...advancedAddCheckpointForm.getInputProps('base_model')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
label="Model Location"
|
||||
required
|
||||
{...advancedAddCheckpointForm.getInputProps('path')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
label="Description"
|
||||
{...advancedAddCheckpointForm.getInputProps('description')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
label="VAE Location"
|
||||
{...advancedAddCheckpointForm.getInputProps('vae')}
|
||||
/>
|
||||
<ModelVariantSelect
|
||||
{...advancedAddCheckpointForm.getInputProps('variant')}
|
||||
/>
|
||||
<Flex flexDirection="column" width="100%" gap={2}>
|
||||
{!useCustomConfig ? (
|
||||
<CheckpointConfigsSelect
|
||||
required
|
||||
width="100%"
|
||||
{...advancedAddCheckpointForm.getInputProps('config')}
|
||||
/>
|
||||
) : (
|
||||
<IAIMantineTextInput
|
||||
required
|
||||
label="Custom Config File Location"
|
||||
{...advancedAddCheckpointForm.getInputProps('config')}
|
||||
/>
|
||||
)}
|
||||
<IAISimpleCheckbox
|
||||
isChecked={useCustomConfig}
|
||||
onChange={() => setUseCustomConfig(!useCustomConfig)}
|
||||
label="Use Custom Config"
|
||||
/>
|
||||
<IAIButton mt={2} type="submit">
|
||||
{t('modelManager.addModel')}
|
||||
</IAIButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { makeToast } from 'app/components/Toaster';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIMantineTextInput from 'common/components/IAIMantineInput';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAddMainModelsMutation } from 'services/api/endpoints/models';
|
||||
import { DiffusersModelConfig } from 'services/api/types';
|
||||
import { setAdvancedAddScanModel } from '../../store/modelManagerSlice';
|
||||
import BaseModelSelect from '../shared/BaseModelSelect';
|
||||
import ModelVariantSelect from '../shared/ModelVariantSelect';
|
||||
|
||||
type AdvancedAddDiffusersProps = {
|
||||
model_path?: string;
|
||||
};
|
||||
|
||||
export default function AdvancedAddDiffusers(props: AdvancedAddDiffusersProps) {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { model_path } = props;
|
||||
|
||||
const [addMainModel] = useAddMainModelsMutation();
|
||||
|
||||
const advancedAddDiffusersForm = useForm<DiffusersModelConfig>({
|
||||
initialValues: {
|
||||
model_name: model_path ? model_path.split('\\').splice(-1)[0] : '',
|
||||
base_model: 'sd-1',
|
||||
model_type: 'main',
|
||||
path: model_path ? model_path : '',
|
||||
description: '',
|
||||
model_format: 'diffusers',
|
||||
error: undefined,
|
||||
vae: '',
|
||||
variant: 'normal',
|
||||
},
|
||||
});
|
||||
|
||||
const advancedAddDiffusersFormHandler = (values: DiffusersModelConfig) => {
|
||||
addMainModel({
|
||||
body: values,
|
||||
})
|
||||
.unwrap()
|
||||
.then((_) => {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: `Model Added: ${values.model_name}`,
|
||||
status: 'success',
|
||||
})
|
||||
)
|
||||
);
|
||||
advancedAddDiffusersForm.reset();
|
||||
// Close Advanced Panel in Scan Models tab
|
||||
if (model_path) {
|
||||
dispatch(setAdvancedAddScanModel(null));
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error) {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: 'Model Add Failed',
|
||||
status: 'error',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={advancedAddDiffusersForm.onSubmit((v) =>
|
||||
advancedAddDiffusersFormHandler(v)
|
||||
)}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Flex flexDirection="column" gap={2}>
|
||||
<IAIMantineTextInput
|
||||
required
|
||||
label="Model Name"
|
||||
{...advancedAddDiffusersForm.getInputProps('model_name')}
|
||||
/>
|
||||
<BaseModelSelect
|
||||
{...advancedAddDiffusersForm.getInputProps('base_model')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
required
|
||||
label="Model Location"
|
||||
placeholder="Provide the path to a local folder where your Diffusers Model is stored"
|
||||
{...advancedAddDiffusersForm.getInputProps('path')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
label="Description"
|
||||
{...advancedAddDiffusersForm.getInputProps('description')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
label="VAE Location"
|
||||
{...advancedAddDiffusersForm.getInputProps('vae')}
|
||||
/>
|
||||
<ModelVariantSelect
|
||||
{...advancedAddDiffusersForm.getInputProps('variant')}
|
||||
/>
|
||||
<IAIButton mt={2} type="submit">
|
||||
{t('modelManager.addModel')}
|
||||
</IAIButton>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { SelectItem } from '@mantine/core';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import { useState } from 'react';
|
||||
import AdvancedAddCheckpoint from './AdvancedAddCheckpoint';
|
||||
import AdvancedAddDiffusers from './AdvancedAddDiffusers';
|
||||
|
||||
export const advancedAddModeData: SelectItem[] = [
|
||||
{ label: 'Diffusers', value: 'diffusers' },
|
||||
{ label: 'Checkpoint / Safetensors', value: 'checkpoint' },
|
||||
];
|
||||
|
||||
export type ManualAddMode = 'diffusers' | 'checkpoint';
|
||||
|
||||
export default function AdvancedAddModels() {
|
||||
const [advancedAddMode, setAdvancedAddMode] =
|
||||
useState<ManualAddMode>('diffusers');
|
||||
|
||||
return (
|
||||
<Flex flexDirection="column" gap={4} width="100%">
|
||||
<IAIMantineSelect
|
||||
label="Model Type"
|
||||
value={advancedAddMode}
|
||||
data={advancedAddModeData}
|
||||
onChange={(v) => {
|
||||
if (!v) return;
|
||||
setAdvancedAddMode(v as ManualAddMode);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
p: 4,
|
||||
borderRadius: 4,
|
||||
bg: 'base.300',
|
||||
_dark: {
|
||||
bg: 'base.850',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{advancedAddMode === 'diffusers' && <AdvancedAddDiffusers />}
|
||||
{advancedAddMode === 'checkpoint' && <AdvancedAddCheckpoint />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import { makeToast } from 'app/components/Toaster';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIInput from 'common/components/IAIInput';
|
||||
import IAIScrollArea from 'common/components/IAIScrollArea';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { difference, forEach, intersection, map, values } from 'lodash-es';
|
||||
import { ChangeEvent, MouseEvent, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
SearchFolderResponse,
|
||||
useGetMainModelsQuery,
|
||||
useGetModelsInFolderQuery,
|
||||
useImportMainModelsMutation,
|
||||
} from 'services/api/endpoints/models';
|
||||
import { setAdvancedAddScanModel } from '../../store/modelManagerSlice';
|
||||
|
||||
export default function FoundModelsList() {
|
||||
const searchFolder = useAppSelector(
|
||||
(state: RootState) => state.modelmanager.searchFolder
|
||||
);
|
||||
const [nameFilter, setNameFilter] = useState<string>('');
|
||||
|
||||
// Get paths of models that are already installed
|
||||
const { data: installedModels } = useGetMainModelsQuery();
|
||||
|
||||
// Get all model paths from a given directory
|
||||
const { foundModels, alreadyInstalled, filteredModels } =
|
||||
useGetModelsInFolderQuery(
|
||||
{
|
||||
search_path: searchFolder ? searchFolder : '',
|
||||
},
|
||||
{
|
||||
selectFromResult: ({ data }) => {
|
||||
const installedModelValues = values(installedModels?.entities);
|
||||
const installedModelPaths = map(installedModelValues, 'path');
|
||||
// Only select models those that aren't already installed to Invoke
|
||||
const notInstalledModels = difference(data, installedModelPaths);
|
||||
const alreadyInstalled = intersection(data, installedModelPaths);
|
||||
return {
|
||||
foundModels: data,
|
||||
alreadyInstalled: foundModelsFilter(alreadyInstalled, nameFilter),
|
||||
filteredModels: foundModelsFilter(notInstalledModels, nameFilter),
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const [importMainModel, { isLoading }] = useImportMainModelsMutation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const quickAddHandler = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
const model_name = e.currentTarget.id.split('\\').splice(-1)[0];
|
||||
importMainModel({
|
||||
body: {
|
||||
location: e.currentTarget.id,
|
||||
},
|
||||
})
|
||||
.unwrap()
|
||||
.then((_) => {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: `Added Model: ${model_name}`,
|
||||
status: 'success',
|
||||
})
|
||||
)
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error) {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: 'Faile To Add Model',
|
||||
status: 'error',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
[dispatch, importMainModel]
|
||||
);
|
||||
|
||||
const handleSearchFilter = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
setNameFilter(e.target.value);
|
||||
}, []);
|
||||
|
||||
const renderModels = ({
|
||||
models,
|
||||
showActions = true,
|
||||
}: {
|
||||
models: string[];
|
||||
showActions?: boolean;
|
||||
}) => {
|
||||
return models.map((model) => {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
p: 4,
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
bg: 'base.200',
|
||||
_dark: {
|
||||
bg: 'base.800',
|
||||
},
|
||||
}}
|
||||
key={model}
|
||||
>
|
||||
<Flex w="100%" sx={{ flexDirection: 'column', minW: '25%' }}>
|
||||
<Text sx={{ fontWeight: 600 }}>
|
||||
{model.split('\\').slice(-1)[0]}
|
||||
</Text>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
color: 'base.600',
|
||||
_dark: {
|
||||
color: 'base.400',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{model}
|
||||
</Text>
|
||||
</Flex>
|
||||
{showActions ? (
|
||||
<Flex gap={2}>
|
||||
<IAIButton
|
||||
id={model}
|
||||
onClick={quickAddHandler}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Quick Add
|
||||
</IAIButton>
|
||||
<IAIButton
|
||||
onClick={() => dispatch(setAdvancedAddScanModel(model))}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Advanced
|
||||
</IAIButton>
|
||||
</Flex>
|
||||
) : (
|
||||
<Text
|
||||
sx={{
|
||||
fontWeight: 600,
|
||||
p: 2,
|
||||
borderRadius: 4,
|
||||
color: 'accent.50',
|
||||
bg: 'accent.400',
|
||||
_dark: {
|
||||
color: 'accent.100',
|
||||
bg: 'accent.600',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Installed
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const renderFoundModels = () => {
|
||||
if (!searchFolder) return;
|
||||
|
||||
if (!foundModels || foundModels.length === 0) {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: 96,
|
||||
userSelect: 'none',
|
||||
bg: 'base.200',
|
||||
_dark: {
|
||||
bg: 'base.900',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Text variant="subtext">No Models Found</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
w: '100%',
|
||||
minW: '50%',
|
||||
}}
|
||||
>
|
||||
<IAIInput
|
||||
onChange={handleSearchFilter}
|
||||
label={t('modelManager.search')}
|
||||
labelPos="side"
|
||||
/>
|
||||
<Flex p={2} gap={2}>
|
||||
<Text sx={{ fontWeight: 600 }}>
|
||||
Models Found: {foundModels.length}
|
||||
</Text>
|
||||
<Text
|
||||
sx={{
|
||||
fontWeight: 600,
|
||||
color: 'accent.500',
|
||||
_dark: {
|
||||
color: 'accent.200',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Not Installed: {filteredModels.length}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<IAIScrollArea offsetScrollbars>
|
||||
<Flex gap={2} flexDirection="column">
|
||||
{renderModels({ models: filteredModels })}
|
||||
{renderModels({ models: alreadyInstalled, showActions: false })}
|
||||
</Flex>
|
||||
</IAIScrollArea>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
return renderFoundModels();
|
||||
}
|
||||
|
||||
const foundModelsFilter = (
|
||||
data: SearchFolderResponse | undefined,
|
||||
nameFilter: string
|
||||
) => {
|
||||
const filteredModels: SearchFolderResponse = [];
|
||||
forEach(data, (model) => {
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.includes(nameFilter)) {
|
||||
filteredModels.push(model);
|
||||
}
|
||||
});
|
||||
return filteredModels;
|
||||
};
|
@ -0,0 +1,99 @@
|
||||
import { Box, Flex, Text } from '@chakra-ui/react';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { FaTimes } from 'react-icons/fa';
|
||||
import { setAdvancedAddScanModel } from '../../store/modelManagerSlice';
|
||||
import AdvancedAddCheckpoint from './AdvancedAddCheckpoint';
|
||||
import AdvancedAddDiffusers from './AdvancedAddDiffusers';
|
||||
import { ManualAddMode, advancedAddModeData } from './AdvancedAddModels';
|
||||
|
||||
export default function ScanAdvancedAddModels() {
|
||||
const advancedAddScanModel = useAppSelector(
|
||||
(state: RootState) => state.modelmanager.advancedAddScanModel
|
||||
);
|
||||
|
||||
const [advancedAddMode, setAdvancedAddMode] =
|
||||
useState<ManualAddMode>('diffusers');
|
||||
|
||||
const [isCheckpoint, setIsCheckpoint] = useState(
|
||||
advancedAddScanModel &&
|
||||
['.ckpt', '.safetensors', '.pth', '.pt'].some((ext) =>
|
||||
advancedAddScanModel.endsWith(ext)
|
||||
)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
isCheckpoint
|
||||
? setAdvancedAddMode('checkpoint')
|
||||
: setAdvancedAddMode('diffusers');
|
||||
}, [setAdvancedAddMode, isCheckpoint]);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
return (
|
||||
advancedAddScanModel && (
|
||||
<Box
|
||||
as={motion.div}
|
||||
initial={{ x: -100, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1, transition: { duration: 0.2 } }}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minWidth: '40%',
|
||||
maxHeight: window.innerHeight - 300,
|
||||
overflow: 'scroll',
|
||||
p: 4,
|
||||
gap: 4,
|
||||
borderRadius: 4,
|
||||
bg: 'base.200',
|
||||
_dark: {
|
||||
bg: 'base.800',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<Text size="xl" fontWeight={600}>
|
||||
{isCheckpoint || advancedAddMode === 'checkpoint'
|
||||
? 'Add Checkpoint Model'
|
||||
: 'Add Diffusers Model'}
|
||||
</Text>
|
||||
<IAIIconButton
|
||||
icon={<FaTimes />}
|
||||
aria-label="Close Advanced"
|
||||
onClick={() => dispatch(setAdvancedAddScanModel(null))}
|
||||
size="sm"
|
||||
/>
|
||||
</Flex>
|
||||
<IAIMantineSelect
|
||||
label="Model Type"
|
||||
value={advancedAddMode}
|
||||
data={advancedAddModeData}
|
||||
onChange={(v) => {
|
||||
if (!v) return;
|
||||
setAdvancedAddMode(v as ManualAddMode);
|
||||
if (v === 'checkpoint') {
|
||||
setIsCheckpoint(true);
|
||||
} else {
|
||||
setIsCheckpoint(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{isCheckpoint ? (
|
||||
<AdvancedAddCheckpoint
|
||||
key={advancedAddScanModel}
|
||||
model_path={advancedAddScanModel}
|
||||
/>
|
||||
) : (
|
||||
<AdvancedAddDiffusers
|
||||
key={advancedAddScanModel}
|
||||
model_path={advancedAddScanModel}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
);
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import FoundModelsList from './FoundModelsList';
|
||||
import ScanAdvancedAddModels from './ScanAdvancedAddModels';
|
||||
import SearchFolderForm from './SearchFolderForm';
|
||||
|
||||
export default function ScanModels() {
|
||||
return (
|
||||
<Flex flexDirection="column" w="100%" gap={4}>
|
||||
<SearchFolderForm />
|
||||
<Flex gap={4}>
|
||||
<Flex
|
||||
sx={{
|
||||
maxHeight: window.innerHeight - 300,
|
||||
overflow: 'scroll',
|
||||
gap: 4,
|
||||
w: '100%',
|
||||
}}
|
||||
>
|
||||
<FoundModelsList />
|
||||
</Flex>
|
||||
<ScanAdvancedAddModels />
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import IAIInput from 'common/components/IAIInput';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaSearch, FaSync, FaTrash } from 'react-icons/fa';
|
||||
import { useGetModelsInFolderQuery } from 'services/api/endpoints/models';
|
||||
import {
|
||||
setAdvancedAddScanModel,
|
||||
setSearchFolder,
|
||||
} from '../../store/modelManagerSlice';
|
||||
|
||||
type SearchFolderForm = {
|
||||
folder: string;
|
||||
};
|
||||
|
||||
function SearchFolderForm() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const searchFolder = useAppSelector(
|
||||
(state: RootState) => state.modelmanager.searchFolder
|
||||
);
|
||||
|
||||
const { refetch: refetchFoundModels } = useGetModelsInFolderQuery({
|
||||
search_path: searchFolder ? searchFolder : '',
|
||||
});
|
||||
|
||||
const searchFolderForm = useForm<SearchFolderForm>({
|
||||
initialValues: {
|
||||
folder: '',
|
||||
},
|
||||
});
|
||||
|
||||
const searchFolderFormSubmitHandler = useCallback(
|
||||
(values: SearchFolderForm) => {
|
||||
dispatch(setSearchFolder(values.folder));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const scanAgainHandler = () => {
|
||||
refetchFoundModels();
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={searchFolderForm.onSubmit((values) =>
|
||||
searchFolderFormSubmitHandler(values)
|
||||
)}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
w: '100%',
|
||||
gap: 2,
|
||||
borderRadius: 4,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Flex w="100%" alignItems="center" gap={4} minH={12}>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
fontWeight: 600,
|
||||
color: 'base.700',
|
||||
minW: 'max-content',
|
||||
_dark: { color: 'base.300' },
|
||||
}}
|
||||
>
|
||||
Folder
|
||||
</Text>
|
||||
{!searchFolder ? (
|
||||
<IAIInput
|
||||
w="100%"
|
||||
size="md"
|
||||
{...searchFolderForm.getInputProps('folder')}
|
||||
/>
|
||||
) : (
|
||||
<Flex
|
||||
sx={{
|
||||
w: '100%',
|
||||
p: 2,
|
||||
px: 4,
|
||||
bg: 'base.300',
|
||||
borderRadius: 4,
|
||||
fontSize: 'sm',
|
||||
fontWeight: 'bold',
|
||||
_dark: { bg: 'base.700' },
|
||||
}}
|
||||
>
|
||||
{searchFolder}
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Flex gap={2}>
|
||||
{!searchFolder ? (
|
||||
<IAIIconButton
|
||||
aria-label={t('modelManager.findModels')}
|
||||
tooltip={t('modelManager.findModels')}
|
||||
icon={<FaSearch />}
|
||||
fontSize={18}
|
||||
size="sm"
|
||||
type="submit"
|
||||
/>
|
||||
) : (
|
||||
<IAIIconButton
|
||||
aria-label={t('modelManager.scanAgain')}
|
||||
tooltip={t('modelManager.scanAgain')}
|
||||
icon={<FaSync />}
|
||||
onClick={scanAgainHandler}
|
||||
fontSize={18}
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
|
||||
<IAIIconButton
|
||||
aria-label={t('modelManager.clearCheckpointFolder')}
|
||||
tooltip={t('modelManager.clearCheckpointFolder')}
|
||||
icon={<FaTrash />}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
dispatch(setSearchFolder(null));
|
||||
dispatch(setAdvancedAddScanModel(null));
|
||||
}}
|
||||
isDisabled={!searchFolder}
|
||||
colorScheme="red"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(SearchFolderForm);
|
@ -0,0 +1,108 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
// import { addNewModel } from 'app/socketio/actions';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SelectItem } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { makeToast } from 'app/components/Toaster';
|
||||
import { RootState } from 'app/store/store';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIMantineTextInput from 'common/components/IAIMantineInput';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useImportMainModelsMutation } from 'services/api/endpoints/models';
|
||||
|
||||
const predictionSelectData: SelectItem[] = [
|
||||
{ label: 'None', value: 'none' },
|
||||
{ label: 'v_prediction', value: 'v_prediction' },
|
||||
{ label: 'epsilon', value: 'epsilon' },
|
||||
{ label: 'sample', value: 'sample' },
|
||||
];
|
||||
|
||||
type ExtendedImportModelConfig = {
|
||||
location: string;
|
||||
prediction_type?: 'v_prediction' | 'epsilon' | 'sample' | 'none' | undefined;
|
||||
};
|
||||
|
||||
export default function SimpleAddModels() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isProcessing = useAppSelector(
|
||||
(state: RootState) => state.system.isProcessing
|
||||
);
|
||||
|
||||
const [importMainModel, { isLoading }] = useImportMainModelsMutation();
|
||||
|
||||
const addModelForm = useForm<ExtendedImportModelConfig>({
|
||||
initialValues: {
|
||||
location: '',
|
||||
prediction_type: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const handleAddModelSubmit = (values: ExtendedImportModelConfig) => {
|
||||
const importModelResponseBody = {
|
||||
location: values.location,
|
||||
prediction_type:
|
||||
values.prediction_type === 'none' ? undefined : values.prediction_type,
|
||||
};
|
||||
|
||||
importMainModel({ body: importModelResponseBody })
|
||||
.unwrap()
|
||||
.then((_) => {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: 'Model Added',
|
||||
status: 'success',
|
||||
})
|
||||
)
|
||||
);
|
||||
addModelForm.reset();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: `${error.data.detail} `,
|
||||
status: 'error',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={addModelForm.onSubmit((v) => handleAddModelSubmit(v))}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Flex flexDirection="column" width="100%" gap={4}>
|
||||
<IAIMantineTextInput
|
||||
label="Model Location"
|
||||
placeholder="Provide a path to a local Diffusers model, local checkpoint / safetensors model or a HuggingFace Repo ID"
|
||||
w="100%"
|
||||
{...addModelForm.getInputProps('location')}
|
||||
/>
|
||||
<IAIMantineSelect
|
||||
label="Prediction Type (for Stable Diffusion 2.x Models only)"
|
||||
data={predictionSelectData}
|
||||
defaultValue="none"
|
||||
{...addModelForm.getInputProps('prediction_type')}
|
||||
/>
|
||||
<IAIButton
|
||||
type="submit"
|
||||
isLoading={isLoading}
|
||||
isDisabled={isLoading || isProcessing}
|
||||
>
|
||||
{t('modelManager.addModel')}
|
||||
</IAIButton>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
import { ButtonGroup, Flex } from '@chakra-ui/react';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import AddModels from './AddModelsPanel/AddModels';
|
||||
import ScanModels from './AddModelsPanel/ScanModels';
|
||||
|
||||
type AddModelTabs = 'add' | 'scan';
|
||||
|
||||
export default function ImportModelsPanel() {
|
||||
const [addModelTab, setAddModelTab] = useState<AddModelTabs>('add');
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Flex flexDirection="column" gap={4}>
|
||||
<ButtonGroup isAttached>
|
||||
<IAIButton
|
||||
onClick={() => setAddModelTab('add')}
|
||||
isChecked={addModelTab == 'add'}
|
||||
size="sm"
|
||||
width="100%"
|
||||
>
|
||||
{t('modelManager.addModel')}
|
||||
</IAIButton>
|
||||
<IAIButton
|
||||
onClick={() => setAddModelTab('scan')}
|
||||
isChecked={addModelTab == 'scan'}
|
||||
size="sm"
|
||||
width="100%"
|
||||
>
|
||||
{t('modelManager.scanForModels')}
|
||||
</IAIButton>
|
||||
</ButtonGroup>
|
||||
|
||||
{addModelTab == 'add' && <AddModels />}
|
||||
{addModelTab == 'scan' && <ScanModels />}
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -1,11 +1,4 @@
|
||||
import {
|
||||
Flex,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Text,
|
||||
Tooltip,
|
||||
useColorMode,
|
||||
} from '@chakra-ui/react';
|
||||
import { Flex, Radio, RadioGroup, Text, Tooltip } from '@chakra-ui/react';
|
||||
import { makeToast } from 'app/components/Toaster';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
@ -23,7 +16,6 @@ import {
|
||||
useMergeMainModelsMutation,
|
||||
} from 'services/api/endpoints/models';
|
||||
import { BaseModelType, MergeModelConfig } from 'services/api/types';
|
||||
import { mode } from 'theme/util/mode';
|
||||
|
||||
const baseModelTypeSelectData = [
|
||||
{ label: 'Stable Diffusion 1', value: 'sd-1' },
|
||||
@ -38,7 +30,6 @@ type MergeInterpolationMethods =
|
||||
|
||||
export default function MergeModelsPanel() {
|
||||
const { t } = useTranslation();
|
||||
const { colorMode } = useColorMode();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { data } = useGetMainModelsQuery();
|
||||
@ -125,9 +116,9 @@ export default function MergeModelsPanel() {
|
||||
mergedModelName !== '' ? mergedModelName : models_names.join('-'),
|
||||
alpha: modelMergeAlpha,
|
||||
interp: modelMergeInterp,
|
||||
// model_merge_save_path:
|
||||
// modelMergeSaveLocType === 'root' ? null : modelMergeCustomSaveLoc,
|
||||
force: modelMergeForce,
|
||||
merge_dest_directory:
|
||||
modelMergeSaveLocType === 'root' ? undefined : modelMergeCustomSaveLoc,
|
||||
};
|
||||
|
||||
mergeModels({
|
||||
@ -227,7 +218,10 @@ export default function MergeModelsPanel() {
|
||||
padding: 4,
|
||||
borderRadius: 'base',
|
||||
gap: 4,
|
||||
bg: mode('base.100', 'base.800')(colorMode),
|
||||
bg: 'base.200',
|
||||
_dark: {
|
||||
bg: 'base.800',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<IAISlider
|
||||
@ -252,7 +246,10 @@ export default function MergeModelsPanel() {
|
||||
padding: 4,
|
||||
borderRadius: 'base',
|
||||
gap: 4,
|
||||
bg: mode('base.100', 'base.800')(colorMode),
|
||||
bg: 'base.200',
|
||||
_dark: {
|
||||
bg: 'base.800',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Text fontWeight={500} fontSize="sm" variant="subtext">
|
||||
@ -288,13 +285,16 @@ export default function MergeModelsPanel() {
|
||||
</RadioGroup>
|
||||
</Flex>
|
||||
|
||||
{/* <Flex
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
padding: 4,
|
||||
borderRadius: 'base',
|
||||
gap: 4,
|
||||
bg: 'base.200',
|
||||
_dark: {
|
||||
bg: 'base.900',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flex columnGap={4}>
|
||||
@ -324,7 +324,7 @@ export default function MergeModelsPanel() {
|
||||
onChange={(e) => setModelMergeCustomSaveLoc(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
</Flex> */}
|
||||
</Flex>
|
||||
|
||||
<IAISimpleCheckbox
|
||||
label={t('modelManager.ignoreMismatch')}
|
||||
|
@ -4,30 +4,23 @@ import { makeToast } from 'app/components/Toaster';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIMantineTextInput from 'common/components/IAIMantineInput';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
||||
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
CheckpointModelConfigEntity,
|
||||
useGetCheckpointConfigsQuery,
|
||||
useUpdateMainModelsMutation,
|
||||
} from 'services/api/endpoints/models';
|
||||
import { CheckpointModelConfig } from 'services/api/types';
|
||||
import BaseModelSelect from '../shared/BaseModelSelect';
|
||||
import CheckpointConfigsSelect from '../shared/CheckpointConfigsSelect';
|
||||
import ModelVariantSelect from '../shared/ModelVariantSelect';
|
||||
import ModelConvert from './ModelConvert';
|
||||
|
||||
const baseModelSelectData = [
|
||||
{ value: 'sd-1', label: MODEL_TYPE_MAP['sd-1'] },
|
||||
{ value: 'sd-2', label: MODEL_TYPE_MAP['sd-2'] },
|
||||
];
|
||||
|
||||
const variantSelectData = [
|
||||
{ value: 'normal', label: 'Normal' },
|
||||
{ value: 'inpaint', label: 'Inpaint' },
|
||||
{ value: 'depth', label: 'Depth' },
|
||||
];
|
||||
|
||||
type CheckpointModelEditProps = {
|
||||
model: CheckpointModelConfigEntity;
|
||||
};
|
||||
@ -38,6 +31,15 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
|
||||
const { model } = props;
|
||||
|
||||
const [updateMainModel, { isLoading }] = useUpdateMainModelsMutation();
|
||||
const { data: availableCheckpointConfigs } = useGetCheckpointConfigsQuery();
|
||||
|
||||
const [useCustomConfig, setUseCustomConfig] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!availableCheckpointConfigs?.includes(model.config)) {
|
||||
setUseCustomConfig(true);
|
||||
}
|
||||
}, [availableCheckpointConfigs, model.config]);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
@ -80,7 +82,7 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
|
||||
)
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch((_) => {
|
||||
checkpointEditForm.reset();
|
||||
dispatch(
|
||||
addToast(
|
||||
@ -128,21 +130,24 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
|
||||
)}
|
||||
>
|
||||
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
||||
<IAIMantineTextInput
|
||||
label={t('modelManager.name')}
|
||||
{...checkpointEditForm.getInputProps('model_name')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
label={t('modelManager.description')}
|
||||
{...checkpointEditForm.getInputProps('description')}
|
||||
/>
|
||||
<IAIMantineSelect
|
||||
label={t('modelManager.baseModel')}
|
||||
data={baseModelSelectData}
|
||||
<BaseModelSelect
|
||||
required
|
||||
{...checkpointEditForm.getInputProps('base_model')}
|
||||
/>
|
||||
<IAIMantineSelect
|
||||
label={t('modelManager.variant')}
|
||||
data={variantSelectData}
|
||||
<ModelVariantSelect
|
||||
required
|
||||
{...checkpointEditForm.getInputProps('variant')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
required
|
||||
label={t('modelManager.modelLocation')}
|
||||
{...checkpointEditForm.getInputProps('path')}
|
||||
/>
|
||||
@ -150,10 +155,27 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
|
||||
label={t('modelManager.vaeLocation')}
|
||||
{...checkpointEditForm.getInputProps('vae')}
|
||||
/>
|
||||
|
||||
<Flex flexDirection="column" gap={2}>
|
||||
{!useCustomConfig ? (
|
||||
<CheckpointConfigsSelect
|
||||
required
|
||||
{...checkpointEditForm.getInputProps('config')}
|
||||
/>
|
||||
) : (
|
||||
<IAIMantineTextInput
|
||||
required
|
||||
label={t('modelManager.config')}
|
||||
{...checkpointEditForm.getInputProps('config')}
|
||||
/>
|
||||
)}
|
||||
<IAISimpleCheckbox
|
||||
isChecked={useCustomConfig}
|
||||
onChange={() => setUseCustomConfig(!useCustomConfig)}
|
||||
label="Use Custom Config"
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<IAIButton
|
||||
type="submit"
|
||||
isDisabled={isBusy || isLoading}
|
||||
|
@ -4,7 +4,6 @@ import { makeToast } from 'app/components/Toaster';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIMantineTextInput from 'common/components/IAIMantineInput';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
||||
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
@ -15,22 +14,13 @@ import {
|
||||
useUpdateMainModelsMutation,
|
||||
} from 'services/api/endpoints/models';
|
||||
import { DiffusersModelConfig } from 'services/api/types';
|
||||
import BaseModelSelect from '../shared/BaseModelSelect';
|
||||
import ModelVariantSelect from '../shared/ModelVariantSelect';
|
||||
|
||||
type DiffusersModelEditProps = {
|
||||
model: DiffusersModelConfigEntity;
|
||||
};
|
||||
|
||||
const baseModelSelectData = [
|
||||
{ value: 'sd-1', label: MODEL_TYPE_MAP['sd-1'] },
|
||||
{ value: 'sd-2', label: MODEL_TYPE_MAP['sd-2'] },
|
||||
];
|
||||
|
||||
const variantSelectData = [
|
||||
{ value: 'normal', label: 'Normal' },
|
||||
{ value: 'inpaint', label: 'Inpaint' },
|
||||
{ value: 'depth', label: 'Depth' },
|
||||
];
|
||||
|
||||
export default function DiffusersModelEdit(props: DiffusersModelEditProps) {
|
||||
const isBusy = useAppSelector(selectIsBusy);
|
||||
|
||||
@ -65,6 +55,7 @@ export default function DiffusersModelEdit(props: DiffusersModelEditProps) {
|
||||
model_name: model.model_name,
|
||||
body: values,
|
||||
};
|
||||
|
||||
updateMainModel(responseBody)
|
||||
.unwrap()
|
||||
.then((payload) => {
|
||||
@ -78,7 +69,7 @@ export default function DiffusersModelEdit(props: DiffusersModelEditProps) {
|
||||
)
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch((_) => {
|
||||
diffusersEditForm.reset();
|
||||
dispatch(
|
||||
addToast(
|
||||
@ -118,21 +109,24 @@ export default function DiffusersModelEdit(props: DiffusersModelEditProps) {
|
||||
)}
|
||||
>
|
||||
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
||||
<IAIMantineTextInput
|
||||
label={t('modelManager.name')}
|
||||
{...diffusersEditForm.getInputProps('model_name')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
label={t('modelManager.description')}
|
||||
{...diffusersEditForm.getInputProps('description')}
|
||||
/>
|
||||
<IAIMantineSelect
|
||||
label={t('modelManager.baseModel')}
|
||||
data={baseModelSelectData}
|
||||
<BaseModelSelect
|
||||
required
|
||||
{...diffusersEditForm.getInputProps('base_model')}
|
||||
/>
|
||||
<IAIMantineSelect
|
||||
label={t('modelManager.variant')}
|
||||
data={variantSelectData}
|
||||
<ModelVariantSelect
|
||||
required
|
||||
{...diffusersEditForm.getInputProps('variant')}
|
||||
/>
|
||||
<IAIMantineTextInput
|
||||
required
|
||||
label={t('modelManager.modelLocation')}
|
||||
{...diffusersEditForm.getInputProps('path')}
|
||||
/>
|
||||
|
@ -1,9 +1,18 @@
|
||||
import { Flex, ListItem, Text, UnorderedList } from '@chakra-ui/react';
|
||||
// import { convertToDiffusers } from 'app/socketio/actions';
|
||||
import {
|
||||
Flex,
|
||||
ListItem,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Text,
|
||||
Tooltip,
|
||||
UnorderedList,
|
||||
} from '@chakra-ui/react';
|
||||
import { makeToast } from 'app/components/Toaster';
|
||||
// import { convertToDiffusers } from 'app/socketio/actions';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIInput from 'common/components/IAIInput';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -15,6 +24,8 @@ interface ModelConvertProps {
|
||||
model: CheckpointModelConfig;
|
||||
}
|
||||
|
||||
type SaveLocation = 'InvokeAIRoot' | 'Custom';
|
||||
|
||||
export default function ModelConvert(props: ModelConvertProps) {
|
||||
const { model } = props;
|
||||
|
||||
@ -23,22 +34,51 @@ export default function ModelConvert(props: ModelConvertProps) {
|
||||
|
||||
const [convertModel, { isLoading }] = useConvertMainModelsMutation();
|
||||
|
||||
const [saveLocation, setSaveLocation] = useState<string>('same');
|
||||
const [saveLocation, setSaveLocation] =
|
||||
useState<SaveLocation>('InvokeAIRoot');
|
||||
const [customSaveLocation, setCustomSaveLocation] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
setSaveLocation('same');
|
||||
setSaveLocation('InvokeAIRoot');
|
||||
}, [model]);
|
||||
|
||||
const modelConvertCancelHandler = () => {
|
||||
setSaveLocation('same');
|
||||
setSaveLocation('InvokeAIRoot');
|
||||
};
|
||||
|
||||
const modelConvertHandler = () => {
|
||||
const responseBody = {
|
||||
base_model: model.base_model,
|
||||
model_name: model.model_name,
|
||||
params: {
|
||||
convert_dest_directory:
|
||||
saveLocation === 'Custom' ? customSaveLocation : undefined,
|
||||
},
|
||||
};
|
||||
|
||||
if (saveLocation === 'Custom' && customSaveLocation === '') {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: t('modelManager.noCustomLocationProvided'),
|
||||
status: 'error',
|
||||
})
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: `${t('modelManager.convertingModelBegin')}: ${
|
||||
model.model_name
|
||||
}`,
|
||||
status: 'success',
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
convertModel(responseBody)
|
||||
.unwrap()
|
||||
.then((_) => {
|
||||
@ -94,35 +134,30 @@ export default function ModelConvert(props: ModelConvertProps) {
|
||||
<Text>{t('modelManager.convertToDiffusersHelpText6')}</Text>
|
||||
</Flex>
|
||||
|
||||
{/* <Flex flexDir="column" gap={4}>
|
||||
<Flex flexDir="column" gap={2}>
|
||||
<Flex marginTop={4} flexDir="column" gap={2}>
|
||||
<Text fontWeight="600">
|
||||
{t('modelManager.convertToDiffusersSaveLocation')}
|
||||
</Text>
|
||||
<RadioGroup value={saveLocation} onChange={(v) => setSaveLocation(v)}>
|
||||
<RadioGroup
|
||||
value={saveLocation}
|
||||
onChange={(v) => setSaveLocation(v as SaveLocation)}
|
||||
>
|
||||
<Flex gap={4}>
|
||||
<Radio value="same">
|
||||
<Tooltip label="Save converted model in the same folder">
|
||||
{t('modelManager.sameFolder')}
|
||||
</Tooltip>
|
||||
</Radio>
|
||||
|
||||
<Radio value="root">
|
||||
<Radio value="InvokeAIRoot">
|
||||
<Tooltip label="Save converted model in the InvokeAI root folder">
|
||||
{t('modelManager.invokeRoot')}
|
||||
</Tooltip>
|
||||
</Radio>
|
||||
|
||||
<Radio value="custom">
|
||||
<Radio value="Custom">
|
||||
<Tooltip label="Save converted model in a custom folder">
|
||||
{t('modelManager.custom')}
|
||||
</Tooltip>
|
||||
</Radio>
|
||||
</Flex>
|
||||
</RadioGroup>
|
||||
</Flex> */}
|
||||
|
||||
{/* {saveLocation === 'custom' && (
|
||||
</Flex>
|
||||
{saveLocation === 'Custom' && (
|
||||
<Flex flexDirection="column" rowGap={2}>
|
||||
<Text fontWeight="500" fontSize="sm" variant="subtext">
|
||||
{t('modelManager.customSaveLocation')}
|
||||
@ -130,13 +165,13 @@ export default function ModelConvert(props: ModelConvertProps) {
|
||||
<IAIInput
|
||||
value={customSaveLocation}
|
||||
onChange={(e) => {
|
||||
if (e.target.value !== '')
|
||||
setCustomSaveLocation(e.target.value);
|
||||
}}
|
||||
width="full"
|
||||
/>
|
||||
</Flex>
|
||||
)} */}
|
||||
)}
|
||||
</Flex>
|
||||
</IAIAlertDialog>
|
||||
);
|
||||
}
|
||||
|
@ -44,10 +44,6 @@ const ModelList = (props: ModelListProps) => {
|
||||
|
||||
return (
|
||||
<Flex flexDirection="column" rowGap={4} width="50%" minWidth="50%">
|
||||
<IAIInput
|
||||
onChange={handleSearchFilter}
|
||||
label={t('modelManager.search')}
|
||||
/>
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
gap={4}
|
||||
@ -79,6 +75,12 @@ const ModelList = (props: ModelListProps) => {
|
||||
</IAIButton>
|
||||
</ButtonGroup>
|
||||
|
||||
<IAIInput
|
||||
onChange={handleSearchFilter}
|
||||
label={t('modelManager.search')}
|
||||
labelPos="side"
|
||||
/>
|
||||
|
||||
{['all', 'diffusers'].includes(modelFormatFilter) &&
|
||||
filteredDiffusersModels.length > 0 && (
|
||||
<Flex sx={{ gap: 2, flexDir: 'column' }}>
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { DeleteIcon } from '@chakra-ui/icons';
|
||||
import { Flex, Text, Tooltip } from '@chakra-ui/react';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { makeToast } from 'app/components/Toaster';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@ -21,6 +23,7 @@ type ModelListItemProps = {
|
||||
export default function ModelListItem(props: ModelListItemProps) {
|
||||
const isBusy = useAppSelector(selectIsBusy);
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const [deleteMainModel] = useDeleteMainModelsMutation();
|
||||
|
||||
const { model, isSelected, setSelectedModelId } = props;
|
||||
@ -30,9 +33,34 @@ export default function ModelListItem(props: ModelListItemProps) {
|
||||
}, [model.id, setSelectedModelId]);
|
||||
|
||||
const handleModelDelete = useCallback(() => {
|
||||
deleteMainModel(model);
|
||||
deleteMainModel(model)
|
||||
.unwrap()
|
||||
.then((_) => {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: `${t('modelManager.modelDeleted')}: ${model.model_name}`,
|
||||
status: 'success',
|
||||
})
|
||||
)
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error) {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: `${t('modelManager.modelDeleteFailed')}: ${
|
||||
model.model_name
|
||||
}`,
|
||||
status: 'success',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
setSelectedModelId(undefined);
|
||||
}, [deleteMainModel, model, setSelectedModelId]);
|
||||
}, [deleteMainModel, model, setSelectedModelId, dispatch, t]);
|
||||
|
||||
return (
|
||||
<Flex sx={{ gap: 2, alignItems: 'center', w: 'full' }}>
|
||||
|
@ -0,0 +1,26 @@
|
||||
import IAIMantineSelect, {
|
||||
IAISelectDataType,
|
||||
IAISelectProps,
|
||||
} from 'common/components/IAIMantineSelect';
|
||||
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const baseModelSelectData: IAISelectDataType[] = [
|
||||
{ value: 'sd-1', label: MODEL_TYPE_MAP['sd-1'] },
|
||||
{ value: 'sd-2', label: MODEL_TYPE_MAP['sd-2'] },
|
||||
];
|
||||
|
||||
type BaseModelSelectProps = Omit<IAISelectProps, 'data'>;
|
||||
|
||||
export default function BaseModelSelect(props: BaseModelSelectProps) {
|
||||
const { ...rest } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAIMantineSelect
|
||||
label={t('modelManager.baseModel')}
|
||||
data={baseModelSelectData}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import IAIMantineSelect, {
|
||||
IAISelectProps,
|
||||
} from 'common/components/IAIMantineSelect';
|
||||
import { useGetCheckpointConfigsQuery } from 'services/api/endpoints/models';
|
||||
|
||||
type CheckpointConfigSelectProps = Omit<IAISelectProps, 'data'>;
|
||||
|
||||
export default function CheckpointConfigsSelect(
|
||||
props: CheckpointConfigSelectProps
|
||||
) {
|
||||
const { data: availableCheckpointConfigs } = useGetCheckpointConfigsQuery();
|
||||
const { ...rest } = props;
|
||||
|
||||
return (
|
||||
<IAIMantineSelect
|
||||
label="Config File"
|
||||
placeholder="Select A Config File"
|
||||
data={availableCheckpointConfigs ? availableCheckpointConfigs : []}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import IAIMantineSelect, {
|
||||
IAISelectDataType,
|
||||
IAISelectProps,
|
||||
} from 'common/components/IAIMantineSelect';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const variantSelectData: IAISelectDataType[] = [
|
||||
{ value: 'normal', label: 'Normal' },
|
||||
{ value: 'inpaint', label: 'Inpaint' },
|
||||
{ value: 'depth', label: 'Depth' },
|
||||
];
|
||||
|
||||
type VariantSelectProps = Omit<IAISelectProps, 'data'>;
|
||||
|
||||
export default function ModelVariantSelect(props: VariantSelectProps) {
|
||||
const { ...rest } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAIMantineSelect
|
||||
label={t('modelManager.variant')}
|
||||
data={variantSelectData}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
import { SchedulerParam } from 'features/parameters/types/parameterSchemas';
|
||||
|
||||
export type AddNewModelType = 'ckpt' | 'diffusers' | null;
|
||||
|
||||
export type Coordinates = {
|
||||
x: number;
|
||||
y: number;
|
||||
@ -22,7 +20,6 @@ export interface UIState {
|
||||
shouldUseCanvasBetaLayout: boolean;
|
||||
shouldShowExistingModelsInSearch: boolean;
|
||||
shouldUseSliders: boolean;
|
||||
addNewModelUIOption: AddNewModelType;
|
||||
shouldHidePreview: boolean;
|
||||
shouldPinGallery: boolean;
|
||||
shouldShowGallery: boolean;
|
||||
|
@ -5,7 +5,9 @@ import {
|
||||
BaseModelType,
|
||||
CheckpointModelConfig,
|
||||
ControlNetModelConfig,
|
||||
ConvertModelConfig,
|
||||
DiffusersModelConfig,
|
||||
ImportModelConfig,
|
||||
LoRAModelConfig,
|
||||
MainModelConfig,
|
||||
MergeModelConfig,
|
||||
@ -13,8 +15,9 @@ import {
|
||||
VaeModelConfig,
|
||||
} from 'services/api/types';
|
||||
|
||||
import queryString from 'query-string';
|
||||
import { ApiFullTagDescription, LIST_TAG, api } from '..';
|
||||
import { paths } from '../schema';
|
||||
import { operations, paths } from '../schema';
|
||||
|
||||
export type DiffusersModelConfigEntity = DiffusersModelConfig & { id: string };
|
||||
export type CheckpointModelConfigEntity = CheckpointModelConfig & {
|
||||
@ -62,6 +65,7 @@ type DeleteMainModelResponse = void;
|
||||
type ConvertMainModelArg = {
|
||||
base_model: BaseModelType;
|
||||
model_name: string;
|
||||
params: ConvertModelConfig;
|
||||
};
|
||||
|
||||
type ConvertMainModelResponse =
|
||||
@ -75,6 +79,28 @@ type MergeMainModelArg = {
|
||||
type MergeMainModelResponse =
|
||||
paths['/api/v1/models/merge/{base_model}']['put']['responses']['200']['content']['application/json'];
|
||||
|
||||
type ImportMainModelArg = {
|
||||
body: ImportModelConfig;
|
||||
};
|
||||
|
||||
type ImportMainModelResponse =
|
||||
paths['/api/v1/models/import']['post']['responses']['201']['content']['application/json'];
|
||||
|
||||
type AddMainModelArg = {
|
||||
body: MainModelConfig;
|
||||
};
|
||||
|
||||
type AddMainModelResponse =
|
||||
paths['/api/v1/models/add']['post']['responses']['201']['content']['application/json'];
|
||||
|
||||
export type SearchFolderResponse =
|
||||
paths['/api/v1/models/search']['get']['responses']['200']['content']['application/json'];
|
||||
|
||||
type CheckpointConfigsResponse =
|
||||
paths['/api/v1/models/ckpt_confs']['get']['responses']['200']['content']['application/json'];
|
||||
|
||||
type SearchFolderArg = operations['search_for_models']['parameters']['query'];
|
||||
|
||||
const mainModelsAdapter = createEntityAdapter<MainModelConfigEntity>({
|
||||
sortComparer: (a, b) => a.model_name.localeCompare(b.model_name),
|
||||
});
|
||||
@ -160,6 +186,29 @@ export const modelsApi = api.injectEndpoints({
|
||||
},
|
||||
invalidatesTags: [{ type: 'MainModel', id: LIST_TAG }],
|
||||
}),
|
||||
importMainModels: build.mutation<
|
||||
ImportMainModelResponse,
|
||||
ImportMainModelArg
|
||||
>({
|
||||
query: ({ body }) => {
|
||||
return {
|
||||
url: `models/import`,
|
||||
method: 'POST',
|
||||
body: body,
|
||||
};
|
||||
},
|
||||
invalidatesTags: [{ type: 'MainModel', id: LIST_TAG }],
|
||||
}),
|
||||
addMainModels: build.mutation<AddMainModelResponse, AddMainModelArg>({
|
||||
query: ({ body }) => {
|
||||
return {
|
||||
url: `models/add`,
|
||||
method: 'POST',
|
||||
body: body,
|
||||
};
|
||||
},
|
||||
invalidatesTags: [{ type: 'MainModel', id: LIST_TAG }],
|
||||
}),
|
||||
deleteMainModels: build.mutation<
|
||||
DeleteMainModelResponse,
|
||||
DeleteMainModelArg
|
||||
@ -176,10 +225,11 @@ export const modelsApi = api.injectEndpoints({
|
||||
ConvertMainModelResponse,
|
||||
ConvertMainModelArg
|
||||
>({
|
||||
query: ({ base_model, model_name }) => {
|
||||
query: ({ base_model, model_name, params }) => {
|
||||
return {
|
||||
url: `models/convert/${base_model}/main/${model_name}`,
|
||||
method: 'PUT',
|
||||
params: params,
|
||||
};
|
||||
},
|
||||
invalidatesTags: [{ type: 'MainModel', id: LIST_TAG }],
|
||||
@ -328,6 +378,36 @@ export const modelsApi = api.injectEndpoints({
|
||||
);
|
||||
},
|
||||
}),
|
||||
getModelsInFolder: build.query<SearchFolderResponse, SearchFolderArg>({
|
||||
query: (arg) => {
|
||||
const folderQueryStr = queryString.stringify(arg, {});
|
||||
return {
|
||||
url: `/models/search?${folderQueryStr}`,
|
||||
};
|
||||
},
|
||||
providesTags: (result, error, arg) => {
|
||||
const tags: ApiFullTagDescription[] = [
|
||||
{ type: 'ScannedModels', id: LIST_TAG },
|
||||
];
|
||||
|
||||
if (result) {
|
||||
tags.push(
|
||||
...result.map((id) => ({
|
||||
type: 'ScannedModels' as const,
|
||||
id,
|
||||
}))
|
||||
);
|
||||
}
|
||||
return tags;
|
||||
},
|
||||
}),
|
||||
getCheckpointConfigs: build.query<CheckpointConfigsResponse, void>({
|
||||
query: () => {
|
||||
return {
|
||||
url: `/models/ckpt_confs`,
|
||||
};
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -339,6 +419,10 @@ export const {
|
||||
useGetVaeModelsQuery,
|
||||
useUpdateMainModelsMutation,
|
||||
useDeleteMainModelsMutation,
|
||||
useImportMainModelsMutation,
|
||||
useAddMainModelsMutation,
|
||||
useConvertMainModelsMutation,
|
||||
useMergeMainModelsMutation,
|
||||
useGetModelsInFolderQuery,
|
||||
useGetCheckpointConfigsQuery,
|
||||
} = modelsApi;
|
||||
|
107
invokeai/frontend/web/src/services/api/schema.d.ts
vendored
107
invokeai/frontend/web/src/services/api/schema.d.ts
vendored
@ -84,7 +84,7 @@ export type paths = {
|
||||
delete: operations["del_model"];
|
||||
/**
|
||||
* Update Model
|
||||
* @description Add Model
|
||||
* @description Update model contents with a new config. If the model name or base fields are changed, then the model is renamed.
|
||||
*/
|
||||
patch: operations["update_model"];
|
||||
};
|
||||
@ -102,13 +102,6 @@ export type paths = {
|
||||
*/
|
||||
post: operations["add_model"];
|
||||
};
|
||||
"/api/v1/models/rename/{base_model}/{model_type}/{model_name}": {
|
||||
/**
|
||||
* Rename Model
|
||||
* @description Rename a model
|
||||
*/
|
||||
post: operations["rename_model"];
|
||||
};
|
||||
"/api/v1/models/convert/{base_model}/{model_type}/{model_name}": {
|
||||
/**
|
||||
* Convert Model
|
||||
@ -1226,7 +1219,7 @@ export type components = {
|
||||
* @description The nodes in this graph
|
||||
*/
|
||||
nodes?: {
|
||||
[key: string]: (components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["RealESRGANInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]) | undefined;
|
||||
[key: string]: (components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["RealESRGANInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]) | undefined;
|
||||
};
|
||||
/**
|
||||
* Edges
|
||||
@ -1269,7 +1262,7 @@ export type components = {
|
||||
* @description The results of node executions
|
||||
*/
|
||||
results: {
|
||||
[key: string]: (components["schemas"]["ImageOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["VaeLoaderOutput"] | components["schemas"]["MetadataAccumulatorOutput"] | components["schemas"]["PromptOutput"] | components["schemas"]["PromptCollectionOutput"] | components["schemas"]["CompelOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["IntOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["IntCollectionOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["CollectInvocationOutput"]) | undefined;
|
||||
[key: string]: (components["schemas"]["ImageOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["VaeLoaderOutput"] | components["schemas"]["MetadataAccumulatorOutput"] | components["schemas"]["IntCollectionOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["CompelOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["IntOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PromptOutput"] | components["schemas"]["PromptCollectionOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["CollectInvocationOutput"]) | undefined;
|
||||
};
|
||||
/**
|
||||
* Errors
|
||||
@ -4031,40 +4024,6 @@ export type components = {
|
||||
* @enum {string}
|
||||
*/
|
||||
ResourceOrigin: "internal" | "external";
|
||||
/**
|
||||
* RestoreFaceInvocation
|
||||
* @description Restores faces in an image.
|
||||
*/
|
||||
RestoreFaceInvocation: {
|
||||
/**
|
||||
* Id
|
||||
* @description The id of this node. Must be unique among all nodes.
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Is Intermediate
|
||||
* @description Whether or not this node is an intermediate node.
|
||||
* @default false
|
||||
*/
|
||||
is_intermediate?: boolean;
|
||||
/**
|
||||
* Type
|
||||
* @default restore_face
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "restore_face";
|
||||
/**
|
||||
* Image
|
||||
* @description The input image
|
||||
*/
|
||||
image?: components["schemas"]["ImageField"];
|
||||
/**
|
||||
* Strength
|
||||
* @description The strength of the restoration
|
||||
* @default 0.75
|
||||
*/
|
||||
strength?: number;
|
||||
};
|
||||
/**
|
||||
* ScaleLatentsInvocation
|
||||
* @description Scales latents by a given factor.
|
||||
@ -4653,18 +4612,18 @@ export type components = {
|
||||
*/
|
||||
image?: components["schemas"]["ImageField"];
|
||||
};
|
||||
/**
|
||||
* StableDiffusion2ModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
StableDiffusion2ModelFormat: "checkpoint" | "diffusers";
|
||||
/**
|
||||
* StableDiffusion1ModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
StableDiffusion1ModelFormat: "checkpoint" | "diffusers";
|
||||
/**
|
||||
* StableDiffusion2ModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
StableDiffusion2ModelFormat: "checkpoint" | "diffusers";
|
||||
};
|
||||
responses: never;
|
||||
parameters: never;
|
||||
@ -4775,7 +4734,7 @@ export type operations = {
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["RealESRGANInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"];
|
||||
"application/json": components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["RealESRGANInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
@ -4812,7 +4771,7 @@ export type operations = {
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["RealESRGANInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"];
|
||||
"application/json": components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["RealESRGANInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
@ -5061,7 +5020,7 @@ export type operations = {
|
||||
};
|
||||
/**
|
||||
* Update Model
|
||||
* @description Add Model
|
||||
* @description Update model contents with a new config. If the model name or base fields are changed, then the model is renamed.
|
||||
*/
|
||||
update_model: {
|
||||
parameters: {
|
||||
@ -5090,6 +5049,8 @@ export type operations = {
|
||||
400: never;
|
||||
/** @description The model could not be found */
|
||||
404: never;
|
||||
/** @description There is already a model corresponding to the new name */
|
||||
409: never;
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
content: {
|
||||
@ -5160,46 +5121,6 @@ export type operations = {
|
||||
424: never;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Rename Model
|
||||
* @description Rename a model
|
||||
*/
|
||||
rename_model: {
|
||||
parameters: {
|
||||
query?: {
|
||||
/** @description new model name */
|
||||
new_name?: string;
|
||||
/** @description new model base */
|
||||
new_base?: components["schemas"]["BaseModelType"];
|
||||
};
|
||||
path: {
|
||||
/** @description Base model */
|
||||
base_model: components["schemas"]["BaseModelType"];
|
||||
/** @description The type of model */
|
||||
model_type: components["schemas"]["ModelType"];
|
||||
/** @description current model name */
|
||||
model_name: string;
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description The model was renamed successfully */
|
||||
201: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["StableDiffusion1ModelCheckpointConfig"] | components["schemas"]["StableDiffusion1ModelDiffusersConfig"] | components["schemas"]["VaeModelConfig"] | components["schemas"]["LoRAModelConfig"] | components["schemas"]["ControlNetModelConfig"] | components["schemas"]["TextualInversionModelConfig"] | components["schemas"]["StableDiffusion2ModelCheckpointConfig"] | components["schemas"]["StableDiffusion2ModelDiffusersConfig"];
|
||||
};
|
||||
};
|
||||
/** @description The model could not be found */
|
||||
404: never;
|
||||
/** @description There is already a model corresponding to the new name */
|
||||
409: never;
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Convert Model
|
||||
* @description Convert a checkpoint model into a diffusers model, optionally saving to the indicated destination directory, or `models` if none.
|
||||
|
@ -58,7 +58,10 @@ export type AnyModelConfig =
|
||||
| ControlNetModelConfig
|
||||
| TextualInversionModelConfig
|
||||
| MainModelConfig;
|
||||
|
||||
export type MergeModelConfig = components['schemas']['Body_merge_models'];
|
||||
export type ConvertModelConfig = components['schemas']['Body_convert_model'];
|
||||
export type ImportModelConfig = components['schemas']['Body_import_model'];
|
||||
|
||||
// Graphs
|
||||
export type Graph = components['schemas']['Graph'];
|
||||
|
@ -16,7 +16,7 @@ const invokeAI = defineStyle((props) => {
|
||||
};
|
||||
|
||||
return {
|
||||
bg: mode('base.200', 'base.600')(props),
|
||||
bg: mode('base.250', 'base.600')(props),
|
||||
color: mode('base.850', 'base.100')(props),
|
||||
borderRadius: 'base',
|
||||
svg: {
|
||||
|
Loading…
Reference in New Issue
Block a user