mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Model Manager UI 3.0 (#3778)
This PR completely ports over the Model Manager to 3.0 -- all of the functionality has now been restored in addition to the following changes. - Model Manager now has been moved to its own tab on the left hand side. - Model Manager has three tabs - Model Manager, Import Models and Merge Models - The edit forms for the Models now allow the users to update the model name and the base model too along with other details. - Checkpoint Edit form now displays the available config files from InvokeAI and also allows users to supply their own custom config file. - Under Import Models you can directly add models or a scan a folder for your checkpoint files. - Adding models has two modes -- Simple and Advanced. - In Simple Mode, you just simply need to pass a path and InvokeAI will try to determine kind of model it is and fill up the rest of the details accordingly. This input lets you supply local paths to diffusers / local paths to checkpoints / huggingface repo ID's to download models / CivitAI links. - Simple Mode also allows you to download different models types like VAE's and Controlnet models and etc. Not just main models. - In cases where the auto detection system of InvokeAI fails to read a model correctly, you can take the manual approach and go to Advanced where you can configure your model while adding it exactly the way you want it. Both Diffusers and Checkpoint models now have their own custom forms. - Scan Models has been cleaned up. It will now only display the models that are not already installed to InvokeAI. And each item will have two options - Quick Add and Advanced .. replicating the Add Model behavior from above. - Scan Models now has a search bar for you to search through your scanned models. - Merge Models functionality has been restored. This is a wrap for this PR. **TODO: (Probably for 3.1)** - Add model management for model types such as VAE's and ControlNet Models - Replace the VAE slot on the edit forms with the installed VAE drop down + custom option
This commit is contained in:
commit
c3a7e35ad8
@ -63,20 +63,35 @@ async def update_model(
|
|||||||
) -> UpdateModelResponse:
|
) -> UpdateModelResponse:
|
||||||
""" Update model contents with a new config. If the model name or base fields are changed, then the model is renamed. """
|
""" 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
|
logger = ApiDependencies.invoker.services.logger
|
||||||
|
|
||||||
|
|
||||||
try:
|
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
|
# rename operation requested
|
||||||
if info.model_name != model_name or info.base_model != base_model:
|
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,
|
base_model = base_model,
|
||||||
model_type = model_type,
|
model_type = model_type,
|
||||||
model_name = model_name,
|
model_name = model_name,
|
||||||
new_name = info.model_name,
|
new_name = info.model_name,
|
||||||
new_base = info.base_model,
|
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}')
|
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
|
model_name = info.model_name
|
||||||
base_model = info.base_model
|
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(
|
ApiDependencies.invoker.services.model_manager.update_model(
|
||||||
model_name=model_name,
|
model_name=model_name,
|
||||||
|
@ -568,6 +568,9 @@ class ModelManager(object):
|
|||||||
model_type=cur_model_type,
|
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)
|
models.append(model_dict)
|
||||||
|
|
||||||
return models
|
return models
|
||||||
@ -635,6 +638,10 @@ class ModelManager(object):
|
|||||||
The returned dict has the same format as the dict returned by
|
The returned dict has the same format as the dict returned by
|
||||||
model_info().
|
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_class = MODEL_CLASSES[base_model][model_type]
|
||||||
model_config = model_class.create_config(**model_attributes)
|
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 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):
|
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)
|
move(old_path, new_path)
|
||||||
model_cfg.path = str(new_path.relative_to(self.app_config.root_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,
|
calc_model_size_by_data,
|
||||||
classproperty,
|
classproperty,
|
||||||
InvalidModelException,
|
InvalidModelException,
|
||||||
|
ModelNotFoundException,
|
||||||
)
|
)
|
||||||
from invokeai.app.services.config import InvokeAIAppConfig
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
from diffusers.utils import is_safetensors_available
|
from diffusers.utils import is_safetensors_available
|
||||||
|
@ -399,6 +399,8 @@
|
|||||||
"deleteModel": "Delete Model",
|
"deleteModel": "Delete Model",
|
||||||
"deleteConfig": "Delete Config",
|
"deleteConfig": "Delete Config",
|
||||||
"deleteMsg1": "Are you sure you want to delete this model from InvokeAI?",
|
"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.",
|
"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",
|
"formMessageDiffusersModelLocation": "Diffusers Model Location",
|
||||||
"formMessageDiffusersModelLocationDesc": "Please enter at least one.",
|
"formMessageDiffusersModelLocationDesc": "Please enter at least one.",
|
||||||
@ -408,11 +410,13 @@
|
|||||||
"convertToDiffusers": "Convert To Diffusers",
|
"convertToDiffusers": "Convert To Diffusers",
|
||||||
"convertToDiffusersHelpText1": "This model will be converted to the 🧨 Diffusers format.",
|
"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.",
|
"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.",
|
"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.",
|
"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?",
|
"convertToDiffusersHelpText6": "Do you wish to convert this model?",
|
||||||
"convertToDiffusersSaveLocation": "Save Location",
|
"convertToDiffusersSaveLocation": "Save Location",
|
||||||
|
"noCustomLocationProvided": "No Custom Location Provided",
|
||||||
|
"convertingModelBegin": "Converting Model. Please wait.",
|
||||||
"v1": "v1",
|
"v1": "v1",
|
||||||
"v2_base": "v2 (512px)",
|
"v2_base": "v2 (512px)",
|
||||||
"v2_768": "v2 (768px)",
|
"v2_768": "v2 (768px)",
|
||||||
@ -450,7 +454,8 @@
|
|||||||
"none": "none",
|
"none": "none",
|
||||||
"addDifference": "Add Difference",
|
"addDifference": "Add Difference",
|
||||||
"pickModelType": "Pick Model Type",
|
"pickModelType": "Pick Model Type",
|
||||||
"selectModel": "Select Model"
|
"selectModel": "Select Model",
|
||||||
|
"importModels": "Import Models"
|
||||||
},
|
},
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"general": "General",
|
"general": "General",
|
||||||
|
@ -21,6 +21,7 @@ import generationReducer from 'features/parameters/store/generationSlice';
|
|||||||
import postprocessingReducer from 'features/parameters/store/postprocessingSlice';
|
import postprocessingReducer from 'features/parameters/store/postprocessingSlice';
|
||||||
import configReducer from 'features/system/store/configSlice';
|
import configReducer from 'features/system/store/configSlice';
|
||||||
import systemReducer from 'features/system/store/systemSlice';
|
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 hotkeysReducer from 'features/ui/store/hotkeysSlice';
|
||||||
import uiReducer from 'features/ui/store/uiSlice';
|
import uiReducer from 'features/ui/store/uiSlice';
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ const allReducers = {
|
|||||||
dynamicPrompts: dynamicPromptsReducer,
|
dynamicPrompts: dynamicPromptsReducer,
|
||||||
imageDeletion: imageDeletionReducer,
|
imageDeletion: imageDeletionReducer,
|
||||||
lora: loraReducer,
|
lora: loraReducer,
|
||||||
|
modelmanager: modelmanagerReducer,
|
||||||
[api.reducerPath]: api.reducer,
|
[api.reducerPath]: api.reducer,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -67,6 +69,7 @@ const rememberedKeys: (keyof typeof allReducers)[] = [
|
|||||||
'controlNet',
|
'controlNet',
|
||||||
'dynamicPrompts',
|
'dynamicPrompts',
|
||||||
'lora',
|
'lora',
|
||||||
|
'modelmanager',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
|
@ -8,19 +8,34 @@ import {
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { stopPastePropagation } from 'common/util/stopPastePropagation';
|
import { stopPastePropagation } from 'common/util/stopPastePropagation';
|
||||||
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
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 {
|
interface IAIInputProps extends InputProps {
|
||||||
label?: string;
|
label?: string;
|
||||||
|
labelPos?: 'top' | 'side';
|
||||||
value?: string;
|
value?: string;
|
||||||
size?: string;
|
size?: string;
|
||||||
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||||
formControlProps?: Omit<FormControlProps, 'isInvalid' | 'isDisabled'>;
|
formControlProps?: Omit<FormControlProps, 'isInvalid' | 'isDisabled'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const labelPosVerticalStyle: CSSProperties = {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 10,
|
||||||
|
};
|
||||||
|
|
||||||
const IAIInput = (props: IAIInputProps) => {
|
const IAIInput = (props: IAIInputProps) => {
|
||||||
const {
|
const {
|
||||||
label = '',
|
label = '',
|
||||||
|
labelPos = 'top',
|
||||||
isDisabled = false,
|
isDisabled = false,
|
||||||
isInvalid,
|
isInvalid,
|
||||||
formControlProps,
|
formControlProps,
|
||||||
@ -51,6 +66,7 @@ const IAIInput = (props: IAIInputProps) => {
|
|||||||
isInvalid={isInvalid}
|
isInvalid={isInvalid}
|
||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
{...formControlProps}
|
{...formControlProps}
|
||||||
|
style={labelPos === 'side' ? labelPosVerticalStyle : undefined}
|
||||||
>
|
>
|
||||||
{label !== '' && <FormLabel>{label}</FormLabel>}
|
{label !== '' && <FormLabel>{label}</FormLabel>}
|
||||||
<Input
|
<Input
|
||||||
|
@ -36,6 +36,7 @@ export default function IAIMantineTextInput(props: IAIMantineTextInputProps) {
|
|||||||
label: {
|
label: {
|
||||||
color: mode(base700, base300)(colorMode),
|
color: mode(base700, base300)(colorMode),
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
|
marginBottom: 4,
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
@ -9,14 +9,14 @@ export type IAISelectDataType = {
|
|||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IAISelectProps = Omit<SelectProps, 'label'> & {
|
export type IAISelectProps = Omit<SelectProps, 'label'> & {
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
inputRef?: RefObject<HTMLInputElement>;
|
inputRef?: RefObject<HTMLInputElement>;
|
||||||
label?: string;
|
label?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const IAIMantineSelect = (props: IAISelectProps) => {
|
const IAIMantineSelect = (props: IAISelectProps) => {
|
||||||
const { tooltip, inputRef, label, disabled, ...rest } = props;
|
const { tooltip, inputRef, label, disabled, required, ...rest } = props;
|
||||||
|
|
||||||
const styles = useMantineSelectStyles();
|
const styles = useMantineSelectStyles();
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ const IAIMantineSelect = (props: IAISelectProps) => {
|
|||||||
<Select
|
<Select
|
||||||
label={
|
label={
|
||||||
label ? (
|
label ? (
|
||||||
<FormControl isDisabled={disabled}>
|
<FormControl isRequired={required} isDisabled={disabled}>
|
||||||
<FormLabel>{label}</FormLabel>
|
<FormLabel>{label}</FormLabel>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
) : undefined
|
) : undefined
|
||||||
|
@ -16,14 +16,13 @@ import {
|
|||||||
ASSETS_CATEGORIES,
|
ASSETS_CATEGORIES,
|
||||||
IMAGE_CATEGORIES,
|
IMAGE_CATEGORIES,
|
||||||
IMAGE_LIMIT,
|
IMAGE_LIMIT,
|
||||||
selectImagesAll,
|
|
||||||
} from 'features/gallery//store/gallerySlice';
|
} from 'features/gallery//store/gallerySlice';
|
||||||
import { selectFilteredImages } from 'features/gallery/store/gallerySelectors';
|
import { selectFilteredImages } from 'features/gallery/store/gallerySelectors';
|
||||||
import { VirtuosoGrid } from 'react-virtuoso';
|
import { VirtuosoGrid } from 'react-virtuoso';
|
||||||
import { receivedPageOfImages } from 'services/api/thunks/image';
|
import { receivedPageOfImages } from 'services/api/thunks/image';
|
||||||
|
import { useListBoardImagesQuery } from '../../../../services/api/endpoints/boardImages';
|
||||||
import ImageGridItemContainer from './ImageGridItemContainer';
|
import ImageGridItemContainer from './ImageGridItemContainer';
|
||||||
import ImageGridListContainer from './ImageGridListContainer';
|
import ImageGridListContainer from './ImageGridListContainer';
|
||||||
import { useListBoardImagesQuery } from '../../../../services/api/endpoints/boardImages';
|
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
[stateSelector, selectFilteredImages],
|
[stateSelector, selectFilteredImages],
|
||||||
@ -180,7 +179,6 @@ const GalleryImageGrid = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
console.log({ selectedBoardId });
|
|
||||||
|
|
||||||
if (status !== 'rejected') {
|
if (status !== 'rejected') {
|
||||||
return (
|
return (
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { UseToastOptions } from '@chakra-ui/react';
|
import { UseToastOptions } from '@chakra-ui/react';
|
||||||
import { PayloadAction, createSlice } from '@reduxjs/toolkit';
|
import { PayloadAction, createSlice } from '@reduxjs/toolkit';
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
|
||||||
|
|
||||||
import { InvokeLogLevel } from 'app/logging/useLogger';
|
import { InvokeLogLevel } from 'app/logging/useLogger';
|
||||||
import { userInvoked } from 'app/store/actions';
|
import { userInvoked } from 'app/store/actions';
|
||||||
import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice';
|
import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice';
|
||||||
import { TFuncKey, t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { LogLevelName } from 'roarr';
|
import { LogLevelName } from 'roarr';
|
||||||
import { imageUploaded } from 'services/api/thunks/image';
|
import { imageUploaded } from 'services/api/thunks/image';
|
||||||
import {
|
import {
|
||||||
@ -44,8 +43,6 @@ export interface SystemState {
|
|||||||
isCancelable: boolean;
|
isCancelable: boolean;
|
||||||
enableImageDebugging: boolean;
|
enableImageDebugging: boolean;
|
||||||
toastQueue: UseToastOptions[];
|
toastQueue: UseToastOptions[];
|
||||||
searchFolder: string | null;
|
|
||||||
foundModels: InvokeAI.FoundModel[] | null;
|
|
||||||
/**
|
/**
|
||||||
* The current progress image
|
* The current progress image
|
||||||
*/
|
*/
|
||||||
@ -79,7 +76,7 @@ export interface SystemState {
|
|||||||
*/
|
*/
|
||||||
consoleLogLevel: InvokeLogLevel;
|
consoleLogLevel: InvokeLogLevel;
|
||||||
shouldLogToConsole: boolean;
|
shouldLogToConsole: boolean;
|
||||||
statusTranslationKey: TFuncKey;
|
statusTranslationKey: any;
|
||||||
/**
|
/**
|
||||||
* When a session is canceled, its ID is stored here until a new session is created.
|
* 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,
|
isCancelable: true,
|
||||||
enableImageDebugging: false,
|
enableImageDebugging: false,
|
||||||
toastQueue: [],
|
toastQueue: [],
|
||||||
searchFolder: null,
|
|
||||||
foundModels: null,
|
|
||||||
progressImage: null,
|
progressImage: null,
|
||||||
shouldAntialiasProgressImage: false,
|
shouldAntialiasProgressImage: false,
|
||||||
sessionId: null,
|
sessionId: null,
|
||||||
@ -132,7 +127,7 @@ export const systemSlice = createSlice({
|
|||||||
setIsProcessing: (state, action: PayloadAction<boolean>) => {
|
setIsProcessing: (state, action: PayloadAction<boolean>) => {
|
||||||
state.isProcessing = action.payload;
|
state.isProcessing = action.payload;
|
||||||
},
|
},
|
||||||
setCurrentStatus: (state, action: PayloadAction<TFuncKey>) => {
|
setCurrentStatus: (state, action: any) => {
|
||||||
state.statusTranslationKey = action.payload;
|
state.statusTranslationKey = action.payload;
|
||||||
},
|
},
|
||||||
setShouldConfirmOnDelete: (state, action: PayloadAction<boolean>) => {
|
setShouldConfirmOnDelete: (state, action: PayloadAction<boolean>) => {
|
||||||
@ -153,15 +148,6 @@ export const systemSlice = createSlice({
|
|||||||
clearToastQueue: (state) => {
|
clearToastQueue: (state) => {
|
||||||
state.toastQueue = [];
|
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
|
* A cancel was scheduled
|
||||||
*/
|
*/
|
||||||
@ -426,8 +412,6 @@ export const {
|
|||||||
setEnableImageDebugging,
|
setEnableImageDebugging,
|
||||||
addToast,
|
addToast,
|
||||||
clearToastQueue,
|
clearToastQueue,
|
||||||
setSearchFolder,
|
|
||||||
setFoundModels,
|
|
||||||
cancelScheduled,
|
cancelScheduled,
|
||||||
scheduledCancelAborted,
|
scheduledCancelAborted,
|
||||||
cancelTypeChanged,
|
cancelTypeChanged,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react';
|
import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react';
|
||||||
import i18n from 'i18n';
|
import i18n from 'i18n';
|
||||||
import { ReactNode, memo } from 'react';
|
import { ReactNode, memo } from 'react';
|
||||||
import AddModelsPanel from './subpanels/AddModelsPanel';
|
import ImportModelsPanel from './subpanels/ImportModelsPanel';
|
||||||
import MergeModelsPanel from './subpanels/MergeModelsPanel';
|
import MergeModelsPanel from './subpanels/MergeModelsPanel';
|
||||||
import ModelManagerPanel from './subpanels/ModelManagerPanel';
|
import ModelManagerPanel from './subpanels/ModelManagerPanel';
|
||||||
|
|
||||||
type ModelManagerTabName = 'modelManager' | 'addModels' | 'mergeModels';
|
type ModelManagerTabName = 'modelManager' | 'importModels' | 'mergeModels';
|
||||||
|
|
||||||
type ModelManagerTabInfo = {
|
type ModelManagerTabInfo = {
|
||||||
id: ModelManagerTabName;
|
id: ModelManagerTabName;
|
||||||
@ -20,9 +20,9 @@ const tabs: ModelManagerTabInfo[] = [
|
|||||||
content: <ModelManagerPanel />,
|
content: <ModelManagerPanel />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'addModels',
|
id: 'importModels',
|
||||||
label: i18n.t('modelManager.addModel'),
|
label: i18n.t('modelManager.importModels'),
|
||||||
content: <AddModelsPanel />,
|
content: <ImportModelsPanel />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'mergeModels',
|
id: 'mergeModels',
|
||||||
@ -46,7 +46,7 @@ const ModelManagerTab = () => {
|
|||||||
</Tab>
|
</Tab>
|
||||||
))}
|
))}
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanels sx={{ w: 'full', h: 'full', p: 4 }}>
|
<TabPanels sx={{ w: 'full', h: 'full' }}>
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<TabPanel sx={{ w: 'full', h: 'full' }} key={tab.id}>
|
<TabPanel sx={{ w: 'full', h: 'full' }} key={tab.id}>
|
||||||
{tab.content}
|
{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 {
|
import { Flex, Radio, RadioGroup, Text, Tooltip } from '@chakra-ui/react';
|
||||||
Flex,
|
|
||||||
Radio,
|
|
||||||
RadioGroup,
|
|
||||||
Text,
|
|
||||||
Tooltip,
|
|
||||||
useColorMode,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { makeToast } from 'app/components/Toaster';
|
import { makeToast } from 'app/components/Toaster';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
@ -23,7 +16,6 @@ import {
|
|||||||
useMergeMainModelsMutation,
|
useMergeMainModelsMutation,
|
||||||
} from 'services/api/endpoints/models';
|
} from 'services/api/endpoints/models';
|
||||||
import { BaseModelType, MergeModelConfig } from 'services/api/types';
|
import { BaseModelType, MergeModelConfig } from 'services/api/types';
|
||||||
import { mode } from 'theme/util/mode';
|
|
||||||
|
|
||||||
const baseModelTypeSelectData = [
|
const baseModelTypeSelectData = [
|
||||||
{ label: 'Stable Diffusion 1', value: 'sd-1' },
|
{ label: 'Stable Diffusion 1', value: 'sd-1' },
|
||||||
@ -38,7 +30,6 @@ type MergeInterpolationMethods =
|
|||||||
|
|
||||||
export default function MergeModelsPanel() {
|
export default function MergeModelsPanel() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { colorMode } = useColorMode();
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { data } = useGetMainModelsQuery();
|
const { data } = useGetMainModelsQuery();
|
||||||
@ -125,9 +116,9 @@ export default function MergeModelsPanel() {
|
|||||||
mergedModelName !== '' ? mergedModelName : models_names.join('-'),
|
mergedModelName !== '' ? mergedModelName : models_names.join('-'),
|
||||||
alpha: modelMergeAlpha,
|
alpha: modelMergeAlpha,
|
||||||
interp: modelMergeInterp,
|
interp: modelMergeInterp,
|
||||||
// model_merge_save_path:
|
|
||||||
// modelMergeSaveLocType === 'root' ? null : modelMergeCustomSaveLoc,
|
|
||||||
force: modelMergeForce,
|
force: modelMergeForce,
|
||||||
|
merge_dest_directory:
|
||||||
|
modelMergeSaveLocType === 'root' ? undefined : modelMergeCustomSaveLoc,
|
||||||
};
|
};
|
||||||
|
|
||||||
mergeModels({
|
mergeModels({
|
||||||
@ -227,7 +218,10 @@ export default function MergeModelsPanel() {
|
|||||||
padding: 4,
|
padding: 4,
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
gap: 4,
|
gap: 4,
|
||||||
bg: mode('base.100', 'base.800')(colorMode),
|
bg: 'base.200',
|
||||||
|
_dark: {
|
||||||
|
bg: 'base.800',
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IAISlider
|
<IAISlider
|
||||||
@ -252,7 +246,10 @@ export default function MergeModelsPanel() {
|
|||||||
padding: 4,
|
padding: 4,
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
gap: 4,
|
gap: 4,
|
||||||
bg: mode('base.100', 'base.800')(colorMode),
|
bg: 'base.200',
|
||||||
|
_dark: {
|
||||||
|
bg: 'base.800',
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text fontWeight={500} fontSize="sm" variant="subtext">
|
<Text fontWeight={500} fontSize="sm" variant="subtext">
|
||||||
@ -288,13 +285,16 @@ export default function MergeModelsPanel() {
|
|||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* <Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
padding: 4,
|
padding: 4,
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
gap: 4,
|
gap: 4,
|
||||||
bg: 'base.900',
|
bg: 'base.200',
|
||||||
|
_dark: {
|
||||||
|
bg: 'base.900',
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex columnGap={4}>
|
<Flex columnGap={4}>
|
||||||
@ -324,7 +324,7 @@ export default function MergeModelsPanel() {
|
|||||||
onChange={(e) => setModelMergeCustomSaveLoc(e.target.value)}
|
onChange={(e) => setModelMergeCustomSaveLoc(e.target.value)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Flex> */}
|
</Flex>
|
||||||
|
|
||||||
<IAISimpleCheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('modelManager.ignoreMismatch')}
|
label={t('modelManager.ignoreMismatch')}
|
||||||
|
@ -4,30 +4,23 @@ import { makeToast } from 'app/components/Toaster';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAIMantineTextInput from 'common/components/IAIMantineInput';
|
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 { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
||||||
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { useCallback } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
CheckpointModelConfigEntity,
|
CheckpointModelConfigEntity,
|
||||||
|
useGetCheckpointConfigsQuery,
|
||||||
useUpdateMainModelsMutation,
|
useUpdateMainModelsMutation,
|
||||||
} from 'services/api/endpoints/models';
|
} from 'services/api/endpoints/models';
|
||||||
import { CheckpointModelConfig } from 'services/api/types';
|
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';
|
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 = {
|
type CheckpointModelEditProps = {
|
||||||
model: CheckpointModelConfigEntity;
|
model: CheckpointModelConfigEntity;
|
||||||
};
|
};
|
||||||
@ -38,6 +31,15 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
|
|||||||
const { model } = props;
|
const { model } = props;
|
||||||
|
|
||||||
const [updateMainModel, { isLoading }] = useUpdateMainModelsMutation();
|
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 dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -80,7 +82,7 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((_) => {
|
||||||
checkpointEditForm.reset();
|
checkpointEditForm.reset();
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
@ -128,21 +130,24 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
||||||
|
<IAIMantineTextInput
|
||||||
|
label={t('modelManager.name')}
|
||||||
|
{...checkpointEditForm.getInputProps('model_name')}
|
||||||
|
/>
|
||||||
<IAIMantineTextInput
|
<IAIMantineTextInput
|
||||||
label={t('modelManager.description')}
|
label={t('modelManager.description')}
|
||||||
{...checkpointEditForm.getInputProps('description')}
|
{...checkpointEditForm.getInputProps('description')}
|
||||||
/>
|
/>
|
||||||
<IAIMantineSelect
|
<BaseModelSelect
|
||||||
label={t('modelManager.baseModel')}
|
required
|
||||||
data={baseModelSelectData}
|
|
||||||
{...checkpointEditForm.getInputProps('base_model')}
|
{...checkpointEditForm.getInputProps('base_model')}
|
||||||
/>
|
/>
|
||||||
<IAIMantineSelect
|
<ModelVariantSelect
|
||||||
label={t('modelManager.variant')}
|
required
|
||||||
data={variantSelectData}
|
|
||||||
{...checkpointEditForm.getInputProps('variant')}
|
{...checkpointEditForm.getInputProps('variant')}
|
||||||
/>
|
/>
|
||||||
<IAIMantineTextInput
|
<IAIMantineTextInput
|
||||||
|
required
|
||||||
label={t('modelManager.modelLocation')}
|
label={t('modelManager.modelLocation')}
|
||||||
{...checkpointEditForm.getInputProps('path')}
|
{...checkpointEditForm.getInputProps('path')}
|
||||||
/>
|
/>
|
||||||
@ -150,10 +155,27 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
|
|||||||
label={t('modelManager.vaeLocation')}
|
label={t('modelManager.vaeLocation')}
|
||||||
{...checkpointEditForm.getInputProps('vae')}
|
{...checkpointEditForm.getInputProps('vae')}
|
||||||
/>
|
/>
|
||||||
<IAIMantineTextInput
|
|
||||||
label={t('modelManager.config')}
|
<Flex flexDirection="column" gap={2}>
|
||||||
{...checkpointEditForm.getInputProps('config')}
|
{!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
|
<IAIButton
|
||||||
type="submit"
|
type="submit"
|
||||||
isDisabled={isBusy || isLoading}
|
isDisabled={isBusy || isLoading}
|
||||||
|
@ -4,7 +4,6 @@ import { makeToast } from 'app/components/Toaster';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAIMantineTextInput from 'common/components/IAIMantineInput';
|
import IAIMantineTextInput from 'common/components/IAIMantineInput';
|
||||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
|
||||||
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
||||||
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
@ -15,22 +14,13 @@ import {
|
|||||||
useUpdateMainModelsMutation,
|
useUpdateMainModelsMutation,
|
||||||
} from 'services/api/endpoints/models';
|
} from 'services/api/endpoints/models';
|
||||||
import { DiffusersModelConfig } from 'services/api/types';
|
import { DiffusersModelConfig } from 'services/api/types';
|
||||||
|
import BaseModelSelect from '../shared/BaseModelSelect';
|
||||||
|
import ModelVariantSelect from '../shared/ModelVariantSelect';
|
||||||
|
|
||||||
type DiffusersModelEditProps = {
|
type DiffusersModelEditProps = {
|
||||||
model: DiffusersModelConfigEntity;
|
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) {
|
export default function DiffusersModelEdit(props: DiffusersModelEditProps) {
|
||||||
const isBusy = useAppSelector(selectIsBusy);
|
const isBusy = useAppSelector(selectIsBusy);
|
||||||
|
|
||||||
@ -65,6 +55,7 @@ export default function DiffusersModelEdit(props: DiffusersModelEditProps) {
|
|||||||
model_name: model.model_name,
|
model_name: model.model_name,
|
||||||
body: values,
|
body: values,
|
||||||
};
|
};
|
||||||
|
|
||||||
updateMainModel(responseBody)
|
updateMainModel(responseBody)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.then((payload) => {
|
.then((payload) => {
|
||||||
@ -78,7 +69,7 @@ export default function DiffusersModelEdit(props: DiffusersModelEditProps) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((_) => {
|
||||||
diffusersEditForm.reset();
|
diffusersEditForm.reset();
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
@ -118,21 +109,24 @@ export default function DiffusersModelEdit(props: DiffusersModelEditProps) {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
||||||
|
<IAIMantineTextInput
|
||||||
|
label={t('modelManager.name')}
|
||||||
|
{...diffusersEditForm.getInputProps('model_name')}
|
||||||
|
/>
|
||||||
<IAIMantineTextInput
|
<IAIMantineTextInput
|
||||||
label={t('modelManager.description')}
|
label={t('modelManager.description')}
|
||||||
{...diffusersEditForm.getInputProps('description')}
|
{...diffusersEditForm.getInputProps('description')}
|
||||||
/>
|
/>
|
||||||
<IAIMantineSelect
|
<BaseModelSelect
|
||||||
label={t('modelManager.baseModel')}
|
required
|
||||||
data={baseModelSelectData}
|
|
||||||
{...diffusersEditForm.getInputProps('base_model')}
|
{...diffusersEditForm.getInputProps('base_model')}
|
||||||
/>
|
/>
|
||||||
<IAIMantineSelect
|
<ModelVariantSelect
|
||||||
label={t('modelManager.variant')}
|
required
|
||||||
data={variantSelectData}
|
|
||||||
{...diffusersEditForm.getInputProps('variant')}
|
{...diffusersEditForm.getInputProps('variant')}
|
||||||
/>
|
/>
|
||||||
<IAIMantineTextInput
|
<IAIMantineTextInput
|
||||||
|
required
|
||||||
label={t('modelManager.modelLocation')}
|
label={t('modelManager.modelLocation')}
|
||||||
{...diffusersEditForm.getInputProps('path')}
|
{...diffusersEditForm.getInputProps('path')}
|
||||||
/>
|
/>
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
import { Flex, ListItem, Text, UnorderedList } from '@chakra-ui/react';
|
import {
|
||||||
// import { convertToDiffusers } from 'app/socketio/actions';
|
Flex,
|
||||||
|
ListItem,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
Text,
|
||||||
|
Tooltip,
|
||||||
|
UnorderedList,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { makeToast } from 'app/components/Toaster';
|
import { makeToast } from 'app/components/Toaster';
|
||||||
|
// import { convertToDiffusers } from 'app/socketio/actions';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
|
import IAIInput from 'common/components/IAIInput';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -15,6 +24,8 @@ interface ModelConvertProps {
|
|||||||
model: CheckpointModelConfig;
|
model: CheckpointModelConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SaveLocation = 'InvokeAIRoot' | 'Custom';
|
||||||
|
|
||||||
export default function ModelConvert(props: ModelConvertProps) {
|
export default function ModelConvert(props: ModelConvertProps) {
|
||||||
const { model } = props;
|
const { model } = props;
|
||||||
|
|
||||||
@ -23,22 +34,51 @@ export default function ModelConvert(props: ModelConvertProps) {
|
|||||||
|
|
||||||
const [convertModel, { isLoading }] = useConvertMainModelsMutation();
|
const [convertModel, { isLoading }] = useConvertMainModelsMutation();
|
||||||
|
|
||||||
const [saveLocation, setSaveLocation] = useState<string>('same');
|
const [saveLocation, setSaveLocation] =
|
||||||
|
useState<SaveLocation>('InvokeAIRoot');
|
||||||
const [customSaveLocation, setCustomSaveLocation] = useState<string>('');
|
const [customSaveLocation, setCustomSaveLocation] = useState<string>('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSaveLocation('same');
|
setSaveLocation('InvokeAIRoot');
|
||||||
}, [model]);
|
}, [model]);
|
||||||
|
|
||||||
const modelConvertCancelHandler = () => {
|
const modelConvertCancelHandler = () => {
|
||||||
setSaveLocation('same');
|
setSaveLocation('InvokeAIRoot');
|
||||||
};
|
};
|
||||||
|
|
||||||
const modelConvertHandler = () => {
|
const modelConvertHandler = () => {
|
||||||
const responseBody = {
|
const responseBody = {
|
||||||
base_model: model.base_model,
|
base_model: model.base_model,
|
||||||
model_name: model.model_name,
|
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)
|
convertModel(responseBody)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
@ -94,35 +134,30 @@ export default function ModelConvert(props: ModelConvertProps) {
|
|||||||
<Text>{t('modelManager.convertToDiffusersHelpText6')}</Text>
|
<Text>{t('modelManager.convertToDiffusersHelpText6')}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* <Flex flexDir="column" gap={4}>
|
<Flex flexDir="column" gap={2}>
|
||||||
<Flex marginTop={4} flexDir="column" gap={2}>
|
<Flex marginTop={4} flexDir="column" gap={2}>
|
||||||
<Text fontWeight="600">
|
<Text fontWeight="600">
|
||||||
{t('modelManager.convertToDiffusersSaveLocation')}
|
{t('modelManager.convertToDiffusersSaveLocation')}
|
||||||
</Text>
|
</Text>
|
||||||
<RadioGroup value={saveLocation} onChange={(v) => setSaveLocation(v)}>
|
<RadioGroup
|
||||||
|
value={saveLocation}
|
||||||
|
onChange={(v) => setSaveLocation(v as SaveLocation)}
|
||||||
|
>
|
||||||
<Flex gap={4}>
|
<Flex gap={4}>
|
||||||
<Radio value="same">
|
<Radio value="InvokeAIRoot">
|
||||||
<Tooltip label="Save converted model in the same folder">
|
|
||||||
{t('modelManager.sameFolder')}
|
|
||||||
</Tooltip>
|
|
||||||
</Radio>
|
|
||||||
|
|
||||||
<Radio value="root">
|
|
||||||
<Tooltip label="Save converted model in the InvokeAI root folder">
|
<Tooltip label="Save converted model in the InvokeAI root folder">
|
||||||
{t('modelManager.invokeRoot')}
|
{t('modelManager.invokeRoot')}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Radio>
|
</Radio>
|
||||||
|
<Radio value="Custom">
|
||||||
<Radio value="custom">
|
|
||||||
<Tooltip label="Save converted model in a custom folder">
|
<Tooltip label="Save converted model in a custom folder">
|
||||||
{t('modelManager.custom')}
|
{t('modelManager.custom')}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Radio>
|
</Radio>
|
||||||
</Flex>
|
</Flex>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</Flex> */}
|
</Flex>
|
||||||
|
{saveLocation === 'Custom' && (
|
||||||
{/* {saveLocation === 'custom' && (
|
|
||||||
<Flex flexDirection="column" rowGap={2}>
|
<Flex flexDirection="column" rowGap={2}>
|
||||||
<Text fontWeight="500" fontSize="sm" variant="subtext">
|
<Text fontWeight="500" fontSize="sm" variant="subtext">
|
||||||
{t('modelManager.customSaveLocation')}
|
{t('modelManager.customSaveLocation')}
|
||||||
@ -130,13 +165,13 @@ export default function ModelConvert(props: ModelConvertProps) {
|
|||||||
<IAIInput
|
<IAIInput
|
||||||
value={customSaveLocation}
|
value={customSaveLocation}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (e.target.value !== '')
|
setCustomSaveLocation(e.target.value);
|
||||||
setCustomSaveLocation(e.target.value);
|
|
||||||
}}
|
}}
|
||||||
width="full"
|
width="full"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
)} */}
|
)}
|
||||||
|
</Flex>
|
||||||
</IAIAlertDialog>
|
</IAIAlertDialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -44,10 +44,6 @@ const ModelList = (props: ModelListProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection="column" rowGap={4} width="50%" minWidth="50%">
|
<Flex flexDirection="column" rowGap={4} width="50%" minWidth="50%">
|
||||||
<IAIInput
|
|
||||||
onChange={handleSearchFilter}
|
|
||||||
label={t('modelManager.search')}
|
|
||||||
/>
|
|
||||||
<Flex
|
<Flex
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
gap={4}
|
gap={4}
|
||||||
@ -79,6 +75,12 @@ const ModelList = (props: ModelListProps) => {
|
|||||||
</IAIButton>
|
</IAIButton>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
|
<IAIInput
|
||||||
|
onChange={handleSearchFilter}
|
||||||
|
label={t('modelManager.search')}
|
||||||
|
labelPos="side"
|
||||||
|
/>
|
||||||
|
|
||||||
{['all', 'diffusers'].includes(modelFormatFilter) &&
|
{['all', 'diffusers'].includes(modelFormatFilter) &&
|
||||||
filteredDiffusersModels.length > 0 && (
|
filteredDiffusersModels.length > 0 && (
|
||||||
<Flex sx={{ gap: 2, flexDir: 'column' }}>
|
<Flex sx={{ gap: 2, flexDir: 'column' }}>
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { DeleteIcon } from '@chakra-ui/icons';
|
import { DeleteIcon } from '@chakra-ui/icons';
|
||||||
import { Flex, Text, Tooltip } from '@chakra-ui/react';
|
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 IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||||
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@ -21,6 +23,7 @@ type ModelListItemProps = {
|
|||||||
export default function ModelListItem(props: ModelListItemProps) {
|
export default function ModelListItem(props: ModelListItemProps) {
|
||||||
const isBusy = useAppSelector(selectIsBusy);
|
const isBusy = useAppSelector(selectIsBusy);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
const [deleteMainModel] = useDeleteMainModelsMutation();
|
const [deleteMainModel] = useDeleteMainModelsMutation();
|
||||||
|
|
||||||
const { model, isSelected, setSelectedModelId } = props;
|
const { model, isSelected, setSelectedModelId } = props;
|
||||||
@ -30,9 +33,34 @@ export default function ModelListItem(props: ModelListItemProps) {
|
|||||||
}, [model.id, setSelectedModelId]);
|
}, [model.id, setSelectedModelId]);
|
||||||
|
|
||||||
const handleModelDelete = useCallback(() => {
|
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);
|
setSelectedModelId(undefined);
|
||||||
}, [deleteMainModel, model, setSelectedModelId]);
|
}, [deleteMainModel, model, setSelectedModelId, dispatch, t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex sx={{ gap: 2, alignItems: 'center', w: 'full' }}>
|
<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';
|
import { SchedulerParam } from 'features/parameters/types/parameterSchemas';
|
||||||
|
|
||||||
export type AddNewModelType = 'ckpt' | 'diffusers' | null;
|
|
||||||
|
|
||||||
export type Coordinates = {
|
export type Coordinates = {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@ -22,7 +20,6 @@ export interface UIState {
|
|||||||
shouldUseCanvasBetaLayout: boolean;
|
shouldUseCanvasBetaLayout: boolean;
|
||||||
shouldShowExistingModelsInSearch: boolean;
|
shouldShowExistingModelsInSearch: boolean;
|
||||||
shouldUseSliders: boolean;
|
shouldUseSliders: boolean;
|
||||||
addNewModelUIOption: AddNewModelType;
|
|
||||||
shouldHidePreview: boolean;
|
shouldHidePreview: boolean;
|
||||||
shouldPinGallery: boolean;
|
shouldPinGallery: boolean;
|
||||||
shouldShowGallery: boolean;
|
shouldShowGallery: boolean;
|
||||||
|
@ -5,7 +5,9 @@ import {
|
|||||||
BaseModelType,
|
BaseModelType,
|
||||||
CheckpointModelConfig,
|
CheckpointModelConfig,
|
||||||
ControlNetModelConfig,
|
ControlNetModelConfig,
|
||||||
|
ConvertModelConfig,
|
||||||
DiffusersModelConfig,
|
DiffusersModelConfig,
|
||||||
|
ImportModelConfig,
|
||||||
LoRAModelConfig,
|
LoRAModelConfig,
|
||||||
MainModelConfig,
|
MainModelConfig,
|
||||||
MergeModelConfig,
|
MergeModelConfig,
|
||||||
@ -13,8 +15,9 @@ import {
|
|||||||
VaeModelConfig,
|
VaeModelConfig,
|
||||||
} from 'services/api/types';
|
} from 'services/api/types';
|
||||||
|
|
||||||
|
import queryString from 'query-string';
|
||||||
import { ApiFullTagDescription, LIST_TAG, api } from '..';
|
import { ApiFullTagDescription, LIST_TAG, api } from '..';
|
||||||
import { paths } from '../schema';
|
import { operations, paths } from '../schema';
|
||||||
|
|
||||||
export type DiffusersModelConfigEntity = DiffusersModelConfig & { id: string };
|
export type DiffusersModelConfigEntity = DiffusersModelConfig & { id: string };
|
||||||
export type CheckpointModelConfigEntity = CheckpointModelConfig & {
|
export type CheckpointModelConfigEntity = CheckpointModelConfig & {
|
||||||
@ -62,6 +65,7 @@ type DeleteMainModelResponse = void;
|
|||||||
type ConvertMainModelArg = {
|
type ConvertMainModelArg = {
|
||||||
base_model: BaseModelType;
|
base_model: BaseModelType;
|
||||||
model_name: string;
|
model_name: string;
|
||||||
|
params: ConvertModelConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ConvertMainModelResponse =
|
type ConvertMainModelResponse =
|
||||||
@ -75,6 +79,28 @@ type MergeMainModelArg = {
|
|||||||
type MergeMainModelResponse =
|
type MergeMainModelResponse =
|
||||||
paths['/api/v1/models/merge/{base_model}']['put']['responses']['200']['content']['application/json'];
|
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>({
|
const mainModelsAdapter = createEntityAdapter<MainModelConfigEntity>({
|
||||||
sortComparer: (a, b) => a.model_name.localeCompare(b.model_name),
|
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 }],
|
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<
|
deleteMainModels: build.mutation<
|
||||||
DeleteMainModelResponse,
|
DeleteMainModelResponse,
|
||||||
DeleteMainModelArg
|
DeleteMainModelArg
|
||||||
@ -176,10 +225,11 @@ export const modelsApi = api.injectEndpoints({
|
|||||||
ConvertMainModelResponse,
|
ConvertMainModelResponse,
|
||||||
ConvertMainModelArg
|
ConvertMainModelArg
|
||||||
>({
|
>({
|
||||||
query: ({ base_model, model_name }) => {
|
query: ({ base_model, model_name, params }) => {
|
||||||
return {
|
return {
|
||||||
url: `models/convert/${base_model}/main/${model_name}`,
|
url: `models/convert/${base_model}/main/${model_name}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
|
params: params,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
invalidatesTags: [{ type: 'MainModel', id: LIST_TAG }],
|
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,
|
useGetVaeModelsQuery,
|
||||||
useUpdateMainModelsMutation,
|
useUpdateMainModelsMutation,
|
||||||
useDeleteMainModelsMutation,
|
useDeleteMainModelsMutation,
|
||||||
|
useImportMainModelsMutation,
|
||||||
|
useAddMainModelsMutation,
|
||||||
useConvertMainModelsMutation,
|
useConvertMainModelsMutation,
|
||||||
useMergeMainModelsMutation,
|
useMergeMainModelsMutation,
|
||||||
|
useGetModelsInFolderQuery,
|
||||||
|
useGetCheckpointConfigsQuery,
|
||||||
} = modelsApi;
|
} = 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"];
|
delete: operations["del_model"];
|
||||||
/**
|
/**
|
||||||
* Update 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"];
|
patch: operations["update_model"];
|
||||||
};
|
};
|
||||||
@ -102,13 +102,6 @@ export type paths = {
|
|||||||
*/
|
*/
|
||||||
post: operations["add_model"];
|
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}": {
|
"/api/v1/models/convert/{base_model}/{model_type}/{model_name}": {
|
||||||
/**
|
/**
|
||||||
* Convert Model
|
* Convert Model
|
||||||
@ -1226,7 +1219,7 @@ export type components = {
|
|||||||
* @description The nodes in this graph
|
* @description The nodes in this graph
|
||||||
*/
|
*/
|
||||||
nodes?: {
|
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
|
* Edges
|
||||||
@ -1269,7 +1262,7 @@ export type components = {
|
|||||||
* @description The results of node executions
|
* @description The results of node executions
|
||||||
*/
|
*/
|
||||||
results: {
|
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
|
* Errors
|
||||||
@ -4031,40 +4024,6 @@ export type components = {
|
|||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
ResourceOrigin: "internal" | "external";
|
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
|
* ScaleLatentsInvocation
|
||||||
* @description Scales latents by a given factor.
|
* @description Scales latents by a given factor.
|
||||||
@ -4653,18 +4612,18 @@ export type components = {
|
|||||||
*/
|
*/
|
||||||
image?: components["schemas"]["ImageField"];
|
image?: components["schemas"]["ImageField"];
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* StableDiffusion2ModelFormat
|
|
||||||
* @description An enumeration.
|
|
||||||
* @enum {string}
|
|
||||||
*/
|
|
||||||
StableDiffusion2ModelFormat: "checkpoint" | "diffusers";
|
|
||||||
/**
|
/**
|
||||||
* StableDiffusion1ModelFormat
|
* StableDiffusion1ModelFormat
|
||||||
* @description An enumeration.
|
* @description An enumeration.
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
StableDiffusion1ModelFormat: "checkpoint" | "diffusers";
|
StableDiffusion1ModelFormat: "checkpoint" | "diffusers";
|
||||||
|
/**
|
||||||
|
* StableDiffusion2ModelFormat
|
||||||
|
* @description An enumeration.
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
StableDiffusion2ModelFormat: "checkpoint" | "diffusers";
|
||||||
};
|
};
|
||||||
responses: never;
|
responses: never;
|
||||||
parameters: never;
|
parameters: never;
|
||||||
@ -4775,7 +4734,7 @@ export type operations = {
|
|||||||
};
|
};
|
||||||
requestBody: {
|
requestBody: {
|
||||||
content: {
|
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: {
|
responses: {
|
||||||
@ -4812,7 +4771,7 @@ export type operations = {
|
|||||||
};
|
};
|
||||||
requestBody: {
|
requestBody: {
|
||||||
content: {
|
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: {
|
responses: {
|
||||||
@ -5061,7 +5020,7 @@ export type operations = {
|
|||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Update 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.
|
||||||
*/
|
*/
|
||||||
update_model: {
|
update_model: {
|
||||||
parameters: {
|
parameters: {
|
||||||
@ -5090,6 +5049,8 @@ export type operations = {
|
|||||||
400: never;
|
400: never;
|
||||||
/** @description The model could not be found */
|
/** @description The model could not be found */
|
||||||
404: never;
|
404: never;
|
||||||
|
/** @description There is already a model corresponding to the new name */
|
||||||
|
409: never;
|
||||||
/** @description Validation Error */
|
/** @description Validation Error */
|
||||||
422: {
|
422: {
|
||||||
content: {
|
content: {
|
||||||
@ -5160,46 +5121,6 @@ export type operations = {
|
|||||||
424: never;
|
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
|
* Convert Model
|
||||||
* @description Convert a checkpoint model into a diffusers model, optionally saving to the indicated destination directory, or `models` if none.
|
* @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
|
| ControlNetModelConfig
|
||||||
| TextualInversionModelConfig
|
| TextualInversionModelConfig
|
||||||
| MainModelConfig;
|
| MainModelConfig;
|
||||||
|
|
||||||
export type MergeModelConfig = components['schemas']['Body_merge_models'];
|
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
|
// Graphs
|
||||||
export type Graph = components['schemas']['Graph'];
|
export type Graph = components['schemas']['Graph'];
|
||||||
|
@ -16,7 +16,7 @@ const invokeAI = defineStyle((props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bg: mode('base.200', 'base.600')(props),
|
bg: mode('base.250', 'base.600')(props),
|
||||||
color: mode('base.850', 'base.100')(props),
|
color: mode('base.850', 'base.100')(props),
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
svg: {
|
svg: {
|
||||||
|
Loading…
Reference in New Issue
Block a user