From a9b1f4b8c654134239dd878ab2d647b940fffe3d Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Thu, 22 Feb 2024 10:12:27 -0500 Subject: [PATCH] add scan model endpoint, break add model into tabs --- .../{ => AddModelPanel}/ImportQueue.tsx | 8 +- .../{ => AddModelPanel}/ImportQueueModel.tsx | 0 .../subpanels/AddModelPanel/SimpleImport.tsx | 67 ++++++++++++++ .../modelManagerV2/subpanels/ImportModels.tsx | 88 ++++--------------- .../web/src/services/api/endpoints/models.ts | 37 +++----- .../frontend/web/src/services/api/schema.ts | 31 +++++++ 6 files changed, 134 insertions(+), 97 deletions(-) rename invokeai/frontend/web/src/features/modelManagerV2/subpanels/{ => AddModelPanel}/ImportQueue.tsx (91%) rename invokeai/frontend/web/src/features/modelManagerV2/subpanels/{ => AddModelPanel}/ImportQueueModel.tsx (100%) create mode 100644 invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/SimpleImport.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportQueue.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ImportQueue.tsx similarity index 91% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportQueue.tsx rename to invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ImportQueue.tsx index 53c037e74f..3d80d11c29 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportQueue.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ImportQueue.tsx @@ -1,4 +1,4 @@ -import { Box, Button,Flex, Text } from '@invoke-ai/ui-library'; +import { Box, Button, Flex, Text } 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'; @@ -7,7 +7,7 @@ import { useCallback, useMemo } from 'react'; import { RiSparklingFill } from 'react-icons/ri'; import { useGetModelImportsQuery, usePruneModelImportsMutation } from 'services/api/endpoints/models'; -import { ImportQueueModel } from './ImportQueueModel'; +import { ImportQueueModel } from '../ImportQueueModel'; export const ImportQueue = () => { const dispatch = useAppDispatch(); @@ -50,7 +50,7 @@ export const ImportQueue = () => { }, [data]); return ( - <> + {t('modelManager.importQueue')} + + + ); +}; diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportModels.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportModels.tsx index e2cdf24c2f..a0dd39de2e 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportModels.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportModels.tsx @@ -1,80 +1,30 @@ -import { Box, Button, Divider,Flex, FormControl, FormLabel, Heading, 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 { t } from 'i18next'; -import type { CSSProperties } from 'react'; -import { useImportMainModelsMutation } from 'services/api/endpoints/models'; +import { Box, Divider, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library'; -import { ImportQueue } from './ImportQueue'; - -const formStyles: CSSProperties = { - width: '100%', -}; - -type ExtendedImportModelConfig = { - location: string; -}; +import { ImportQueue } from './AddModelPanel/ImportQueue'; +import { SimpleImport } from './AddModelPanel/SimpleImport'; export const ImportModels = () => { - const dispatch = useAppDispatch(); - - const [importMainModel, { isLoading }] = useImportMainModelsMutation(); - - const addModelForm = useForm({ - initialValues: { - location: '', - }, - }); - - const handleAddModelSubmit = (values: ExtendedImportModelConfig) => { - importMainModel({ source: values.location, config: undefined }) - .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 ( Add Model - -
handleAddModelSubmit(v))} style={formStyles}> - - - - {t('modelManager.modelLocation')} - - - - - -
+ + + + Simple + Advanced + Scan + + + + + + Advanced Import Placeholder + Scan Models Placeholder + + + diff --git a/invokeai/frontend/web/src/services/api/endpoints/models.ts b/invokeai/frontend/web/src/services/api/endpoints/models.ts index 3e1abc8560..a1cdb43617 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/models.ts +++ b/invokeai/frontend/web/src/services/api/endpoints/models.ts @@ -25,10 +25,10 @@ type UpdateModelArg = { }; type UpdateModelResponse = paths['/api/v2/models/i/{key}']['patch']['responses']['200']['content']['application/json']; +type GetModelConfigResponse = paths['/api/v2/models/i/{key}']['get']['responses']['200']['content']['application/json']; type GetModelMetadataResponse = - paths['/api/v2/models/meta/i/{key}']['get']['responses']['200']['content']['application/json']; -type GetModelResponse = paths['/api/v2/models/i/{key}']['get']['responses']['200']['content']['application/json']; + paths['/api/v2/models/i/{key}/meta']['get']['responses']['200']['content']['application/json']; type ListModelsArg = NonNullable; @@ -78,16 +78,13 @@ type AddMainModelArg = { type AddMainModelResponse = paths['/api/v2/models/add']['post']['responses']['201']['content']['application/json']; -type SyncModelsResponse = paths['/api/v2/models/sync']['patch']['responses']['204']['content'] - -export type SearchFolderResponse = - paths['/api/v2/models/search']['get']['responses']['200']['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']; -type SearchFolderArg = operations['search_for_models']['parameters']['query']; - export const mainModelsAdapter = createEntityAdapter({ selectId: (entity) => entity.key, sortComparer: (a, b) => a.name.localeCompare(b.name), @@ -131,7 +128,6 @@ const buildProvidesTags = (tagType: (typeof tagTypes)[number]) => (result: EntityState | undefined) => { const tags: ApiTagDescription[] = [{ type: tagType, id: LIST_TAG }, 'Model']; - if (result) { tags.push( ...result.ids.map((id) => ({ @@ -175,15 +171,9 @@ export const modelsApi = api.injectEndpoints({ providesTags: buildProvidesTags('MainModel'), transformResponse: buildTransformResponse(mainModelsAdapter), }), - getModel: build.query({ - query: (key) => { - return buildModelsUrl(`i/${key}`); - }, - providesTags: ['Model'], - }), getModelMetadata: build.query({ query: (key) => { - return buildModelsUrl(`meta/i/${key}`); + return buildModelsUrl(`i/${key}/meta`); }, providesTags: ['Model'], }), @@ -247,7 +237,7 @@ export const modelsApi = api.injectEndpoints({ }, invalidatesTags: ['Model'], }), - getModelConfig: build.query({ + getModelConfig: build.query({ query: (key) => buildModelsUrl(`i/${key}`), providesTags: (result) => { const tags: ApiTagDescription[] = ['Model']; @@ -259,7 +249,7 @@ export const modelsApi = api.injectEndpoints({ return tags; }, }), - syncModels: build.mutation({ + syncModels: build.mutation({ query: () => { return { url: buildModelsUrl('sync'), @@ -298,16 +288,16 @@ export const modelsApi = api.injectEndpoints({ providesTags: buildProvidesTags('TextualInversionModel'), transformResponse: buildTransformResponse(textualInversionModelsAdapter), }), - getModelsInFolder: build.query({ + scanModels: build.query({ query: (arg) => { - const folderQueryStr = queryString.stringify(arg, {}); + const folderQueryStr = arg ? queryString.stringify(arg, {}) : ''; return { - url: buildModelsUrl(`search?${folderQueryStr}`), + url: buildModelsUrl(`scan_folder?${folderQueryStr}`), }; }, }), getModelImports: build.query({ - query: (arg) => { + query: () => { return { url: buildModelsUrl(`import`), }; @@ -358,11 +348,10 @@ export const { useConvertMainModelsMutation, useMergeMainModelsMutation, useSyncModelsMutation, - useGetModelsInFolderQuery, + useScanModelsQuery, useGetCheckpointConfigsQuery, useGetModelImportsQuery, useGetModelMetadataQuery, useDeleteModelImportMutation, usePruneModelImportsMutation, - useGetModelQuery, } = modelsApi; diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts index 81ea2577be..d7205c5c88 100644 --- a/invokeai/frontend/web/src/services/api/schema.ts +++ b/invokeai/frontend/web/src/services/api/schema.ts @@ -60,6 +60,10 @@ export type paths = { */ get: operations["list_tags"]; }; + "/api/v2/models/scan_folder": { + /** Scan For Models */ + get: operations["scan_for_models"]; + }; "/api/v2/models/tags/search": { /** * Search By Metadata Tags @@ -11361,6 +11365,33 @@ export type operations = { }; }; }; + /** Scan For Models */ + scan_for_models: { + parameters: { + query?: { + /** @description Directory path to search for models */ + scan_path?: string; + }; + }; + responses: { + /** @description Directory scanned successfully */ + 200: { + content: { + "application/json": string[]; + }; + }; + /** @description Invalid directory path */ + 400: { + content: never; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; /** * Search By Metadata Tags * @description Get a list of models.