From baf1194cae7297af666f87132946740d49507a4a Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Fri, 23 Feb 2024 16:03:56 -0500 Subject: [PATCH] clean up old model manager components and endpoints --- invokeai/frontend/web/src/app/store/store.ts | 6 +- .../SyncModels/SyncModelsButton.tsx | 32 -- .../SyncModels/SyncModelsIconButton.tsx | 33 -- .../components/SyncModels/useSyncModels.ts | 40 -- .../modelManager/store/modelManagerSlice.ts | 47 --- .../subpanels/AddModelsPanel/AddModels.tsx | 35 -- .../AddModelsPanel/AdvancedAddCheckpoint.tsx | 168 --------- .../AddModelsPanel/AdvancedAddDiffusers.tsx | 148 -------- .../AddModelsPanel/AdvancedAddModels.tsx | 50 --- .../AddModelsPanel/FoundModelsList.tsx | 176 --------- .../AddModelsPanel/ScanAdvancedAddModels.tsx | 95 ----- .../subpanels/AddModelsPanel/ScanModels.tsx | 22 -- .../AddModelsPanel/SearchFolderForm.tsx | 103 ----- .../AddModelsPanel/SimpleAddModels.tsx | 92 ----- .../subpanels/AddModelsPanel/util.ts | 15 - .../subpanels/ImportModelsPanel.tsx | 34 -- .../subpanels/MergeModelsPanel.tsx | 352 ------------------ .../subpanels/ModelManagerPanel.tsx | 63 ---- .../ModelManagerPanel/CheckpointModelEdit.tsx | 185 --------- .../ModelManagerPanel/DiffusersModelEdit.tsx | 133 ------- .../ModelManagerPanel/LoRAModelEdit.tsx | 127 ------- .../ModelManagerPanel/ModelConvert.tsx | 165 -------- .../subpanels/ModelManagerPanel/ModelList.tsx | 205 ---------- .../ModelManagerPanel/ModelListItem.tsx | 111 ------ .../subpanels/ModelManagerSettingsPanel.tsx | 14 - .../ModelManagerSettingsPanel/SyncModels.tsx | 22 -- .../subpanels/shared/BaseModelSelect.tsx | 36 -- .../shared/CheckpointConfigsSelect.tsx | 32 -- .../subpanels/shared/ModelVariantSelect.tsx | 34 -- .../inputs/MainModelFieldInputComponent.tsx | 2 +- .../RefinerModelFieldInputComponent.tsx | 2 +- .../SDXLMainModelFieldInputComponent.tsx | 2 +- .../inputs/VAEModelFieldInputComponent.tsx | 2 +- .../GenerationSettingsAccordion.tsx | 2 +- .../web/src/services/api/endpoints/models.ts | 62 +-- 35 files changed, 11 insertions(+), 2636 deletions(-) delete mode 100644 invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsButton.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsIconButton.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/components/SyncModels/useSyncModels.ts delete mode 100644 invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AddModels.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddModels.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/FoundModelsList.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanModels.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SearchFolderForm.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SimpleAddModels.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/util.ts delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/ImportModelsPanel.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/MergeModelsPanel.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelConvert.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelList.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelListItem.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModels.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/shared/BaseModelSelect.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/shared/CheckpointConfigsSelect.tsx delete mode 100644 invokeai/frontend/web/src/features/modelManager/subpanels/shared/ModelVariantSelect.tsx diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index c63bc02e09..f85ff916b0 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -15,8 +15,7 @@ import { dynamicPromptsPersistConfig, dynamicPromptsSlice } from 'features/dynam import { galleryPersistConfig, gallerySlice } from 'features/gallery/store/gallerySlice'; import { hrfPersistConfig, hrfSlice } from 'features/hrf/store/hrfSlice'; import { loraPersistConfig, loraSlice } from 'features/lora/store/loraSlice'; -import { modelManagerPersistConfig, modelManagerSlice } from 'features/modelManager/store/modelManagerSlice'; -import { modelManagerV2Slice } from 'features/modelManagerV2/store/modelManagerV2Slice'; +import { modelManagerV2PersistConfig, modelManagerV2Slice } from 'features/modelManagerV2/store/modelManagerV2Slice'; import { nodesPersistConfig, nodesSlice } from 'features/nodes/store/nodesSlice'; import { workflowPersistConfig, workflowSlice } from 'features/nodes/store/workflowSlice'; import { generationPersistConfig, generationSlice } from 'features/parameters/store/generationSlice'; @@ -55,7 +54,6 @@ const allReducers = { [deleteImageModalSlice.name]: deleteImageModalSlice.reducer, [changeBoardModalSlice.name]: changeBoardModalSlice.reducer, [loraSlice.name]: loraSlice.reducer, - [modelManagerSlice.name]: modelManagerSlice.reducer, [modelManagerV2Slice.name]: modelManagerV2Slice.reducer, [sdxlSlice.name]: sdxlSlice.reducer, [queueSlice.name]: queueSlice.reducer, @@ -103,7 +101,7 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = { [dynamicPromptsPersistConfig.name]: dynamicPromptsPersistConfig, [sdxlPersistConfig.name]: sdxlPersistConfig, [loraPersistConfig.name]: loraPersistConfig, - [modelManagerPersistConfig.name]: modelManagerPersistConfig, + [modelManagerV2PersistConfig.name]: modelManagerV2PersistConfig, [hrfPersistConfig.name]: hrfPersistConfig, }; diff --git a/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsButton.tsx b/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsButton.tsx deleted file mode 100644 index 8a49bc2585..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsButton.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { ButtonProps } from '@invoke-ai/ui-library'; -import { Button } from '@invoke-ai/ui-library'; -import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { PiArrowsClockwiseBold } from 'react-icons/pi'; - -import { useSyncModels } from './useSyncModels'; - -export const SyncModelsButton = memo((props: Omit) => { - const { t } = useTranslation(); - const { syncModels, isLoading } = useSyncModels(); - const isSyncModelEnabled = useFeatureStatus('syncModels').isFeatureEnabled; - - if (!isSyncModelEnabled) { - return null; - } - - return ( - - ); -}); - -SyncModelsButton.displayName = 'SyncModelsButton'; diff --git a/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsIconButton.tsx b/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsIconButton.tsx deleted file mode 100644 index 986a99bd0b..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsIconButton.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type { IconButtonProps } from '@invoke-ai/ui-library'; -import { IconButton } from '@invoke-ai/ui-library'; -import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { PiArrowsClockwiseBold } from 'react-icons/pi'; - -import { useSyncModels } from './useSyncModels'; - -export const SyncModelsIconButton = memo((props: Omit) => { - const { t } = useTranslation(); - const { syncModels, isLoading } = useSyncModels(); - const isSyncModelEnabled = useFeatureStatus('syncModels').isFeatureEnabled; - - if (!isSyncModelEnabled) { - return null; - } - - return ( - } - tooltip={t('modelManager.syncModels')} - aria-label={t('modelManager.syncModels')} - isLoading={isLoading} - onClick={syncModels} - size="sm" - variant="ghost" - {...props} - /> - ); -}); - -SyncModelsIconButton.displayName = 'SyncModelsIconButton'; diff --git a/invokeai/frontend/web/src/features/modelManager/components/SyncModels/useSyncModels.ts b/invokeai/frontend/web/src/features/modelManager/components/SyncModels/useSyncModels.ts deleted file mode 100644 index 3ccaba66a5..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/components/SyncModels/useSyncModels.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { useAppDispatch } from 'app/store/storeHooks'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSyncModelsMutation } from 'services/api/endpoints/models'; - -export const useSyncModels = () => { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - const [_syncModels, { isLoading }] = useSyncModelsMutation(); - const syncModels = useCallback(() => { - _syncModels() - .unwrap() - .then((_) => { - dispatch( - addToast( - makeToast({ - title: `${t('modelManager.modelsSynced')}`, - status: 'success', - }) - ) - ); - }) - .catch((error) => { - if (error) { - dispatch( - addToast( - makeToast({ - title: `${t('modelManager.modelSyncFailed')}`, - status: 'error', - }) - ) - ); - } - }); - }, [dispatch, _syncModels, t]); - - return { syncModels, isLoading }; -}; diff --git a/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts b/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts deleted file mode 100644 index c450e64b3c..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { PayloadAction } from '@reduxjs/toolkit'; -import { createSlice } from '@reduxjs/toolkit'; -import type { PersistConfig, RootState } from 'app/store/store'; - -type ModelManagerState = { - _version: 1; - searchFolder: string | null; - advancedAddScanModel: string | null; -}; - -export const initialModelManagerState: ModelManagerState = { - _version: 1, - searchFolder: null, - advancedAddScanModel: null, -}; - -export const modelManagerSlice = createSlice({ - name: 'modelmanager', - initialState: initialModelManagerState, - reducers: { - setSearchFolder: (state, action: PayloadAction) => { - state.searchFolder = action.payload; - }, - setAdvancedAddScanModel: (state, action: PayloadAction) => { - state.advancedAddScanModel = action.payload; - }, - }, -}); - -export const { setSearchFolder, setAdvancedAddScanModel } = modelManagerSlice.actions; - -export const selectModelManagerSlice = (state: RootState) => state.modelmanager; - -/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -export const migrateModelManagerState = (state: any): any => { - if (!('_version' in state)) { - state._version = 1; - } - return state; -}; - -export const modelManagerPersistConfig: PersistConfig = { - name: modelManagerSlice.name, - initialState: initialModelManagerState, - migrate: migrateModelManagerState, - persistDenylist: [], -}; diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AddModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AddModels.tsx deleted file mode 100644 index cc496674f0..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AddModels.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Button, ButtonGroup, Flex, Text } from '@invoke-ai/ui-library'; -import { memo, useCallback, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useGetModelImportsQuery } from 'services/api/endpoints/models'; - -import AdvancedAddModels from './AdvancedAddModels'; -import SimpleAddModels from './SimpleAddModels'; - -const AddModels = () => { - const { t } = useTranslation(); - const [addModelMode, setAddModelMode] = useState<'simple' | 'advanced'>('simple'); - const handleAddModelSimple = useCallback(() => setAddModelMode('simple'), []); - const handleAddModelAdvanced = useCallback(() => setAddModelMode('advanced'), []); - const { data } = useGetModelImportsQuery(); - console.log({ data }); - return ( - - - - - - - {addModelMode === 'simple' && } - {addModelMode === 'advanced' && } - - {data?.map((model) => {model.status})} - - ); -}; - -export default memo(AddModels); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx deleted file mode 100644 index 31f4312eb5..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import { Button, Checkbox, Flex, FormControl, FormErrorMessage, FormLabel, Input } from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { setAdvancedAddScanModel } from 'features/modelManager/store/modelManagerSlice'; -import BaseModelSelect from 'features/modelManager/subpanels/shared/BaseModelSelect'; -import CheckpointConfigsSelect from 'features/modelManager/subpanels/shared/CheckpointConfigsSelect'; -import ModelVariantSelect from 'features/modelManager/subpanels/shared/ModelVariantSelect'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import type { CSSProperties, FocusEventHandler } from 'react'; -import { memo, useCallback, useState } from 'react'; -import type { SubmitHandler } from 'react-hook-form'; -import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useAddMainModelsMutation } from 'services/api/endpoints/models'; -import type { CheckpointModelConfig } from 'services/api/types'; - -import { getModelName } from './util'; - -type AdvancedAddCheckpointProps = { - model_path?: string; -}; - -const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => { - const { t } = useTranslation(); - const dispatch = useAppDispatch(); - const { model_path } = props; - - const { - register, - handleSubmit, - control, - getValues, - setValue, - formState: { errors }, - reset, - } = useForm({ - defaultValues: { - model_name: model_path ? getModelName(model_path) : '', - 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', - }, - mode: 'onChange', - }); - - const [addMainModel] = useAddMainModelsMutation(); - - const [useCustomConfig, setUseCustomConfig] = useState(false); - - const onSubmit = useCallback>( - (values) => { - addMainModel({ - body: values, - }) - .unwrap() - .then((_) => { - dispatch( - addToast( - makeToast({ - title: t('modelManager.modelAdded', { - modelName: values.model_name, - }), - status: 'success', - }) - ) - ); - reset(); - - // Close Advanced Panel in Scan Models tab - if (model_path) { - dispatch(setAdvancedAddScanModel(null)); - } - }) - .catch((error) => { - if (error) { - dispatch( - addToast( - makeToast({ - title: t('toast.modelAddFailed'), - status: 'error', - }) - ) - ); - } - }); - }, - [addMainModel, dispatch, model_path, reset, t] - ); - - const onBlur: FocusEventHandler = useCallback( - (e) => { - if (getValues().model_name === '') { - const modelName = getModelName(e.currentTarget.value); - if (modelName) { - setValue('model_name', modelName as string); - } - } - }, - [getValues, setValue] - ); - - const handleChangeUseCustomConfig = useCallback(() => setUseCustomConfig((prev) => !prev), []); - - return ( -
- - - {t('modelManager.model')} - value.trim().length > 3 || 'Must be at least 3 characters', - })} - /> - {errors.model_name?.message && {errors.model_name?.message}} - - control={control} name="base_model" /> - - {t('modelManager.modelLocation')} - value.trim().length > 0 || 'Must provide a path', - onBlur, - })} - /> - {errors.path?.message && {errors.path?.message}} - - - {t('modelManager.description')} - - - - {t('modelManager.vaeLocation')} - - - control={control} name="variant" /> - - {!useCustomConfig ? ( - - ) : ( - - {t('modelManager.config')} - - - )} - - {t('modelManager.useCustomConfig')} - - - - - -
- ); -}; - -const formStyles: CSSProperties = { - width: '100%', -}; - -export default memo(AdvancedAddCheckpoint); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx deleted file mode 100644 index 07b28d35fd..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import { Button, Flex, FormControl, FormErrorMessage, FormLabel, Input } from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { setAdvancedAddScanModel } from 'features/modelManager/store/modelManagerSlice'; -import BaseModelSelect from 'features/modelManager/subpanels/shared/BaseModelSelect'; -import ModelVariantSelect from 'features/modelManager/subpanels/shared/ModelVariantSelect'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import type { CSSProperties, FocusEventHandler } from 'react'; -import { memo, useCallback } from 'react'; -import type { SubmitHandler } from 'react-hook-form'; -import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useAddMainModelsMutation } from 'services/api/endpoints/models'; -import type { DiffusersModelConfig } from 'services/api/types'; - -import { getModelName } from './util'; - -type AdvancedAddDiffusersProps = { - model_path?: string; -}; - -const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => { - const { t } = useTranslation(); - const dispatch = useAppDispatch(); - const { model_path } = props; - - const [addMainModel] = useAddMainModelsMutation(); - - const { - register, - handleSubmit, - control, - getValues, - setValue, - formState: { errors }, - reset, - } = useForm({ - defaultValues: { - model_name: model_path ? getModelName(model_path, false) : '', - base_model: 'sd-1', - model_type: 'main', - path: model_path ? model_path : '', - description: '', - model_format: 'diffusers', - error: undefined, - vae: '', - variant: 'normal', - }, - mode: 'onChange', - }); - - const onSubmit = useCallback>( - (values) => { - addMainModel({ - body: values, - }) - .unwrap() - .then((_) => { - dispatch( - addToast( - makeToast({ - title: t('modelManager.modelAdded', { - modelName: values.model_name, - }), - status: 'success', - }) - ) - ); - reset(); - // Close Advanced Panel in Scan Models tab - if (model_path) { - dispatch(setAdvancedAddScanModel(null)); - } - }) - .catch((error) => { - if (error) { - dispatch( - addToast( - makeToast({ - title: t('toast.modelAddFailed'), - status: 'error', - }) - ) - ); - } - }); - }, - [addMainModel, dispatch, model_path, reset, t] - ); - - const onBlur: FocusEventHandler = useCallback( - (e) => { - if (getValues().model_name === '') { - const modelName = getModelName(e.currentTarget.value, false); - if (modelName) { - setValue('model_name', modelName as string); - } - } - }, - [getValues, setValue] - ); - - return ( -
- - - {t('modelManager.name')} - value.trim().length > 3 || 'Must be at least 3 characters', - })} - /> - {errors.model_name?.message && {errors.model_name?.message}} - - control={control} name="base_model" /> - - {t('modelManager.modelLocation')} - value.trim().length > 0 || 'Must provide a path', - onBlur, - })} - /> - {errors.path?.message && {errors.path?.message}} - - - {t('modelManager.description')} - - - - {t('modelManager.vaeLocation')} - - - control={control} name="variant" /> - - - -
- ); -}; - -const formStyles: CSSProperties = { - width: '100%', -}; - -export default memo(AdvancedAddDiffusers); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddModels.tsx deleted file mode 100644 index fc03c6bb1f..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddModels.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library'; -import { Combobox, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library'; -import { memo, useCallback, useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { z } from 'zod'; - -import AdvancedAddCheckpoint from './AdvancedAddCheckpoint'; -import AdvancedAddDiffusers from './AdvancedAddDiffusers'; - -export const zManualAddMode = z.enum(['diffusers', 'checkpoint']); -export type ManualAddMode = z.infer; -export const isManualAddMode = (v: unknown): v is ManualAddMode => zManualAddMode.safeParse(v).success; - -const AdvancedAddModels = () => { - const [advancedAddMode, setAdvancedAddMode] = useState('diffusers'); - - const { t } = useTranslation(); - const handleChange: ComboboxOnChange = useCallback((v) => { - if (!isManualAddMode(v?.value)) { - return; - } - setAdvancedAddMode(v.value); - }, []); - - const options: ComboboxOption[] = useMemo( - () => [ - { label: t('modelManager.diffusersModels'), value: 'diffusers' }, - { label: t('modelManager.checkpointOrSafetensors'), value: 'checkpoint' }, - ], - [t] - ); - - const value = useMemo(() => options.find((o) => o.value === advancedAddMode), [options, advancedAddMode]); - - return ( - - - {t('modelManager.modelType')} - - - - - {advancedAddMode === 'diffusers' && } - {advancedAddMode === 'checkpoint' && } - - - ); -}; - -export default memo(AdvancedAddModels); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/FoundModelsList.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/FoundModelsList.tsx deleted file mode 100644 index 206a955e29..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/FoundModelsList.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import { Button, Flex, FormControl, FormLabel, Input, Text } from '@invoke-ai/ui-library'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; -import { setAdvancedAddScanModel } from 'features/modelManager/store/modelManagerSlice'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import { difference, forEach, intersection, map, values } from 'lodash-es'; -import type { ChangeEvent, MouseEvent } from 'react'; -import { memo, useCallback, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { ALL_BASE_MODELS } from 'services/api/constants'; -import type { SearchFolderResponse } from 'services/api/endpoints/models'; -import { - useGetMainModelsQuery, - useGetModelsInFolderQuery, - useImportMainModelsMutation, -} from 'services/api/endpoints/models'; - -const FoundModelsList = () => { - const searchFolder = useAppSelector((s) => s.modelmanager.searchFolder); - const [nameFilter, setNameFilter] = useState(''); - - // Get paths of models that are already installed - const { data: installedModels } = useGetMainModelsQuery(ALL_BASE_MODELS); - - // 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) => { - 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: t('toast.modelAddFailed'), - status: 'error', - }) - ) - ); - } - }); - }, - [dispatch, importMainModel, t] - ); - - const handleSearchFilter = useCallback((e: ChangeEvent) => { - setNameFilter(e.target.value); - }, []); - - const handleClickSetAdvanced = useCallback((model: string) => dispatch(setAdvancedAddScanModel(model)), [dispatch]); - - const renderModels = ({ models, showActions = true }: { models: string[]; showActions?: boolean }) => { - return models.map((model) => { - return ( - - - {model.split('\\').slice(-1)[0]} - - {model} - - - {showActions ? ( - - - - - ) : ( - - {t('common.installed')} - - )} - - ); - }); - }; - - const renderFoundModels = () => { - if (!searchFolder) { - return null; - } - - if (!foundModels || foundModels.length === 0) { - return ( - - {t('modelManager.noModels')} - - ); - } - - return ( - - - {t('modelManager.search')} - - - - - {t('modelManager.modelsFound')}: {foundModels.length} - - - {t('common.notInstalled')}: {filteredModels.length} - - - - - - {renderModels({ models: filteredModels })} - {renderModels({ models: alreadyInstalled, showActions: false })} - - - - ); - }; - - return renderFoundModels(); -}; - -const foundModelsFilter = (data: SearchFolderResponse | undefined, nameFilter: string) => { - const filteredModels: SearchFolderResponse = []; - forEach(data, (model) => { - if (!model) { - return null; - } - - if (model.includes(nameFilter)) { - filteredModels.push(model); - } - }); - return filteredModels; -}; - -export default memo(FoundModelsList); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx deleted file mode 100644 index 445eb7ed5a..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library'; -import { Box, Combobox, Flex, FormControl, FormLabel, IconButton, Text } from '@invoke-ai/ui-library'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { setAdvancedAddScanModel } from 'features/modelManager/store/modelManagerSlice'; -import { memo, useCallback, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { PiXBold } from 'react-icons/pi'; - -import AdvancedAddCheckpoint from './AdvancedAddCheckpoint'; -import AdvancedAddDiffusers from './AdvancedAddDiffusers'; -import type { ManualAddMode } from './AdvancedAddModels'; -import { isManualAddMode } from './AdvancedAddModels'; - -const ScanAdvancedAddModels = () => { - const advancedAddScanModel = useAppSelector((s) => s.modelmanager.advancedAddScanModel); - - const { t } = useTranslation(); - - const options: ComboboxOption[] = useMemo( - () => [ - { label: t('modelManager.diffusersModels'), value: 'diffusers' }, - { label: t('modelManager.checkpointOrSafetensors'), value: 'checkpoint' }, - ], - [t] - ); - - const [advancedAddMode, setAdvancedAddMode] = useState('diffusers'); - - const [isCheckpoint, setIsCheckpoint] = useState(true); - - useEffect(() => { - advancedAddScanModel && ['.ckpt', '.safetensors', '.pth', '.pt'].some((ext) => advancedAddScanModel.endsWith(ext)) - ? setAdvancedAddMode('checkpoint') - : setAdvancedAddMode('diffusers'); - }, [advancedAddScanModel, setAdvancedAddMode, isCheckpoint]); - - const dispatch = useAppDispatch(); - - const handleClickSetAdvanced = useCallback(() => dispatch(setAdvancedAddScanModel(null)), [dispatch]); - - const handleChangeAddMode = useCallback((v) => { - if (!isManualAddMode(v?.value)) { - return; - } - setAdvancedAddMode(v.value); - if (v.value === 'checkpoint') { - setIsCheckpoint(true); - } else { - setIsCheckpoint(false); - } - }, []); - - const value = useMemo(() => options.find((o) => o.value === advancedAddMode), [options, advancedAddMode]); - - if (!advancedAddScanModel) { - return null; - } - - return ( - - - - {isCheckpoint || advancedAddMode === 'checkpoint' ? 'Add Checkpoint Model' : 'Add Diffusers Model'} - - } - aria-label={t('modelManager.closeAdvanced')} - onClick={handleClickSetAdvanced} - size="sm" - /> - - - {t('modelManager.modelType')} - - - {isCheckpoint ? ( - - ) : ( - - )} - - ); -}; - -export default memo(ScanAdvancedAddModels); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanModels.tsx deleted file mode 100644 index f3287270a2..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanModels.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Flex } from '@invoke-ai/ui-library'; -import { memo } from 'react'; - -import FoundModelsList from './FoundModelsList'; -import ScanAdvancedAddModels from './ScanAdvancedAddModels'; -import SearchFolderForm from './SearchFolderForm'; - -const ScanModels = () => { - return ( - - - - - - - - - - ); -}; - -export default memo(ScanModels); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SearchFolderForm.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SearchFolderForm.tsx deleted file mode 100644 index dc1d39f7aa..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SearchFolderForm.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { Flex, IconButton, Input, Text } from '@invoke-ai/ui-library'; -import { useForm } from '@mantine/form'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { setAdvancedAddScanModel, setSearchFolder } from 'features/modelManager/store/modelManagerSlice'; -import type { CSSProperties } from 'react'; -import { memo, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { PiArrowsCounterClockwiseBold, PiMagnifyingGlassBold, PiTrashSimpleBold } from 'react-icons/pi'; -import { useGetModelsInFolderQuery } from 'services/api/endpoints/models'; - -type SearchFolderForm = { - folder: string; -}; - -function SearchFolderForm() { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const searchFolder = useAppSelector((s) => s.modelmanager.searchFolder); - - const { refetch: refetchFoundModels } = useGetModelsInFolderQuery({ - search_path: searchFolder ? searchFolder : '', - }); - - const searchFolderForm = useForm({ - initialValues: { - folder: '', - }, - }); - - const searchFolderFormSubmitHandler = useCallback( - (values: SearchFolderForm) => { - dispatch(setSearchFolder(values.folder)); - }, - [dispatch] - ); - - const scanAgainHandler = useCallback(() => { - refetchFoundModels(); - }, [refetchFoundModels]); - - const handleClickClearCheckpointFolder = useCallback(() => { - dispatch(setSearchFolder(null)); - dispatch(setAdvancedAddScanModel(null)); - }, [dispatch]); - - return ( -
searchFolderFormSubmitHandler(values))} style={formStyles}> - - - - {t('common.folder')} - - {!searchFolder ? ( - - ) : ( - - {searchFolder} - - )} - - - - {!searchFolder ? ( - } - fontSize={18} - size="sm" - type="submit" - /> - ) : ( - } - onClick={scanAgainHandler} - fontSize={18} - size="sm" - /> - )} - - } - size="sm" - onClick={handleClickClearCheckpointFolder} - isDisabled={!searchFolder} - colorScheme="red" - /> - - -
- ); -} - -export default memo(SearchFolderForm); - -const formStyles: CSSProperties = { - width: '100%', -}; diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SimpleAddModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SimpleAddModels.tsx deleted file mode 100644 index 0124d6d570..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SimpleAddModels.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import type { ComboboxOption } from '@invoke-ai/ui-library'; -import { Button, Combobox, Flex, FormControl, FormLabel, Input } from '@invoke-ai/ui-library'; -import { useForm } from '@mantine/form'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import type { CSSProperties } from 'react'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useImportMainModelsMutation } from 'services/api/endpoints/models'; - -const options: ComboboxOption[] = [ - { 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; -}; - -const SimpleAddModels = () => { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const [importMainModel, { isLoading }] = useImportMainModelsMutation(); - - const addModelForm = useForm({ - initialValues: { - location: '', - prediction_type: undefined, - }, - }); - - const handleAddModelSubmit = (values: ExtendedImportModelConfig) => { - const importModelResponseBody = { - config: values.prediction_type === 'none' ? undefined : values.prediction_type, - }; - - importMainModel({ source: values.location, config: importModelResponseBody }) - .unwrap() - .then((_) => { - dispatch( - addToast( - makeToast({ - title: t('toast.modelAddedSimple'), - status: 'success', - }) - ) - ); - addModelForm.reset(); - }) - .catch((error) => { - if (error) { - dispatch( - addToast( - makeToast({ - title: `${error.data.detail} `, - status: 'error', - }) - ) - ); - } - }); - }; - - return ( -
handleAddModelSubmit(v))} style={formStyles}> - - - {t('modelManager.modelLocation')} - - - - {t('modelManager.predictionType')} - - - - -
- ); -}; - -const formStyles: CSSProperties = { - width: '100%', -}; - -export default memo(SimpleAddModels); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/util.ts b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/util.ts deleted file mode 100644 index 7314a86908..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/util.ts +++ /dev/null @@ -1,15 +0,0 @@ -export function getModelName(filepath: string, isCheckpoint: boolean = true) { - let regex; - if (isCheckpoint) { - regex = new RegExp('[^\\\\/]+(?=\\.)'); - } else { - regex = new RegExp('[^\\\\/]+(?=[\\\\/]?$)'); - } - - const match = filepath.match(regex); - if (match) { - return match[0]; - } else { - return ''; - } -} diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ImportModelsPanel.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ImportModelsPanel.tsx deleted file mode 100644 index 960f798e79..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/ImportModelsPanel.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Button, ButtonGroup, Flex } from '@invoke-ai/ui-library'; -import { memo, useCallback, useState } from 'react'; -import { useTranslation } from 'react-i18next'; - -import AddModels from './AddModelsPanel/AddModels'; -import ScanModels from './AddModelsPanel/ScanModels'; - -type AddModelTabs = 'add' | 'scan'; - -const ImportModelsPanel = () => { - const [addModelTab, setAddModelTab] = useState('add'); - const { t } = useTranslation(); - - const handleClickAddTab = useCallback(() => setAddModelTab('add'), []); - const handleClickScanTab = useCallback(() => setAddModelTab('scan'), []); - - return ( - - - - - - - {addModelTab === 'add' && } - {addModelTab === 'scan' && } - - ); -}; - -export default memo(ImportModelsPanel); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/MergeModelsPanel.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/MergeModelsPanel.tsx deleted file mode 100644 index fe90db5b8a..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/MergeModelsPanel.tsx +++ /dev/null @@ -1,352 +0,0 @@ -import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library'; -import { - Button, - Checkbox, - Combobox, - CompositeNumberInput, - CompositeSlider, - Flex, - FormControl, - FormHelperText, - FormLabel, - Input, - Radio, - RadioGroup, - Text, - Tooltip, -} from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import { pickBy } from 'lodash-es'; -import type { ChangeEvent } from 'react'; -import { memo, useCallback, useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { ALL_BASE_MODELS } from 'services/api/constants'; -import { useGetMainModelsQuery, useMergeMainModelsMutation } from 'services/api/endpoints/models'; -import type { BaseModelType, MergeModelConfig } from 'services/api/types'; - -const baseModelTypeSelectOptions: ComboboxOption[] = [ - { label: 'Stable Diffusion 1', value: 'sd-1' }, - { label: 'Stable Diffusion 2', value: 'sd-2' }, -]; - -type MergeInterpolationMethods = 'weighted_sum' | 'sigmoid' | 'inv_sigmoid' | 'add_difference'; - -const MergeModelsPanel = () => { - const { t } = useTranslation(); - const dispatch = useAppDispatch(); - - const { data } = useGetMainModelsQuery(ALL_BASE_MODELS); - - const [mergeModels, { isLoading }] = useMergeMainModelsMutation(); - - const [baseModel, setBaseModel] = useState('sd-1'); - const valueBaseModel = useMemo(() => baseModelTypeSelectOptions.find((o) => o.value === baseModel), [baseModel]); - const sd1DiffusersModels = pickBy( - data?.entities, - (value, _) => value?.model_format === 'diffusers' && value?.base_model === 'sd-1' - ); - - const sd2DiffusersModels = pickBy( - data?.entities, - (value, _) => value?.model_format === 'diffusers' && value?.base_model === 'sd-2' - ); - - const modelsMap = useMemo(() => { - return { - 'sd-1': sd1DiffusersModels, - 'sd-2': sd2DiffusersModels, - }; - }, [sd1DiffusersModels, sd2DiffusersModels]); - - const [modelOne, setModelOne] = useState( - Object.keys(modelsMap[baseModel as keyof typeof modelsMap])?.[0] ?? null - ); - const [modelTwo, setModelTwo] = useState( - Object.keys(modelsMap[baseModel as keyof typeof modelsMap])?.[1] ?? null - ); - const [modelThree, setModelThree] = useState(null); - - const [mergedModelName, setMergedModelName] = useState(''); - const [modelMergeAlpha, setModelMergeAlpha] = useState(0.5); - - const [modelMergeInterp, setModelMergeInterp] = useState('weighted_sum'); - - const [modelMergeSaveLocType, setModelMergeSaveLocType] = useState<'root' | 'custom'>('root'); - - const [modelMergeCustomSaveLoc, setModelMergeCustomSaveLoc] = useState(''); - - const [modelMergeForce, setModelMergeForce] = useState(false); - - const optionsModelOne = useMemo( - () => - Object.keys(modelsMap[baseModel as keyof typeof modelsMap]) - .filter((model) => model !== modelTwo && model !== modelThree) - .map((model) => ({ label: model, value: model })), - [modelsMap, baseModel, modelTwo, modelThree] - ); - - const optionsModelTwo = useMemo( - () => - Object.keys(modelsMap[baseModel as keyof typeof modelsMap]) - .filter((model) => model !== modelOne && model !== modelThree) - .map((model) => ({ label: model, value: model })), - [modelsMap, baseModel, modelOne, modelThree] - ); - - const optionsModelThree = useMemo( - () => - Object.keys(modelsMap[baseModel as keyof typeof modelsMap]) - .filter((model) => model !== modelOne && model !== modelTwo) - .map((model) => ({ label: model, value: model })), - [modelsMap, baseModel, modelOne, modelTwo] - ); - - const onChangeBaseModel = useCallback((v) => { - if (!v) { - return; - } - if (!(v.value === 'sd-1' || v.value === 'sd-2')) { - return; - } - setBaseModel(v.value); - setModelOne(null); - setModelTwo(null); - }, []); - - const onChangeModelOne = useCallback((v) => { - if (!v) { - return; - } - setModelOne(v.value); - }, []); - const onChangeModelTwo = useCallback((v) => { - if (!v) { - return; - } - setModelTwo(v.value); - }, []); - const onChangeModelThree = useCallback((v) => { - if (!v) { - setModelThree(null); - setModelMergeInterp('add_difference'); - } else { - setModelThree(v.value); - setModelMergeInterp('weighted_sum'); - } - }, []); - - const valueModelOne = useMemo(() => optionsModelOne.find((o) => o.value === modelOne), [modelOne, optionsModelOne]); - const valueModelTwo = useMemo(() => optionsModelTwo.find((o) => o.value === modelTwo), [modelTwo, optionsModelTwo]); - const valueModelThree = useMemo( - () => optionsModelThree.find((o) => o.value === modelThree), - [modelThree, optionsModelThree] - ); - - const handleChangeMergedModelName = useCallback( - (e: ChangeEvent) => setMergedModelName(e.target.value), - [] - ); - const handleChangeModelMergeAlpha = useCallback((v: number) => setModelMergeAlpha(v), []); - const handleResetModelMergeAlpha = useCallback(() => setModelMergeAlpha(0.5), []); - const handleChangeMergeInterp = useCallback((v: MergeInterpolationMethods) => setModelMergeInterp(v), []); - const handleChangeMergeSaveLocType = useCallback((v: 'root' | 'custom') => setModelMergeSaveLocType(v), []); - const handleChangeMergeCustomSaveLoc = useCallback( - (e: ChangeEvent) => setModelMergeCustomSaveLoc(e.target.value), - [] - ); - const handleChangeModelMergeForce = useCallback( - (e: ChangeEvent) => setModelMergeForce(e.target.checked), - [] - ); - - const mergeModelsHandler = useCallback(() => { - const models_names: string[] = []; - - let modelsToMerge: (string | null)[] = [modelOne, modelTwo, modelThree]; - modelsToMerge = modelsToMerge.filter((model) => model !== null); - modelsToMerge.forEach((model) => { - const n = model?.split('/')?.[2]; - if (n) { - models_names.push(n); - } - }); - - const mergeModelsInfo: MergeModelConfig['body'] = { - model_names: models_names, - merged_model_name: mergedModelName !== '' ? mergedModelName : models_names.join('-'), - alpha: modelMergeAlpha, - interp: modelMergeInterp, - force: modelMergeForce, - merge_dest_directory: modelMergeSaveLocType === 'root' ? undefined : modelMergeCustomSaveLoc, - }; - - mergeModels({ - base_model: baseModel, - body: { body: mergeModelsInfo }, - }) - .unwrap() - .then((_) => { - dispatch( - addToast( - makeToast({ - title: t('modelManager.modelsMerged'), - status: 'success', - }) - ) - ); - }) - .catch((error) => { - if (error) { - dispatch( - addToast( - makeToast({ - title: t('modelManager.modelsMergeFailed'), - status: 'error', - }) - ) - ); - } - }); - }, [ - baseModel, - dispatch, - mergeModels, - mergedModelName, - modelMergeAlpha, - modelMergeCustomSaveLoc, - modelMergeForce, - modelMergeInterp, - modelMergeSaveLocType, - modelOne, - modelThree, - modelTwo, - t, - ]); - - return ( - - - {t('modelManager.modelMergeHeaderHelp1')} - - {t('modelManager.modelMergeHeaderHelp2')} - - - - - - {t('modelManager.modelType')} - - - - {t('modelManager.modelOne')} - - - - {t('modelManager.modelTwo')} - - - - {t('modelManager.modelThree')} - - - - - - {t('modelManager.mergedModelName')} - - - - - - {t('modelManager.alpha')} - - - {t('modelManager.modelMergeAlphaHelp')} - - - - - - {t('modelManager.interpolationType')} - - - - {modelThree === null ? ( - <> - - {t('modelManager.weightedSum')} - - - {t('modelManager.sigmoid')} - - - {t('modelManager.inverseSigmoid')} - - - ) : ( - - - {t('modelManager.addDifference')} - - - )} - - - - - - - - {t('modelManager.mergedModelSaveLocation')} - - - - - {t('modelManager.invokeAIFolder')} - - - - {t('modelManager.custom')} - - - - - - {modelMergeSaveLocType === 'custom' && ( - - {t('modelManager.mergedModelCustomSaveLocation')} - - - )} - - - - {t('modelManager.ignoreMismatch')} - - - - - - ); -}; - -export default memo(MergeModelsPanel); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel.tsx deleted file mode 100644 index 06b5b7db36..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { Flex, Text } from '@invoke-ai/ui-library'; -import { memo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { ALL_BASE_MODELS } from 'services/api/constants'; -import { useGetLoRAModelsQuery, useGetMainModelsQuery } from 'services/api/endpoints/models'; -import type { DiffusersModelConfig, LoRAConfig, MainModelConfig } from 'services/api/types'; - -import CheckpointModelEdit from './ModelManagerPanel/CheckpointModelEdit'; -import DiffusersModelEdit from './ModelManagerPanel/DiffusersModelEdit'; -import LoRAModelEdit from './ModelManagerPanel/LoRAModelEdit'; -import ModelList from './ModelManagerPanel/ModelList'; - -const ModelManagerPanel = () => { - const [selectedModelId, setSelectedModelId] = useState(); - const { mainModel } = useGetMainModelsQuery(ALL_BASE_MODELS, { - selectFromResult: ({ data }) => ({ - mainModel: selectedModelId ? data?.entities[selectedModelId] : undefined, - }), - }); - const { loraModel } = useGetLoRAModelsQuery(undefined, { - selectFromResult: ({ data }) => ({ - loraModel: selectedModelId ? data?.entities[selectedModelId] : undefined, - }), - }); - - const model = mainModel ? mainModel : loraModel; - - return ( - - - - - ); -}; - -type ModelEditProps = { - model: MainModelConfig | LoRAConfig | undefined; -}; - -const ModelEdit = (props: ModelEditProps) => { - const { t } = useTranslation(); - const { model } = props; - - if (model?.format === 'checkpoint') { - return ; - } - - if (model?.format === 'diffusers') { - return ; - } - - if (model?.type === 'lora') { - return ; - } - - return ( - - {t('modelManager.noModelSelected')} - - ); -}; - -export default memo(ModelManagerPanel); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx deleted file mode 100644 index c24d660cc6..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx +++ /dev/null @@ -1,185 +0,0 @@ -import { - Badge, - Button, - Checkbox, - Divider, - Flex, - FormControl, - FormErrorMessage, - FormLabel, - Input, - Text, -} from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import BaseModelSelect from 'features/modelManager/subpanels/shared/BaseModelSelect'; -import CheckpointConfigsSelect from 'features/modelManager/subpanels/shared/CheckpointConfigsSelect'; -import ModelVariantSelect from 'features/modelManager/subpanels/shared/ModelVariantSelect'; -import { MODEL_TYPE_MAP } from 'features/parameters/types/constants'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import { memo, useCallback, useEffect, useState } from 'react'; -import type { SubmitHandler } from 'react-hook-form'; -import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useGetCheckpointConfigsQuery, useUpdateModelsMutation } from 'services/api/endpoints/models'; -import type { CheckpointModelConfig } from 'services/api/types'; - -import ModelConvert from './ModelConvert'; - -type CheckpointModelEditProps = { - model: CheckpointModelConfig; -}; - -const CheckpointModelEdit = (props: CheckpointModelEditProps) => { - const { model } = props; - - const [updateModel, { isLoading }] = useUpdateModelsMutation(); - const { data: availableCheckpointConfigs } = useGetCheckpointConfigsQuery(); - - const [useCustomConfig, setUseCustomConfig] = useState(false); - - useEffect(() => { - if (!availableCheckpointConfigs?.includes(model.config)) { - setUseCustomConfig(true); - } - }, [availableCheckpointConfigs, model.config]); - - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const { - register, - handleSubmit, - control, - formState: { errors }, - reset, - } = useForm({ - defaultValues: { - name: model.name ? model.name : '', - base: model.base, - type: 'main', - path: model.path ? model.path : '', - description: model.description ? model.description : '', - format: 'checkpoint', - vae: model.vae ? model.vae : '', - config: model.config ? model.config : '', - variant: model.variant, - }, - mode: 'onChange', - }); - - const handleChangeUseCustomConfig = useCallback(() => setUseCustomConfig((prev) => !prev), []); - - const onSubmit = useCallback>( - (values) => { - const responseBody = { - key: model.key, - body: values, - }; - updateModel(responseBody) - .unwrap() - .then((payload) => { - reset(payload as CheckpointModelConfig, { keepDefaultValues: true }); - dispatch( - addToast( - makeToast({ - title: t('modelManager.modelUpdated'), - status: 'success', - }) - ) - ); - }) - .catch((_) => { - reset(); - dispatch( - addToast( - makeToast({ - title: t('modelManager.modelUpdateFailed'), - status: 'error', - }) - ) - ); - }); - }, - [dispatch, model.key, reset, t, updateModel] - ); - - return ( - - - - - {model.name} - - - {MODEL_TYPE_MAP[model.base]} {t('modelManager.model')} - - - {![''].includes(model.base) ? ( - - ) : ( - - {t('modelManager.conversionNotSupported')} - - )} - - - - -
- - - {t('modelManager.name')} - value.trim().length > 3 || 'Must be at least 3 characters', - })} - /> - {errors.name?.message && {errors.name?.message}} - - - {t('modelManager.description')} - - - control={control} name="base" /> - control={control} name="variant" /> - - {t('modelManager.modelLocation')} - value.trim().length > 0 || 'Must provide a path', - })} - /> - {errors.path?.message && {errors.path?.message}} - - - {t('modelManager.vaeLocation')} - - - - - {!useCustomConfig ? ( - - ) : ( - - {t('modelManager.config')} - - - )} - - {t('modelManager.useCustomConfig')} - - - - - - -
-
-
- ); -}; - -export default memo(CheckpointModelEdit); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx deleted file mode 100644 index b5023f0eff..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { Button, Divider, Flex, FormControl, FormErrorMessage, FormLabel, Input, Text } from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import BaseModelSelect from 'features/modelManager/subpanels/shared/BaseModelSelect'; -import ModelVariantSelect from 'features/modelManager/subpanels/shared/ModelVariantSelect'; -import { MODEL_TYPE_MAP } from 'features/parameters/types/constants'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import { memo, useCallback } from 'react'; -import type { SubmitHandler } from 'react-hook-form'; -import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useUpdateModelsMutation } from 'services/api/endpoints/models'; -import type { DiffusersModelConfig } from 'services/api/types'; - -type DiffusersModelEditProps = { - model: DiffusersModelConfig; -}; - -const DiffusersModelEdit = (props: DiffusersModelEditProps) => { - const { model } = props; - - const [updateModel, { isLoading }] = useUpdateModelsMutation(); - - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const { - register, - handleSubmit, - control, - formState: { errors }, - reset, - } = useForm({ - defaultValues: { - name: model.name ? model.name : '', - base: model.base, - type: 'main', - path: model.path ? model.path : '', - description: model.description ? model.description : '', - format: 'diffusers', - vae: model.vae ? model.vae : '', - variant: model.variant, - }, - mode: 'onChange', - }); - - const onSubmit = useCallback>( - (values) => { - const responseBody = { - key: model.key, - body: values, - }; - - updateModel(responseBody) - .unwrap() - .then((payload) => { - reset(payload as DiffusersModelConfig, { keepDefaultValues: true }); - dispatch( - addToast( - makeToast({ - title: t('modelManager.modelUpdated'), - status: 'success', - }) - ) - ); - }) - .catch((_) => { - reset(); - dispatch( - addToast( - makeToast({ - title: t('modelManager.modelUpdateFailed'), - status: 'error', - }) - ) - ); - }); - }, - [dispatch, model.key, reset, t, updateModel] - ); - - return ( - - - - {model.name} - - - {MODEL_TYPE_MAP[model.base]} {t('modelManager.model')} - - - - -
- - - {t('modelManager.name')} - value.trim().length > 3 || 'Must be at least 3 characters', - })} - /> - {errors.name?.message && {errors.name?.message}} - - - {t('modelManager.description')} - - - control={control} name="base" /> - control={control} name="variant" /> - - {t('modelManager.modelLocation')} - value.trim().length > 0 || 'Must provide a path', - })} - /> - {errors.path?.message && {errors.path?.message}} - - - {t('modelManager.vaeLocation')} - - - - -
-
- ); -}; - -export default memo(DiffusersModelEdit); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx deleted file mode 100644 index 81f2c4df29..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { Button, Divider, Flex, FormControl, FormErrorMessage, FormLabel, Input, Text } from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import BaseModelSelect from 'features/modelManager/subpanels/shared/BaseModelSelect'; -import { LORA_MODEL_FORMAT_MAP, MODEL_TYPE_MAP } from 'features/parameters/types/constants'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import { memo, useCallback } from 'react'; -import type { SubmitHandler } from 'react-hook-form'; -import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useUpdateModelsMutation } from 'services/api/endpoints/models'; -import type { LoRAModelConfig } from 'services/api/types'; - -type LoRAModelEditProps = { - model: LoRAModelConfig; -}; - -const LoRAModelEdit = (props: LoRAModelEditProps) => { - const { model } = props; - - const [updateModel, { isLoading }] = useUpdateModelsMutation(); - - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const { - register, - handleSubmit, - control, - formState: { errors }, - reset, - } = useForm({ - defaultValues: { - name: model.name ? model.name : '', - base: model.base, - type: 'lora', - path: model.path ? model.path : '', - description: model.description ? model.description : '', - format: model.format, - }, - mode: 'onChange', - }); - - const onSubmit = useCallback>( - (values) => { - const responseBody = { - key: model.key, - body: values, - }; - - updateModel(responseBody) - .unwrap() - .then((payload) => { - reset(payload as LoRAModelConfig, { keepDefaultValues: true }); - dispatch( - addToast( - makeToast({ - title: t('modelManager.modelUpdated'), - status: 'success', - }) - ) - ); - }) - .catch((_) => { - reset(); - dispatch( - addToast( - makeToast({ - title: t('modelManager.modelUpdateFailed'), - status: 'error', - }) - ) - ); - }); - }, - [dispatch, model.key, reset, t, updateModel] - ); - - return ( - - - - {model.name} - - - {MODEL_TYPE_MAP[model.base]} {t('modelManager.model')} ⋅ {LORA_MODEL_FORMAT_MAP[model.format]}{' '} - {t('common.format')} - - - - -
- - - {t('modelManager.name')} - value.trim().length > 3 || 'Must be at least 3 characters', - })} - /> - {errors.name?.message && {errors.name?.message}} - - - {t('modelManager.description')} - - - control={control} name="base" /> - - - {t('modelManager.modelLocation')} - value.trim().length > 0 || 'Must provide a path', - })} - /> - {errors.path?.message && {errors.path?.message}} - - - -
-
- ); -}; - -export default memo(LoRAModelEdit); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelConvert.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelConvert.tsx deleted file mode 100644 index 9a2746abe6..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelConvert.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import { - Button, - ConfirmationAlertDialog, - Flex, - FormControl, - FormLabel, - Input, - ListItem, - Radio, - RadioGroup, - Text, - Tooltip, - UnorderedList, - useDisclosure, -} from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import type { ChangeEvent } from 'react'; -import { memo, useCallback, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useConvertMainModelsMutation } from 'services/api/endpoints/models'; -import type { CheckpointModelConfig } from 'services/api/types'; - -interface ModelConvertProps { - model: CheckpointModelConfig; -} - -type SaveLocation = 'InvokeAIRoot' | 'Custom'; - -const ModelConvert = (props: ModelConvertProps) => { - const { model } = props; - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - const [convertModel, { isLoading }] = useConvertMainModelsMutation(); - const { isOpen, onOpen, onClose } = useDisclosure(); - const [saveLocation, setSaveLocation] = useState('InvokeAIRoot'); - const [customSaveLocation, setCustomSaveLocation] = useState(''); - - useEffect(() => { - setSaveLocation('InvokeAIRoot'); - }, [model]); - - const modelConvertCancelHandler = useCallback(() => { - setSaveLocation('InvokeAIRoot'); - }, []); - - const handleChangeSaveLocation = useCallback((v: string) => { - setSaveLocation(v as SaveLocation); - }, []); - const handleChangeCustomSaveLocation = useCallback((e: ChangeEvent) => { - setCustomSaveLocation(e.target.value); - }, []); - - const modelConvertHandler = useCallback(() => { - const queryArg = { - base_model: model.base, - model_name: model.name, - 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.name}`, - status: 'info', - }) - ) - ); - - convertModel(queryArg) - .unwrap() - .then(() => { - dispatch( - addToast( - makeToast({ - title: `${t('modelManager.modelConverted')}: ${model.name}`, - status: 'success', - }) - ) - ); - }) - .catch(() => { - dispatch( - addToast( - makeToast({ - title: `${t('modelManager.modelConversionFailed')}: ${model.name}`, - status: 'error', - }) - ) - ); - }); - }, [convertModel, customSaveLocation, dispatch, model.base, model.name, saveLocation, t]); - - return ( - <> - - - - {t('modelManager.convertToDiffusersHelpText1')} - - {t('modelManager.convertToDiffusersHelpText2')} - {t('modelManager.convertToDiffusersHelpText3')} - {t('modelManager.convertToDiffusersHelpText4')} - {t('modelManager.convertToDiffusersHelpText5')} - - {t('modelManager.convertToDiffusersHelpText6')} - - - - - {t('modelManager.convertToDiffusersSaveLocation')} - - - - - {t('modelManager.invokeRoot')} - - - - {t('modelManager.custom')} - - - - - {saveLocation === 'Custom' && ( - - {t('modelManager.customSaveLocation')} - - - )} - - - - ); -}; - -export default memo(ModelConvert); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelList.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelList.tsx deleted file mode 100644 index b129b7310d..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelList.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import { Button, ButtonGroup, Flex, FormControl, FormLabel, Input, Spinner, Text } from '@invoke-ai/ui-library'; -import type { EntityState } from '@reduxjs/toolkit'; -import { forEach } from 'lodash-es'; -import type { ChangeEvent, PropsWithChildren } from 'react'; -import { memo, useCallback, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { ALL_BASE_MODELS } from 'services/api/constants'; -// import type { LoRAConfig, MainModelConfig } from 'services/api/endpoints/models'; -import { useGetLoRAModelsQuery, useGetMainModelsQuery } from 'services/api/endpoints/models'; -import type { LoRAConfig, MainModelConfig } from 'services/api/types'; - -import ModelListItem from './ModelListItem'; - -type ModelListProps = { - selectedModelId: string | undefined; - setSelectedModelId: (name: string | undefined) => void; -}; - -type ModelFormat = 'all' | 'checkpoint' | 'diffusers'; - -type ModelType = 'main' | 'lora'; - -type CombinedModelFormat = ModelFormat | 'lora'; - -const ModelList = (props: ModelListProps) => { - const { selectedModelId, setSelectedModelId } = props; - const { t } = useTranslation(); - const [nameFilter, setNameFilter] = useState(''); - const [modelFormatFilter, setModelFormatFilter] = useState('all'); - - const { filteredDiffusersModels, isLoadingDiffusersModels } = useGetMainModelsQuery(ALL_BASE_MODELS, { - selectFromResult: ({ data, isLoading }) => ({ - filteredDiffusersModels: modelsFilter(data, 'main', 'diffusers', nameFilter), - isLoadingDiffusersModels: isLoading, - }), - }); - - const { filteredCheckpointModels, isLoadingCheckpointModels } = useGetMainModelsQuery(ALL_BASE_MODELS, { - selectFromResult: ({ data, isLoading }) => ({ - filteredCheckpointModels: modelsFilter(data, 'main', 'checkpoint', nameFilter), - isLoadingCheckpointModels: isLoading, - }), - }); - - const { filteredLoraModels, isLoadingLoraModels } = useGetLoRAModelsQuery(undefined, { - selectFromResult: ({ data, isLoading }) => ({ - filteredLoraModels: modelsFilter(data, 'lora', undefined, nameFilter), - isLoadingLoraModels: isLoading, - }), - }); - - const handleSearchFilter = useCallback((e: ChangeEvent) => { - setNameFilter(e.target.value); - }, []); - - return ( - - - - - - - - - - - {t('modelManager.search')} - - - - - {/* Diffusers List */} - {isLoadingDiffusersModels && } - {['all', 'diffusers'].includes(modelFormatFilter) && - !isLoadingDiffusersModels && - filteredDiffusersModels.length > 0 && ( - - )} - {/* Checkpoints List */} - {isLoadingCheckpointModels && } - {['all', 'checkpoint'].includes(modelFormatFilter) && - !isLoadingCheckpointModels && - filteredCheckpointModels.length > 0 && ( - - )} - - {/* LoRAs List */} - {isLoadingLoraModels && } - {['all', 'lora'].includes(modelFormatFilter) && !isLoadingLoraModels && filteredLoraModels.length > 0 && ( - - )} - - - - ); -}; - -export default memo(ModelList); - -const modelsFilter = ( - data: EntityState | undefined, - model_type: ModelType, - model_format: ModelFormat | undefined, - nameFilter: string -) => { - const filteredModels: T[] = []; - forEach(data?.entities, (model) => { - if (!model) { - return; - } - - const matchesFilter = model.name.toLowerCase().includes(nameFilter.toLowerCase()); - - const matchesFormat = model_format === undefined || model.format === model_format; - const matchesType = model.type === model_type; - - if (matchesFilter && matchesFormat && matchesType) { - filteredModels.push(model); - } - }); - return filteredModels; -}; - -const StyledModelContainer = memo((props: PropsWithChildren) => { - return ( - - {props.children} - - ); -}); - -StyledModelContainer.displayName = 'StyledModelContainer'; - -type ModelListWrapperProps = { - title: string; - modelList: MainModelConfig[] | LoRAConfig[]; - selected: ModelListProps; -}; - -const ModelListWrapper = memo((props: ModelListWrapperProps) => { - const { title, modelList, selected } = props; - return ( - - - - {title} - - {modelList.map((model) => ( - - ))} - - - ); -}); - -ModelListWrapper.displayName = 'ModelListWrapper'; - -const FetchingModelsLoader = memo(({ loadingMessage }: { loadingMessage?: string }) => { - return ( - - - - {loadingMessage ? loadingMessage : 'Fetching...'} - - - ); -}); - -FetchingModelsLoader.displayName = 'FetchingModelsLoader'; diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelListItem.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelListItem.tsx deleted file mode 100644 index 08b9b61aea..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelListItem.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { - Badge, - Button, - ConfirmationAlertDialog, - Flex, - IconButton, - Text, - Tooltip, - useDisclosure, -} from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { MODEL_TYPE_SHORT_MAP } from 'features/parameters/types/constants'; -import { addToast } from 'features/system/store/systemSlice'; -import { makeToast } from 'features/system/util/makeToast'; -import { memo, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { PiTrashSimpleBold } from 'react-icons/pi'; -import { useDeleteModelsMutation } from 'services/api/endpoints/models'; -import type { LoRAConfig, MainModelConfig } from 'services/api/types'; - -type ModelListItemProps = { - model: MainModelConfig | LoRAConfig; - isSelected: boolean; - setSelectedModelId: (v: string | undefined) => void; -}; - -const ModelListItem = (props: ModelListItemProps) => { - const { t } = useTranslation(); - const dispatch = useAppDispatch(); - const [deleteModel] = useDeleteModelsMutation(); - const { isOpen, onOpen, onClose } = useDisclosure(); - - const { model, isSelected, setSelectedModelId } = props; - - const handleSelectModel = useCallback(() => { - setSelectedModelId(model.key); - }, [model.key, setSelectedModelId]); - - const handleModelDelete = useCallback(() => { - deleteModel({ key: model.key }) - .unwrap() - .then((_) => { - dispatch( - addToast( - makeToast({ - title: `${t('modelManager.modelDeleted')}: ${model.name}`, - status: 'success', - }) - ) - ); - }) - .catch((error) => { - if (error) { - dispatch( - addToast( - makeToast({ - title: `${t('modelManager.modelDeleteFailed')}: ${model.name}`, - status: 'error', - }) - ) - ); - } - }); - setSelectedModelId(undefined); - }, [deleteModel, model, setSelectedModelId, dispatch, t]); - - return ( - - - - - {MODEL_TYPE_SHORT_MAP[model.base as keyof typeof MODEL_TYPE_SHORT_MAP]} - - - {model.name} - - - - } - aria-label={t('modelManager.deleteConfig')} - colorScheme="error" - /> - - - {t('modelManager.deleteMsg1')} - {t('modelManager.deleteMsg2')} - - - - ); -}; - -export default memo(ModelListItem); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel.tsx deleted file mode 100644 index 50118d9b97..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Flex } from '@invoke-ai/ui-library'; -import { memo } from 'react'; - -import SyncModels from './ModelManagerSettingsPanel/SyncModels'; - -const ModelManagerSettingsPanel = () => { - return ( - - - - ); -}; - -export default memo(ModelManagerSettingsPanel); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModels.tsx deleted file mode 100644 index a4af3e1517..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModels.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Flex, Text } from '@invoke-ai/ui-library'; -import { SyncModelsButton } from 'features/modelManager/components/SyncModels/SyncModelsButton'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; - -const SyncModels = () => { - const { t } = useTranslation(); - - return ( - - - {t('modelManager.syncModels')} - - {t('modelManager.syncModelsDesc')} - - - - - ); -}; - -export default memo(SyncModels); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/shared/BaseModelSelect.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/shared/BaseModelSelect.tsx deleted file mode 100644 index fce16f3f13..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/shared/BaseModelSelect.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library'; -import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library'; -import { typedMemo } from 'common/util/typedMemo'; -import { MODEL_TYPE_MAP } from 'features/parameters/types/constants'; -import { useCallback, useMemo } from 'react'; -import type { UseControllerProps } from 'react-hook-form'; -import { useController } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import type { AnyModelConfig } from 'services/api/types'; - -const options: ComboboxOption[] = [ - { value: 'sd-1', label: MODEL_TYPE_MAP['sd-1'] }, - { value: 'sd-2', label: MODEL_TYPE_MAP['sd-2'] }, - { value: 'sdxl', label: MODEL_TYPE_MAP['sdxl'] }, - { value: 'sdxl-refiner', label: MODEL_TYPE_MAP['sdxl-refiner'] }, -]; - -const BaseModelSelect = (props: UseControllerProps) => { - const { t } = useTranslation(); - const { field } = useController(props); - const value = useMemo(() => options.find((o) => o.value === field.value), [field.value]); - const onChange = useCallback( - (v) => { - field.onChange(v?.value); - }, - [field] - ); - return ( - - {t('modelManager.baseModel')} - - - ); -}; - -export default typedMemo(BaseModelSelect); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/shared/CheckpointConfigsSelect.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/shared/CheckpointConfigsSelect.tsx deleted file mode 100644 index 569f1abbba..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/shared/CheckpointConfigsSelect.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { ChakraProps, ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library'; -import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library'; -import { memo, useCallback, useMemo } from 'react'; -import { useController, type UseControllerProps } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useGetCheckpointConfigsQuery } from 'services/api/endpoints/models'; -import type { CheckpointModelConfig } from 'services/api/types'; - -const sx: ChakraProps['sx'] = { w: 'full' }; - -const CheckpointConfigsSelect = (props: UseControllerProps) => { - const { data } = useGetCheckpointConfigsQuery(); - const { t } = useTranslation(); - const options = useMemo(() => (data ? data.map((i) => ({ label: i, value: i })) : []), [data]); - const { field } = useController(props); - const value = useMemo(() => options.find((o) => o.value === field.value), [field.value, options]); - const onChange = useCallback( - (v) => { - field.onChange(v?.value); - }, - [field] - ); - - return ( - - {t('modelManager.configFile')} - - - ); -}; - -export default memo(CheckpointConfigsSelect); diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/shared/ModelVariantSelect.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/shared/ModelVariantSelect.tsx deleted file mode 100644 index 27ded93843..0000000000 --- a/invokeai/frontend/web/src/features/modelManager/subpanels/shared/ModelVariantSelect.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library'; -import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library'; -import { typedMemo } from 'common/util/typedMemo'; -import { useCallback, useMemo } from 'react'; -import type { UseControllerProps } from 'react-hook-form'; -import { useController } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import type { CheckpointModelConfig, DiffusersModelConfig } from 'services/api/types'; - -const options: ComboboxOption[] = [ - { value: 'normal', label: 'Normal' }, - { value: 'inpaint', label: 'Inpaint' }, - { value: 'depth', label: 'Depth' }, -]; - -const ModelVariantSelect = (props: UseControllerProps) => { - const { t } = useTranslation(); - const { field } = useController(props); - const value = useMemo(() => options.find((o) => o.value === field.value), [field.value]); - const onChange = useCallback( - (v) => { - field.onChange(v?.value); - }, - [field] - ); - return ( - - {t('modelManager.variant')} - - - ); -}; - -export default typedMemo(ModelVariantSelect); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/MainModelFieldInputComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/MainModelFieldInputComponent.tsx index 1cb0658b81..c2b3d3d69a 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/MainModelFieldInputComponent.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/MainModelFieldInputComponent.tsx @@ -1,7 +1,7 @@ import { Combobox, Flex, FormControl } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox'; -import { SyncModelsIconButton } from 'features/modelManager/components/SyncModels/SyncModelsIconButton'; +import { SyncModelsIconButton } from 'features/modelManagerV2/components/SyncModels/SyncModelsIconButton'; import { fieldMainModelValueChanged } from 'features/nodes/store/nodesSlice'; import type { MainModelFieldInputInstance, MainModelFieldInputTemplate } from 'features/nodes/types/field'; import { memo, useCallback } from 'react'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/RefinerModelFieldInputComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/RefinerModelFieldInputComponent.tsx index be2b4a4d4f..0499eb262e 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/RefinerModelFieldInputComponent.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/RefinerModelFieldInputComponent.tsx @@ -1,7 +1,7 @@ import { Combobox, Flex, FormControl } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox'; -import { SyncModelsIconButton } from 'features/modelManager/components/SyncModels/SyncModelsIconButton'; +import { SyncModelsIconButton } from 'features/modelManagerV2/components/SyncModels/SyncModelsIconButton'; import { fieldRefinerModelValueChanged } from 'features/nodes/store/nodesSlice'; import type { SDXLRefinerModelFieldInputInstance, diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SDXLMainModelFieldInputComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SDXLMainModelFieldInputComponent.tsx index d0d7754606..0a48b0c917 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SDXLMainModelFieldInputComponent.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SDXLMainModelFieldInputComponent.tsx @@ -1,7 +1,7 @@ import { Combobox, Flex, FormControl } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox'; -import { SyncModelsIconButton } from 'features/modelManager/components/SyncModels/SyncModelsIconButton'; +import { SyncModelsIconButton } from 'features/modelManagerV2/components/SyncModels/SyncModelsIconButton'; import { fieldMainModelValueChanged } from 'features/nodes/store/nodesSlice'; import type { SDXLMainModelFieldInputInstance, SDXLMainModelFieldInputTemplate } from 'features/nodes/types/field'; import { memo, useCallback } from 'react'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VAEModelFieldInputComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VAEModelFieldInputComponent.tsx index 272f7f5b35..23ead6c543 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VAEModelFieldInputComponent.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VAEModelFieldInputComponent.tsx @@ -1,7 +1,7 @@ import { Combobox, Flex, FormControl } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox'; -import { SyncModelsIconButton } from 'features/modelManager/components/SyncModels/SyncModelsIconButton'; +import { SyncModelsIconButton } from 'features/modelManagerV2/components/SyncModels/SyncModelsIconButton'; import { fieldVaeModelValueChanged } from 'features/nodes/store/nodesSlice'; import type { VAEModelFieldInputInstance, VAEModelFieldInputTemplate } from 'features/nodes/types/field'; import { pick } from 'lodash-es'; diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx index d57e48f11e..0633193883 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx @@ -16,7 +16,7 @@ import { EMPTY_ARRAY } from 'app/store/util'; import { LoRAList } from 'features/lora/components/LoRAList'; import LoRASelect from 'features/lora/components/LoRASelect'; import { selectLoraSlice } from 'features/lora/store/loraSlice'; -import { SyncModelsIconButton } from 'features/modelManager/components/SyncModels/SyncModelsIconButton'; +import { SyncModelsIconButton } from 'features/modelManagerV2/components/SyncModels/SyncModelsIconButton'; import ParamCFGScale from 'features/parameters/components/Core/ParamCFGScale'; import ParamScheduler from 'features/parameters/components/Core/ParamScheduler'; import ParamSteps from 'features/parameters/components/Core/ParamSteps'; diff --git a/invokeai/frontend/web/src/services/api/endpoints/models.ts b/invokeai/frontend/web/src/services/api/endpoints/models.ts index 0f68353148..51b78a2277 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/models.ts +++ b/invokeai/frontend/web/src/services/api/endpoints/models.ts @@ -10,7 +10,6 @@ import type { IPAdapterModelConfig, LoRAModelConfig, MainModelConfig, - MergeModelConfig, T2IAdapterModelConfig, TextualInversionModelConfig, VAEModelConfig, @@ -38,22 +37,9 @@ type DeleteMainModelArg = { type DeleteMainModelResponse = void; -type ConvertMainModelArg = { - base_model: BaseModelType; - model_name: string; - convert_dest_directory?: string; -}; - type ConvertMainModelResponse = paths['/api/v2/models/convert/{key}']['put']['responses']['200']['content']['application/json']; -type MergeMainModelArg = { - base_model: BaseModelType; - body: MergeModelConfig; -}; - -type MergeMainModelResponse = paths['/api/v2/models/merge']['put']['responses']['200']['content']['application/json']; - type ImportMainModelArg = { source: NonNullable; access_token?: operations['heuristic_install_model']['parameters']['query']['access_token']; @@ -72,20 +58,11 @@ type DeleteImportModelsResponse = type PruneModelImportsResponse = paths['/api/v2/models/import']['patch']['responses']['200']['content']['application/json']; -type ImportAdvancedModelArg = { - source: NonNullable; - config: NonNullable; -}; - -type ImportAdvancedModelResponse = paths['/api/v2/models/import']['post']['responses']['201']['content']['application/json']; export type ScanFolderResponse = paths['/api/v2/models/scan_folder']['get']['responses']['200']['content']['application/json']; type ScanFolderArg = operations['scan_for_models']['parameters']['query']; -type CheckpointConfigsResponse = - paths['/api/v2/models/ckpt_confs']['get']['responses']['200']['content']['application/json']; - export const mainModelsAdapter = createEntityAdapter({ selectId: (entity) => entity.key, sortComparer: (a, b) => a.name.localeCompare(b.name), @@ -199,16 +176,6 @@ export const modelsApi = api.injectEndpoints({ }, invalidatesTags: ['Model', 'ModelImports'], }), - importAdvancedModel: build.mutation({ - query: ({ source, config}) => { - return { - url: buildModelsUrl('install'), - method: 'POST', - body: { source, config }, - }; - }, - invalidatesTags: ['Model', 'ModelImports'], - }), deleteModels: build.mutation({ query: ({ key }) => { return { @@ -218,25 +185,14 @@ export const modelsApi = api.injectEndpoints({ }, invalidatesTags: ['Model'], }), - convertMainModels: build.mutation({ - query: ({ base_model, model_name, convert_dest_directory }) => { + convertMainModels: build.mutation({ + query: (key) => { return { - url: buildModelsUrl(`convert/${base_model}/main/${model_name}`), + url: buildModelsUrl(`convert/${key}`), method: 'PUT', - params: { convert_dest_directory }, }; }, - invalidatesTags: ['Model'], - }), - mergeMainModels: build.mutation({ - query: ({ base_model, body }) => { - return { - url: buildModelsUrl(`merge/${base_model}`), - method: 'PUT', - body: body, - }; - }, - invalidatesTags: ['Model'], + invalidatesTags: ['ModelConfig'], }), getModelConfig: build.query({ query: (key) => buildModelsUrl(`i/${key}`), @@ -323,13 +279,6 @@ export const modelsApi = api.injectEndpoints({ }, invalidatesTags: ['ModelImports'], }), - getCheckpointConfigs: build.query({ - query: () => { - return { - url: buildModelsUrl(`ckpt_confs`), - }; - }, - }), }), }); @@ -345,13 +294,10 @@ export const { useDeleteModelsMutation, useUpdateModelsMutation, useImportMainModelsMutation, - useImportAdvancedModelMutation, useConvertMainModelsMutation, - useMergeMainModelsMutation, useSyncModelsMutation, useScanModelsQuery, useLazyScanModelsQuery, - useGetCheckpointConfigsQuery, useGetModelImportsQuery, useGetModelMetadataQuery, useDeleteModelImportMutation,