mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Adding model edits (unstable/WIP)
This commit is contained in:
parent
79d577bff9
commit
4eed06903c
26
frontend/src/app/invokeai.d.ts
vendored
26
frontend/src/app/invokeai.d.ts
vendored
@ -165,6 +165,11 @@ export declare type Model = {
|
|||||||
status: ModelStatus;
|
status: ModelStatus;
|
||||||
description: string;
|
description: string;
|
||||||
weights: string;
|
weights: string;
|
||||||
|
config?: string;
|
||||||
|
vae?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
default?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type ModelList = Record<string, Model>;
|
export declare type ModelList = Record<string, Model>;
|
||||||
@ -259,3 +264,24 @@ export declare type UploadOutpaintingMergeImagePayload = {
|
|||||||
dataURL: string;
|
dataURL: string;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export declare type InvokeModelConfigProps = {
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
config: string;
|
||||||
|
weights: string;
|
||||||
|
vae?: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
default?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export declare type OnFoundModelResponse = {
|
||||||
|
search_folder: string;
|
||||||
|
found_models: FoundModel[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export declare type FoundModel = {
|
||||||
|
name: string;
|
||||||
|
location: string;
|
||||||
|
};
|
||||||
|
@ -44,6 +44,7 @@ const systemBlacklist = [
|
|||||||
'socketId',
|
'socketId',
|
||||||
'totalIterations',
|
'totalIterations',
|
||||||
'totalSteps',
|
'totalSteps',
|
||||||
|
'openModel',
|
||||||
].map((blacklistItem) => `system.${blacklistItem}`);
|
].map((blacklistItem) => `system.${blacklistItem}`);
|
||||||
|
|
||||||
const galleryBlacklist = [
|
const galleryBlacklist = [
|
||||||
|
@ -1,5 +1,330 @@
|
|||||||
import React from 'react';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import { Field, FieldInputProps, Formik, FormikProps } from 'formik';
|
||||||
|
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||||
|
import { addNewModel, } from 'app/socketio/actions';
|
||||||
|
import { InvokeModelConfigProps } from 'app/invokeai';
|
||||||
|
import { SystemState } from 'features/system/store/systemSlice';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
FormControl,
|
||||||
|
FormErrorMessage,
|
||||||
|
FormHelperText,
|
||||||
|
FormLabel,
|
||||||
|
HStack,
|
||||||
|
Input,
|
||||||
|
NumberDecrementStepper,
|
||||||
|
NumberIncrementStepper,
|
||||||
|
NumberInput,
|
||||||
|
NumberInputField,
|
||||||
|
NumberInputStepper,
|
||||||
|
Text,
|
||||||
|
VStack,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function ModelEdit() {
|
export default function ModelEdit() {
|
||||||
return <div>ModelEdit</div>;
|
|
||||||
|
const MIN_MODEL_SIZE = 64;
|
||||||
|
const MAX_MODEL_SIZE = 2048;
|
||||||
|
const modelListSelector = createSelector(
|
||||||
|
(state: RootState) => state.system,
|
||||||
|
(system: SystemState) => {
|
||||||
|
const models = _.map(system.model_list, (model, key) => {
|
||||||
|
return { name: key, ...model };
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedModel = models.find((model) => model.name === system.openModel);
|
||||||
|
|
||||||
|
return {
|
||||||
|
openedModel: {
|
||||||
|
name:selectedModel?.name,
|
||||||
|
description:selectedModel?.description,
|
||||||
|
config:selectedModel?.config,
|
||||||
|
weights:selectedModel?.weights,
|
||||||
|
vae:selectedModel?.vae,
|
||||||
|
width:selectedModel?.width,
|
||||||
|
height:selectedModel?.height,
|
||||||
|
default:selectedModel?.default,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const { openedModel } = useAppSelector(modelListSelector);
|
||||||
|
|
||||||
|
|
||||||
|
const viewModelFormValues: InvokeModelConfigProps = {
|
||||||
|
name: openedModel.name || '',
|
||||||
|
description: openedModel.description || '',
|
||||||
|
config: openedModel.config || '',
|
||||||
|
weights: openedModel.weights || '',
|
||||||
|
vae: openedModel.vae || '',
|
||||||
|
width: openedModel.width || 0,
|
||||||
|
height: openedModel.height || 0,
|
||||||
|
default: openedModel.default || false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const addModelFormSubmitHandler = (values: InvokeModelConfigProps) => {
|
||||||
|
dispatch(addNewModel(values));
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
function hasWhiteSpace(s: string) {
|
||||||
|
return /\\s/g.test(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function baseValidation(value: string) {
|
||||||
|
let error;
|
||||||
|
if (hasWhiteSpace(value)) error = 'Cannot use spaces';
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!openedModel) {
|
||||||
|
return (
|
||||||
|
<Flex> 'No model selected' </Flex>
|
||||||
|
|
||||||
|
)} else {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Formik
|
||||||
|
initialValues={viewModelFormValues}
|
||||||
|
onSubmit={addModelFormSubmitHandler}
|
||||||
|
>
|
||||||
|
{({ handleSubmit, errors, touched }) => (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<VStack rowGap={'0.5rem'}>
|
||||||
|
<Text fontSize={20} fontWeight="bold" alignSelf={'start'}>
|
||||||
|
Manual
|
||||||
|
</Text>
|
||||||
|
{/* Name */}
|
||||||
|
<FormControl
|
||||||
|
isInvalid={!!errors.name && touched.name}
|
||||||
|
isRequired
|
||||||
|
>
|
||||||
|
<FormLabel htmlFor="name">Name</FormLabel>
|
||||||
|
<VStack alignItems={'start'}>
|
||||||
|
<Field
|
||||||
|
as={Input}
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
type="text"
|
||||||
|
validate={baseValidation}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
{!!errors.name && touched.name ? (
|
||||||
|
<FormErrorMessage>{errors.name}</FormErrorMessage>
|
||||||
|
) : (
|
||||||
|
<FormHelperText margin={0}>
|
||||||
|
Enter a name for your model
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
|
<FormControl
|
||||||
|
isInvalid={!!errors.description && touched.description}
|
||||||
|
isRequired
|
||||||
|
>
|
||||||
|
<FormLabel htmlFor="description">Description</FormLabel>
|
||||||
|
<VStack alignItems={'start'}>
|
||||||
|
<Field
|
||||||
|
as={Input}
|
||||||
|
id="description"
|
||||||
|
name="description"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
{!!errors.description && touched.description ? (
|
||||||
|
<FormErrorMessage>
|
||||||
|
{errors.description}
|
||||||
|
</FormErrorMessage>
|
||||||
|
) : (
|
||||||
|
<FormHelperText margin={0}>
|
||||||
|
Add a description for your model
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
{/* Config */}
|
||||||
|
<FormControl
|
||||||
|
isInvalid={!!errors.config && touched.config}
|
||||||
|
isRequired
|
||||||
|
>
|
||||||
|
<FormLabel htmlFor="config">Config</FormLabel>
|
||||||
|
<VStack alignItems={'start'}>
|
||||||
|
<Field
|
||||||
|
as={Input}
|
||||||
|
id="config"
|
||||||
|
name="config"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
{!!errors.config && touched.config ? (
|
||||||
|
<FormErrorMessage>{errors.config}</FormErrorMessage>
|
||||||
|
) : (
|
||||||
|
<FormHelperText margin={0}>
|
||||||
|
Path to the config file of your model.
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
{/* Weights */}
|
||||||
|
<FormControl
|
||||||
|
isInvalid={!!errors.weights && touched.weights}
|
||||||
|
isRequired
|
||||||
|
>
|
||||||
|
<FormLabel htmlFor="config">Model Location</FormLabel>
|
||||||
|
<VStack alignItems={'start'}>
|
||||||
|
<Field
|
||||||
|
as={Input}
|
||||||
|
id="weights"
|
||||||
|
name="weights"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
{!!errors.weights && touched.weights ? (
|
||||||
|
<FormErrorMessage>
|
||||||
|
{errors.weights}
|
||||||
|
</FormErrorMessage>
|
||||||
|
) : (
|
||||||
|
<FormHelperText margin={0}>
|
||||||
|
Path to where your model is located.
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
{/* VAE */}
|
||||||
|
<FormControl isInvalid={!!errors.vae && touched.vae}>
|
||||||
|
<FormLabel htmlFor="vae">VAE Location</FormLabel>
|
||||||
|
<VStack alignItems={'start'}>
|
||||||
|
<Field as={Input} id="vae" name="vae" type="text" />
|
||||||
|
{!!errors.vae && touched.vae ? (
|
||||||
|
<FormErrorMessage>{errors.vae}</FormErrorMessage>
|
||||||
|
) : (
|
||||||
|
<FormHelperText margin={0}>
|
||||||
|
Path to where your VAE is located.
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<HStack width={'100%'}>
|
||||||
|
{/* Width */}
|
||||||
|
<FormControl
|
||||||
|
isInvalid={!!errors.width && touched.width}
|
||||||
|
>
|
||||||
|
<FormLabel htmlFor="width">Width</FormLabel>
|
||||||
|
<VStack alignItems={'start'}>
|
||||||
|
<Field id="width" name="width">
|
||||||
|
{({
|
||||||
|
field,
|
||||||
|
form,
|
||||||
|
}: {
|
||||||
|
field: FieldInputProps<number>;
|
||||||
|
form: FormikProps<InvokeModelConfigProps>;
|
||||||
|
}) => (
|
||||||
|
<NumberInput
|
||||||
|
{...field}
|
||||||
|
id="width"
|
||||||
|
name="width"
|
||||||
|
min={MIN_MODEL_SIZE}
|
||||||
|
max={MAX_MODEL_SIZE}
|
||||||
|
step={64}
|
||||||
|
onChange={(value) =>
|
||||||
|
form.setFieldValue(
|
||||||
|
field.name,
|
||||||
|
Number(value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<NumberInputField />
|
||||||
|
<NumberInputStepper>
|
||||||
|
<NumberIncrementStepper />
|
||||||
|
<NumberDecrementStepper />
|
||||||
|
</NumberInputStepper>
|
||||||
|
</NumberInput>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
{!!errors.width && touched.width ? (
|
||||||
|
<FormErrorMessage>
|
||||||
|
{errors.width}
|
||||||
|
</FormErrorMessage>
|
||||||
|
) : (
|
||||||
|
<FormHelperText margin={0}>
|
||||||
|
Default width of your model.
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
{/* Height */}
|
||||||
|
<FormControl
|
||||||
|
isInvalid={!!errors.height && touched.height}
|
||||||
|
>
|
||||||
|
<FormLabel htmlFor="height">Height</FormLabel>
|
||||||
|
<VStack alignItems={'start'}>
|
||||||
|
<Field id="height" name="height">
|
||||||
|
{({
|
||||||
|
field,
|
||||||
|
form,
|
||||||
|
}: {
|
||||||
|
field: FieldInputProps<number>;
|
||||||
|
form: FormikProps<InvokeModelConfigProps>;
|
||||||
|
}) => (
|
||||||
|
<NumberInput
|
||||||
|
{...field}
|
||||||
|
id="height"
|
||||||
|
name="height"
|
||||||
|
min={MIN_MODEL_SIZE}
|
||||||
|
max={MAX_MODEL_SIZE}
|
||||||
|
step={64}
|
||||||
|
onChange={(value) =>
|
||||||
|
form.setFieldValue(
|
||||||
|
field.name,
|
||||||
|
Number(value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<NumberInputField />
|
||||||
|
<NumberInputStepper>
|
||||||
|
<NumberIncrementStepper />
|
||||||
|
<NumberDecrementStepper />
|
||||||
|
</NumberInputStepper>
|
||||||
|
</NumberInput>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
{!!errors.height && touched.height ? (
|
||||||
|
<FormErrorMessage>
|
||||||
|
{errors.height}
|
||||||
|
</FormErrorMessage>
|
||||||
|
) : (
|
||||||
|
<FormHelperText margin={0}>
|
||||||
|
Default height of your model.
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</FormControl>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="modal-close-btn"
|
||||||
|
isLoading={isProcessing}
|
||||||
|
>
|
||||||
|
Save Model
|
||||||
|
</Button>
|
||||||
|
</VStack>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
)}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { DeleteIcon } from '@chakra-ui/icons';
|
import { EditIcon, DeleteIcon } from '@chakra-ui/icons';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Flex,
|
Flex,
|
||||||
@ -12,6 +12,7 @@ import { deleteModel, requestModelChange } from 'app/socketio/actions';
|
|||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||||
|
import { setOpenModel } from 'features/system/store/systemSlice';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -29,12 +30,17 @@ export default function ModelListItem(props: ModelListItemProps) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { name, status, description } = props;
|
const { name, status, description } = props;
|
||||||
|
|
||||||
const handleChangeModel = () => {
|
const handleChangeModel = () => {
|
||||||
dispatch(requestModelChange(name));
|
dispatch(requestModelChange(name));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openModel = () => {
|
||||||
|
dispatch(setOpenModel(name));
|
||||||
|
};
|
||||||
|
|
||||||
const handleModelDelete = () => {
|
const handleModelDelete = () => {
|
||||||
dispatch(deleteModel(name));
|
dispatch(deleteModel(name));
|
||||||
};
|
};
|
||||||
@ -68,6 +74,14 @@ export default function ModelListItem(props: ModelListItemProps) {
|
|||||||
>
|
>
|
||||||
{t('modelmanager:load')}
|
{t('modelmanager:load')}
|
||||||
</Button>
|
</Button>
|
||||||
|
<IconButton
|
||||||
|
icon={<EditIcon />}
|
||||||
|
size={'sm'}
|
||||||
|
onClick={openModel}
|
||||||
|
aria-label="Modify Config"
|
||||||
|
isDisabled={status === 'active' || isProcessing || !isConnected}
|
||||||
|
className=" modal-close-btn"
|
||||||
|
/>
|
||||||
<IAIAlertDialog
|
<IAIAlertDialog
|
||||||
title={t('modelmanager:deleteModel')}
|
title={t('modelmanager:deleteModel')}
|
||||||
acceptCallback={handleModelDelete}
|
acceptCallback={handleModelDelete}
|
||||||
|
@ -49,6 +49,7 @@ export interface SystemState
|
|||||||
toastQueue: UseToastOptions[];
|
toastQueue: UseToastOptions[];
|
||||||
searchFolder: string | null;
|
searchFolder: string | null;
|
||||||
foundModels: InvokeAI.FoundModel[] | null;
|
foundModels: InvokeAI.FoundModel[] | null;
|
||||||
|
openModel: string | null,
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialSystemState: SystemState = {
|
const initialSystemState: SystemState = {
|
||||||
@ -86,6 +87,7 @@ const initialSystemState: SystemState = {
|
|||||||
toastQueue: [],
|
toastQueue: [],
|
||||||
searchFolder: null,
|
searchFolder: null,
|
||||||
foundModels: null,
|
foundModels: null,
|
||||||
|
openModel: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const systemSlice = createSlice({
|
export const systemSlice = createSlice({
|
||||||
@ -238,6 +240,9 @@ export const systemSlice = createSlice({
|
|||||||
) => {
|
) => {
|
||||||
state.foundModels = action.payload;
|
state.foundModels = action.payload;
|
||||||
},
|
},
|
||||||
|
setOpenModel: (state, action: PayloadAction<string | null>) => {
|
||||||
|
state.openModel = action.payload;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -268,6 +273,7 @@ export const {
|
|||||||
setProcessingIndeterminateTask,
|
setProcessingIndeterminateTask,
|
||||||
setSearchFolder,
|
setSearchFolder,
|
||||||
setFoundModels,
|
setFoundModels,
|
||||||
|
setOpenModel
|
||||||
} = systemSlice.actions;
|
} = systemSlice.actions;
|
||||||
|
|
||||||
export default systemSlice.reducer;
|
export default systemSlice.reducer;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user