Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 5 additions & 51 deletions invokeai/app/api/routers/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,20 +315,21 @@ async def list_ckpt_configs(
return ApiDependencies.invoker.services.model_manager.list_checkpoint_configs()


@models_router.get(
@models_router.post(
"/sync",
operation_id="sync_to_config",
responses={
201: { "description": "synchronization successful" },
},
status_code = 201,
response_model = None
response_model = bool
)
async def sync_to_config(
)->None:
)->bool:
"""Call after making changes to models.yaml, autoimport directories or models directory to synchronize
in-memory data structures with disk data structures."""
return ApiDependencies.invoker.services.model_manager.sync_to_config()
ApiDependencies.invoker.services.model_manager.sync_to_config()
return True

@models_router.put(
"/merge/{base_model}",
Expand Down Expand Up @@ -373,50 +374,3 @@ async def merge_models(
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
return response

# The rename operation is now supported by update_model and no longer needs to be
# a standalone route.
# @models_router.post(
# "/rename/{base_model}/{model_type}/{model_name}",
# operation_id="rename_model",
# responses= {
# 201: {"description" : "The model was renamed successfully"},
# 404: {"description" : "The model could not be found"},
# 409: {"description" : "There is already a model corresponding to the new name"},
# },
# status_code=201,
# response_model=ImportModelResponse
# )
# async def rename_model(
# base_model: BaseModelType = Path(description="Base model"),
# model_type: ModelType = Path(description="The type of model"),
# model_name: str = Path(description="current model name"),
# new_name: Optional[str] = Query(description="new model name", default=None),
# new_base: Optional[BaseModelType] = Query(description="new model base", default=None),
# ) -> ImportModelResponse:
# """ Rename a model"""

# logger = ApiDependencies.invoker.services.logger

# try:
# result = ApiDependencies.invoker.services.model_manager.rename_model(
# base_model = base_model,
# model_type = model_type,
# model_name = model_name,
# new_name = new_name,
# new_base = new_base,
# )
# logger.debug(result)
# logger.info(f'Successfully renamed {model_name}=>{new_name}')
# model_raw = ApiDependencies.invoker.services.model_manager.list_model(
# model_name=new_name or model_name,
# base_model=new_base or base_model,
# model_type=model_type
# )
# return parse_obj_as(ImportModelResponse, model_raw)
# except ModelNotFoundException as e:
# logger.error(str(e))
# raise HTTPException(status_code=404, detail=str(e))
# except ValueError as e:
# logger.error(str(e))
# raise HTTPException(status_code=409, detail=str(e))
7 changes: 6 additions & 1 deletion invokeai/frontend/web/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,12 @@
"addDifference": "Add Difference",
"pickModelType": "Pick Model Type",
"selectModel": "Select Model",
"importModels": "Import Models"
"importModels": "Import Models",
"settings": "Settings",
"syncModels": "Sync Models",
"syncModelsDesc": "If your models are out of sync with the backend, you can refresh them up using this option. This is generally handy in cases where you manually update your models.yaml file or add models to the InvokeAI root folder after the application has booted.",
"modelsSynced": "Models Synced",
"modelSyncFailed": "Model Sync Failed"
},
"parameters": {
"general": "General",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import {
ModelInputFieldTemplate,
} from 'features/nodes/types/types';

import { Box, Flex } from '@chakra-ui/react';
import { SelectItem } from '@mantine/core';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
import { modelIdToMainModelParam } from 'features/parameters/util/modelIdToMainModelParam';
import SyncModelsButton from 'features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton';
import { forEach } from 'lodash-es';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -88,18 +90,23 @@ const ModelInputFieldComponent = (
data={[]}
/>
) : (
<IAIMantineSearchableSelect
tooltip={selectedModel?.description}
label={
selectedModel?.base_model && MODEL_TYPE_MAP[selectedModel?.base_model]
}
value={selectedModel?.id}
placeholder={data.length > 0 ? 'Select a model' : 'No models available'}
data={data}
error={data.length === 0}
disabled={data.length === 0}
onChange={handleChangeModel}
/>
<Flex w="100%" alignItems="center" gap={2}>
<IAIMantineSearchableSelect
tooltip={selectedModel?.description}
label={
selectedModel?.base_model && MODEL_TYPE_MAP[selectedModel?.base_model]
}
value={selectedModel?.id}
placeholder={data.length > 0 ? 'Select a model' : 'No models available'}
data={data}
error={data.length === 0}
disabled={data.length === 0}
onChange={handleChangeModel}
/>
<Box mt={7}>
<SyncModelsButton iconMode />
</Box>
</Flex>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { useTranslation } from 'react-i18next';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';

import { Box, Flex } from '@chakra-ui/react';
import { SelectItem } from '@mantine/core';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { modelSelected } from 'features/parameters/store/actions';
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
import { modelIdToMainModelParam } from 'features/parameters/util/modelIdToMainModelParam';
import SyncModelsButton from 'features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton';
import { forEach } from 'lodash-es';
import { useGetMainModelsQuery } from 'services/api/endpoints/models';

Expand Down Expand Up @@ -84,16 +86,22 @@ const ParamMainModelSelect = () => {
data={[]}
/>
) : (
<IAIMantineSearchableSelect
tooltip={selectedModel?.description}
label={t('modelManager.model')}
value={selectedModel?.id}
placeholder={data.length > 0 ? 'Select a model' : 'No models available'}
data={data}
error={data.length === 0}
disabled={data.length === 0}
onChange={handleChangeModel}
/>
<Flex w="100%" alignItems="center" gap={2}>
<IAIMantineSearchableSelect
tooltip={selectedModel?.description}
label={t('modelManager.model')}
value={selectedModel?.id}
placeholder={data.length > 0 ? 'Select a model' : 'No models available'}
data={data}
error={data.length === 0}
disabled={data.length === 0}
onChange={handleChangeModel}
w="100%"
/>
<Box mt={7}>
<SyncModelsButton iconMode />
</Box>
</Flex>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import { ReactNode, memo } from 'react';
import ImportModelsPanel from './subpanels/ImportModelsPanel';
import MergeModelsPanel from './subpanels/MergeModelsPanel';
import ModelManagerPanel from './subpanels/ModelManagerPanel';
import ModelManagerSettingsPanel from './subpanels/ModelManagerSettingsPanel';

type ModelManagerTabName = 'modelManager' | 'importModels' | 'mergeModels';
type ModelManagerTabName =
| 'modelManager'
| 'importModels'
| 'mergeModels'
| 'settings';

type ModelManagerTabInfo = {
id: ModelManagerTabName;
Expand All @@ -29,6 +34,11 @@ const tabs: ModelManagerTabInfo[] = [
label: i18n.t('modelManager.mergeModels'),
content: <MergeModelsPanel />,
},
{
id: 'settings',
label: i18n.t('modelManager.settings'),
content: <ModelManagerSettingsPanel />,
},
];

const ModelManagerTab = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Flex } from '@chakra-ui/react';
import SyncModels from './ModelManagerSettingsPanel/SyncModels';

export default function ModelManagerSettingsPanel() {
return (
<Flex>
<SyncModels />
</Flex>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Flex, Text } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import SyncModelsButton from './SyncModelsButton';

export default function SyncModels() {
const { t } = useTranslation();

return (
<Flex
sx={{
w: 'full',
p: 4,
borderRadius: 4,
gap: 4,
justifyContent: 'space-between',
alignItems: 'center',
bg: 'base.200',
_dark: { bg: 'base.800' },
}}
>
<Flex
sx={{
flexDirection: 'column',
gap: 2,
}}
>
<Text sx={{ fontWeight: 600 }}>{t('modelManager.syncModels')}</Text>
<Text fontSize="sm" sx={{ _dark: { color: 'base.400' } }}>
{t('modelManager.syncModelsDesc')}
</Text>
</Flex>
<SyncModelsButton />
</Flex>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { makeToast } from 'app/components/Toaster';
import { useAppDispatch } from 'app/store/storeHooks';
import IAIButton from 'common/components/IAIButton';
import IAIIconButton from 'common/components/IAIIconButton';
import { addToast } from 'features/system/store/systemSlice';
import { useTranslation } from 'react-i18next';
import { FaSync } from 'react-icons/fa';
import { useSyncModelsMutation } from 'services/api/endpoints/models';

type SyncModelsButtonProps = {
iconMode?: boolean;
};

export default function SyncModelsButton(props: SyncModelsButtonProps) {
const { iconMode = false } = props;
const dispatch = useAppDispatch();
const { t } = useTranslation();

const [syncModels, { isLoading }] = useSyncModelsMutation();

const syncModelsHandler = () => {
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',
})
)
);
}
});
};

return !iconMode ? (
<IAIButton
isLoading={isLoading}
onClick={syncModelsHandler}
minW="max-content"
>
Sync Models
</IAIButton>
) : (
<IAIIconButton
icon={<FaSync />}
tooltip={t('modelManager.syncModels')}
aria-label={t('modelManager.syncModels')}
isLoading={isLoading}
onClick={syncModelsHandler}
size="sm"
/>
);
}
13 changes: 13 additions & 0 deletions invokeai/frontend/web/src/services/api/endpoints/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ type AddMainModelArg = {
type AddMainModelResponse =
paths['/api/v1/models/add']['post']['responses']['201']['content']['application/json'];

type SyncModelsResponse =
paths['/api/v1/models/sync']['post']['responses']['201']['content']['application/json'];

export type SearchFolderResponse =
paths['/api/v1/models/search']['get']['responses']['200']['content']['application/json'];

Expand Down Expand Up @@ -244,6 +247,15 @@ export const modelsApi = api.injectEndpoints({
},
invalidatesTags: [{ type: 'MainModel', id: LIST_TAG }],
}),
syncModels: build.mutation<SyncModelsResponse, void>({
query: () => {
return {
url: `models/sync`,
method: 'POST',
};
},
invalidatesTags: [{ type: 'MainModel', id: LIST_TAG }],
}),
getLoRAModels: build.query<EntityState<LoRAModelConfigEntity>, void>({
query: () => ({ url: 'models/', params: { model_type: 'lora' } }),
providesTags: (result, error, arg) => {
Expand Down Expand Up @@ -423,6 +435,7 @@ export const {
useAddMainModelsMutation,
useConvertMainModelsMutation,
useMergeMainModelsMutation,
useSyncModelsMutation,
useGetModelsInFolderQuery,
useGetCheckpointConfigsQuery,
} = modelsApi;
Loading