Skip to content

Commit 47414be

Browse files
fix(ui): dropped model config cache breaking model edit UI
The model edit UI's composition allows for the model edit form to be instantiated before the model's config has been received. This results in the form having no values - all the fields are blank instead of populated by the model config. Part of the fix is to pass the model config around directly instead of relying on _all_ components to fetch the model directly. I also fixed a crapload of performance issues related to improper use of redux selectors.
1 parent 74cef38 commit 47414be

File tree

15 files changed

+305
-309
lines changed

15 files changed

+305
-309
lines changed
Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
import { skipToken } from '@reduxjs/toolkit/query';
21
import { isNil } from 'lodash-es';
32
import { useMemo } from 'react';
4-
import { useGetModelConfigWithTypeGuard } from 'services/api/hooks/useGetModelConfigWithTypeGuard';
5-
import { isControlNetOrT2IAdapterModelConfig } from 'services/api/types';
6-
7-
export const useControlNetOrT2IAdapterDefaultSettings = (modelKey?: string | null) => {
8-
const { modelConfig, isLoading } = useGetModelConfigWithTypeGuard(
9-
modelKey ?? skipToken,
10-
isControlNetOrT2IAdapterModelConfig
11-
);
3+
import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types';
124

5+
export const useControlNetOrT2IAdapterDefaultSettings = (
6+
modelConfig: ControlNetModelConfig | T2IAdapterModelConfig
7+
) => {
138
const defaultSettingsDefaults = useMemo(() => {
149
return {
1510
preprocessor: {
@@ -19,5 +14,5 @@ export const useControlNetOrT2IAdapterDefaultSettings = (modelKey?: string | nul
1914
};
2015
}, [modelConfig?.default_settings]);
2116

22-
return { defaultSettingsDefaults, isLoading };
17+
return defaultSettingsDefaults;
2318
};

invokeai/frontend/web/src/features/modelManagerV2/hooks/useMainModelDefaultSettings.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
import { skipToken } from '@reduxjs/toolkit/query';
21
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
32
import { useAppSelector } from 'app/store/storeHooks';
4-
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
53
import { selectConfigSlice } from 'features/system/store/configSlice';
64
import { isNil } from 'lodash-es';
75
import { useMemo } from 'react';
8-
import { useGetModelConfigWithTypeGuard } from 'services/api/hooks/useGetModelConfigWithTypeGuard';
9-
import { isNonRefinerMainModelConfig } from 'services/api/types';
6+
import type { MainModelConfig } from 'services/api/types';
107

118
const initialStatesSelector = createMemoizedSelector(selectConfigSlice, (config) => {
129
const { steps, guidance, scheduler, cfgRescaleMultiplier, vaePrecision, width, height } = config.sd;
@@ -22,9 +19,7 @@ const initialStatesSelector = createMemoizedSelector(selectConfigSlice, (config)
2219
};
2320
});
2421

25-
export const useMainModelDefaultSettings = (modelKey?: string | null) => {
26-
const { modelConfig, isLoading } = useGetModelConfigWithTypeGuard(modelKey ?? skipToken, isNonRefinerMainModelConfig);
27-
22+
export const useMainModelDefaultSettings = (modelConfig: MainModelConfig) => {
2823
const {
2924
initialSteps,
3025
initialCfg,
@@ -81,5 +76,5 @@ export const useMainModelDefaultSettings = (modelKey?: string | null) => {
8176
initialHeight,
8277
]);
8378

84-
return { defaultSettingsDefaults, isLoading, optimalDimension: getOptimalDimension(modelConfig) };
79+
return defaultSettingsDefaults;
8580
};

invokeai/frontend/web/src/features/modelManagerV2/store/modelManagerV2Slice.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { PayloadAction } from '@reduxjs/toolkit';
22
import { createSlice } from '@reduxjs/toolkit';
3-
import type { PersistConfig } from 'app/store/store';
3+
import type { PersistConfig, RootState } from 'app/store/store';
44
import type { ModelType } from 'services/api/types';
55

66
export type FilterableModelType = Exclude<ModelType, 'onnx' | 'clip_vision'> | 'refiner';
@@ -50,6 +50,8 @@ export const modelManagerV2Slice = createSlice({
5050
export const { setSelectedModelKey, setSearchTerm, setFilteredModelType, setSelectedModelMode, setScanPath } =
5151
modelManagerV2Slice.actions;
5252

53+
export const selectModelManagerV2Slice = (state: RootState) => state.modelmanagerV2;
54+
5355
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
5456
const migrateModelManagerState = (state: any): any => {
5557
if (!('_version' in state)) {

invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import { FetchingModelsLoader } from './FetchingModelsLoader';
2121
import { ModelListWrapper } from './ModelListWrapper';
2222

2323
const ModelList = () => {
24-
const { searchTerm, filteredModelType } = useAppSelector((s) => s.modelmanagerV2);
24+
const filteredModelType = useAppSelector((s) => s.modelmanagerV2.filteredModelType);
25+
const searchTerm = useAppSelector((s) => s.modelmanagerV2.searchTerm);
2526
const { t } = useTranslation();
2627

2728
const [mainModels, { isLoading: isLoadingMainModels }] = useMainModels();

invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { SystemStyleObject } from '@invoke-ai/ui-library';
22
import { ConfirmationAlertDialog, Flex, IconButton, Spacer, Text, useDisclosure } from '@invoke-ai/ui-library';
3+
import { createSelector } from '@reduxjs/toolkit';
34
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
4-
import { setSelectedModelKey } from 'features/modelManagerV2/store/modelManagerV2Slice';
5+
import { selectModelManagerV2Slice, setSelectedModelKey } from 'features/modelManagerV2/store/modelManagerV2Slice';
56
import ModelBaseBadge from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge';
67
import ModelFormatBadge from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge';
78
import { toast } from 'features/toast/toast';
@@ -23,15 +24,21 @@ const sx: SystemStyleObject = {
2324
"&[aria-selected='true']": { bg: 'base.700' },
2425
};
2526

26-
const ModelListItem = (props: ModelListItemProps) => {
27+
const ModelListItem = ({ model }: ModelListItemProps) => {
2728
const { t } = useTranslation();
2829
const dispatch = useAppDispatch();
29-
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
30+
const selectIsSelected = useMemo(
31+
() =>
32+
createSelector(
33+
selectModelManagerV2Slice,
34+
(modelManagerV2Slice) => modelManagerV2Slice.selectedModelKey === model.key
35+
),
36+
[model.key]
37+
);
38+
const isSelected = useAppSelector(selectIsSelected);
3039
const [deleteModel] = useDeleteModelsMutation();
3140
const { isOpen, onOpen, onClose } = useDisclosure();
3241

33-
const { model } = props;
34-
3542
const handleSelectModel = useCallback(() => {
3643
dispatch(setSelectedModelKey(model.key));
3744
}, [model.key, dispatch]);
@@ -43,11 +50,6 @@ const ModelListItem = (props: ModelListItemProps) => {
4350
},
4451
[onOpen]
4552
);
46-
47-
const isSelected = useMemo(() => {
48-
return selectedModelKey === model.key;
49-
}, [selectedModelKey, model.key]);
50-
5153
const handleModelDelete = useCallback(() => {
5254
deleteModel({ key: model.key })
5355
.unwrap()

invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings.tsx

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { Button, Flex, Heading, SimpleGrid, Text } from '@invoke-ai/ui-library';
2-
import { useAppSelector } from 'app/store/storeHooks';
1+
import { Button, Flex, Heading, SimpleGrid } from '@invoke-ai/ui-library';
32
import { useControlNetOrT2IAdapterDefaultSettings } from 'features/modelManagerV2/hooks/useControlNetOrT2IAdapterDefaultSettings';
43
import { DefaultPreprocessor } from 'features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/DefaultPreprocessor';
54
import type { FormField } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings';
@@ -10,17 +9,20 @@ import { useForm } from 'react-hook-form';
109
import { useTranslation } from 'react-i18next';
1110
import { PiCheckBold } from 'react-icons/pi';
1211
import { useUpdateModelMutation } from 'services/api/endpoints/models';
12+
import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types';
1313

1414
export type ControlNetOrT2IAdapterDefaultSettingsFormData = {
1515
preprocessor: FormField<string>;
1616
};
1717

18-
export const ControlNetOrT2IAdapterDefaultSettings = () => {
19-
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
18+
type Props = {
19+
modelConfig: ControlNetModelConfig | T2IAdapterModelConfig;
20+
};
21+
22+
export const ControlNetOrT2IAdapterDefaultSettings = ({ modelConfig }: Props) => {
2023
const { t } = useTranslation();
2124

22-
const { defaultSettingsDefaults, isLoading: isLoadingDefaultSettings } =
23-
useControlNetOrT2IAdapterDefaultSettings(selectedModelKey);
25+
const defaultSettingsDefaults = useControlNetOrT2IAdapterDefaultSettings(modelConfig);
2426

2527
const [updateModel, { isLoading: isLoadingUpdateModel }] = useUpdateModelMutation();
2628

@@ -30,16 +32,12 @@ export const ControlNetOrT2IAdapterDefaultSettings = () => {
3032

3133
const onSubmit = useCallback<SubmitHandler<ControlNetOrT2IAdapterDefaultSettingsFormData>>(
3234
(data) => {
33-
if (!selectedModelKey) {
34-
return;
35-
}
36-
3735
const body = {
3836
preprocessor: data.preprocessor.isEnabled ? data.preprocessor.value : null,
3937
};
4038

4139
updateModel({
42-
key: selectedModelKey,
40+
key: modelConfig.key,
4341
body: { default_settings: body },
4442
})
4543
.unwrap()
@@ -61,13 +59,9 @@ export const ControlNetOrT2IAdapterDefaultSettings = () => {
6159
}
6260
});
6361
},
64-
[selectedModelKey, reset, updateModel, t]
62+
[updateModel, modelConfig.key, t, reset]
6563
);
6664

67-
if (isLoadingDefaultSettings) {
68-
return <Text>{t('common.loading')}</Text>;
69-
}
70-
7165
return (
7266
<>
7367
<Flex gap="4" justifyContent="space-between" w="full" pb={4}>

invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
import { Button, Flex, Heading, SimpleGrid, Text } from '@invoke-ai/ui-library';
1+
import { Button, Flex, Heading, SimpleGrid } from '@invoke-ai/ui-library';
22
import { useAppSelector } from 'app/store/storeHooks';
33
import { useMainModelDefaultSettings } from 'features/modelManagerV2/hooks/useMainModelDefaultSettings';
44
import { DefaultHeight } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight';
55
import { DefaultWidth } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth';
66
import type { ParameterScheduler } from 'features/parameters/types/parameterSchemas';
7+
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
78
import { toast } from 'features/toast/toast';
8-
import { useCallback } from 'react';
9+
import { useCallback, useMemo } from 'react';
910
import type { SubmitHandler } from 'react-hook-form';
1011
import { useForm } from 'react-hook-form';
1112
import { useTranslation } from 'react-i18next';
1213
import { PiCheckBold } from 'react-icons/pi';
1314
import { useUpdateModelMutation } from 'services/api/endpoints/models';
15+
import type { MainModelConfig } from 'services/api/types';
1416

1517
import { DefaultCfgRescaleMultiplier } from './DefaultCfgRescaleMultiplier';
1618
import { DefaultCfgScale } from './DefaultCfgScale';
@@ -35,16 +37,16 @@ export type MainModelDefaultSettingsFormData = {
3537
height: FormField<number>;
3638
};
3739

38-
export const MainModelDefaultSettings = () => {
40+
type Props = {
41+
modelConfig: MainModelConfig;
42+
};
43+
44+
export const MainModelDefaultSettings = ({ modelConfig }: Props) => {
3945
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
4046
const { t } = useTranslation();
4147

42-
const {
43-
defaultSettingsDefaults,
44-
isLoading: isLoadingDefaultSettings,
45-
optimalDimension,
46-
} = useMainModelDefaultSettings(selectedModelKey);
47-
48+
const defaultSettingsDefaults = useMainModelDefaultSettings(modelConfig);
49+
const optimalDimension = useMemo(() => getOptimalDimension(modelConfig), [modelConfig]);
4850
const [updateModel, { isLoading: isLoadingUpdateModel }] = useUpdateModelMutation();
4951

5052
const { handleSubmit, control, formState, reset } = useForm<MainModelDefaultSettingsFormData>({
@@ -94,10 +96,6 @@ export const MainModelDefaultSettings = () => {
9496
[selectedModelKey, reset, updateModel, t]
9597
);
9698

97-
if (isLoadingDefaultSettings) {
98-
return <Text>{t('common.loading')}</Text>;
99-
}
100-
10199
return (
102100
<>
103101
<Flex gap="4" justifyContent="space-between" w="full" pb={4}>

0 commit comments

Comments
 (0)