mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Model Edit Initial Implementation
This commit is contained in:
parent
a823e37126
commit
6523fd07ab
21
frontend/src/app/invokeai.d.ts
vendored
21
frontend/src/app/invokeai.d.ts
vendored
@ -264,24 +264,3 @@ 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;
|
|
||||||
};
|
|
||||||
|
@ -26,9 +26,9 @@ import {
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FaPlus } from 'react-icons/fa';
|
import { FaPlus } from 'react-icons/fa';
|
||||||
import { Field, FieldInputProps, Formik, FormikProps } from 'formik';
|
import { Field, FieldInputProps, Formik, FormikProps } from 'formik';
|
||||||
import { RootState } from 'app/store';
|
import type { RootState } from 'app/store';
|
||||||
import { addNewModel } from 'app/socketio/actions';
|
import { addNewModel } from 'app/socketio/actions';
|
||||||
import { InvokeModelConfigProps } from 'app/invokeai';
|
import type { InvokeModelConfigProps } from 'app/invokeai';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAICheckbox from 'common/components/IAICheckbox';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import SearchModels from './SearchModels';
|
import SearchModels from './SearchModels';
|
||||||
|
@ -1,331 +1,60 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector, Dictionary } from '@reduxjs/toolkit';
|
||||||
import { Field, FieldInputProps, Formik, FormikProps } from 'formik';
|
import type { RootState } from 'app/store';
|
||||||
import { RootState } from 'app/store';
|
import { useAppSelector } from 'app/storeHooks';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks'
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { addNewModel, } from 'app/socketio/actions';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { InvokeModelConfigProps } from 'app/invokeai';
|
import _ from 'lodash';
|
||||||
import { SystemState } from 'features/system/store/systemSlice';
|
import { Model } from 'app/invokeai';
|
||||||
import {
|
import { Flex, Spacer, Text } from '@chakra-ui/react';
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
FormControl,
|
|
||||||
FormErrorMessage,
|
|
||||||
FormHelperText,
|
|
||||||
FormLabel,
|
|
||||||
HStack,
|
|
||||||
Input,
|
|
||||||
NumberDecrementStepper,
|
|
||||||
NumberIncrementStepper,
|
|
||||||
NumberInput,
|
|
||||||
NumberInputField,
|
|
||||||
NumberInputStepper,
|
|
||||||
Text,
|
|
||||||
VStack,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const selector = createSelector(
|
||||||
|
[systemSelector],
|
||||||
|
(system) => {
|
||||||
|
const { openModel, model_list } = system;
|
||||||
|
return {
|
||||||
|
model_list,
|
||||||
|
openModel,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
memoizeOptions: {
|
||||||
|
resultEqualityCheck: _.isEqual,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default function ModelEdit() {
|
export default function ModelEdit() {
|
||||||
|
const { openModel, model_list } = useAppSelector(selector);
|
||||||
|
const [openedModel, setOpenedModel] = useState<Model>();
|
||||||
|
|
||||||
const MIN_MODEL_SIZE = 64;
|
useEffect(() => {
|
||||||
const MAX_MODEL_SIZE = 2048;
|
if (openModel) {
|
||||||
const modelListSelector = createSelector(
|
const retrievedModel = _.pickBy(model_list, (val, key) => {
|
||||||
(state: RootState) => state.system,
|
return _.isEqual(key, openModel);
|
||||||
(system: SystemState) => {
|
|
||||||
const models = _.map(system.model_list, (model, key) => {
|
|
||||||
return { name: key, ...model };
|
|
||||||
});
|
});
|
||||||
|
setOpenedModel(retrievedModel[openModel]);
|
||||||
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,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
}, [model_list, openModel]);
|
||||||
|
|
||||||
|
console.log(openedModel);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex flexDirection="column" rowGap="1rem">
|
||||||
|
<Flex columnGap="1rem" alignItems="center">
|
||||||
|
<Text fontSize="lg" fontWeight="bold">
|
||||||
|
{openModel}
|
||||||
|
</Text>
|
||||||
|
<Text>{openedModel?.status}</Text>
|
||||||
|
</Flex>
|
||||||
|
<Flex flexDirection="column">
|
||||||
|
<Text>{openedModel?.config}</Text>
|
||||||
|
<Text>{openedModel?.description}</Text>
|
||||||
|
<Text>{openedModel?.height}</Text>
|
||||||
|
<Text>{openedModel?.width}</Text>
|
||||||
|
<Text>{openedModel?.default}</Text>
|
||||||
|
<Text>{openedModel?.vae}</Text>
|
||||||
|
<Text>{openedModel?.weights}</Text>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
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>
|
|
||||||
)}
|
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ const ModelList = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection={'column'} rowGap="2rem" width={'50%'}>
|
<Flex flexDirection={'column'} rowGap="2rem" width="45%" minWidth="45%">
|
||||||
<Flex justifyContent={'space-between'}>
|
<Flex justifyContent={'space-between'}>
|
||||||
<Text fontSize={'1.4rem'} fontWeight="bold">
|
<Text fontSize={'1.4rem'} fontWeight="bold">
|
||||||
{t('modelmanager:availableModels')}
|
{t('modelmanager:availableModels')}
|
||||||
|
@ -134,12 +134,13 @@ class ModelCache(object):
|
|||||||
'''
|
'''
|
||||||
models = {}
|
models = {}
|
||||||
for name in self.config:
|
for name in self.config:
|
||||||
try:
|
description = self.config[name].description if 'description' in self.config[name] else '<no description>'
|
||||||
description = self.config[name].description
|
weights = self.config[name].weights if 'weights' in self.config[name] else '<no weights>'
|
||||||
weights = self.config[name].weights
|
config = self.config[name].config if 'config' in self.config[name] else '<no config>'
|
||||||
except ConfigAttributeError:
|
width = self.config[name].width if 'width' in self.config[name] else 512
|
||||||
description = '<no description>'
|
height = self.config[name].height if 'height' in self.config[name] else 512
|
||||||
weights = '<not found>'
|
default = self.config[name].default if 'default' in self.config[name] else False
|
||||||
|
vae = self.config[name].vae if 'vae' in self.config[name] else '<no vae>'
|
||||||
|
|
||||||
if self.current_model == name:
|
if self.current_model == name:
|
||||||
status = 'active'
|
status = 'active'
|
||||||
@ -151,7 +152,12 @@ class ModelCache(object):
|
|||||||
models[name]={
|
models[name]={
|
||||||
'status' : status,
|
'status' : status,
|
||||||
'description' : description,
|
'description' : description,
|
||||||
'weights': weights
|
'weights': weights,
|
||||||
|
'config': config,
|
||||||
|
'width': width,
|
||||||
|
'height': height,
|
||||||
|
'vae': vae,
|
||||||
|
'default': default
|
||||||
}
|
}
|
||||||
return models
|
return models
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user