From c4a4e8b85e16dcf1cfebb054190e1ce060d2f883 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 18 Sep 2025 19:53:27 +1000 Subject: [PATCH] feat(ui): toast warning when installed model is unidentified --- invokeai/frontend/web/public/locales/en.json | 3 ++ .../src/features/system/store/constants.ts | 1 + .../services/events/onModelInstallError.tsx | 8 ++++ .../src/services/events/setEventListeners.tsx | 41 +++++++++++++++++-- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 17154cce87..785bc168d5 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -914,6 +914,9 @@ "hfTokenReset": "HF Token Reset", "urlUnauthorizedErrorMessage": "You may need to configure an API token to access this model.", "urlUnauthorizedErrorMessage2": "Learn how here.", + "unidentifiedModelTitle": "Unable to identify model", + "unidentifiedModelMessage": "We were unable to identify the type, base and/or format of the installed model. Try editing the model and selecting the appropriate settings for the model.", + "unidentifiedModelMessage2": "If you don't see the correct settings, or the model doesn't work after changing them, ask for help on or create an issue on .", "imageEncoderModelId": "Image Encoder Model ID", "installedModelsCount": "{{installed}} of {{total}} models installed.", "includesNModels": "Includes {{n}} models and their dependencies.", diff --git a/invokeai/frontend/web/src/features/system/store/constants.ts b/invokeai/frontend/web/src/features/system/store/constants.ts index 68b363c509..0ca2d24129 100644 --- a/invokeai/frontend/web/src/features/system/store/constants.ts +++ b/invokeai/frontend/web/src/features/system/store/constants.ts @@ -1,3 +1,4 @@ export const githubLink = 'http://github.com/invoke-ai/InvokeAI'; +export const githubIssuesLink = 'https://github.com/invoke-ai/InvokeAI/issues'; export const discordLink = 'https://discord.gg/ZmtBAhwWhy'; export const websiteLink = 'https://www.invoke.com/'; diff --git a/invokeai/frontend/web/src/services/events/onModelInstallError.tsx b/invokeai/frontend/web/src/services/events/onModelInstallError.tsx index 0cb77e294c..4b57381f19 100644 --- a/invokeai/frontend/web/src/services/events/onModelInstallError.tsx +++ b/invokeai/frontend/web/src/services/events/onModelInstallError.tsx @@ -4,6 +4,7 @@ import { logger } from 'app/logging/logger'; import type { AppDispatch, AppGetState } from 'app/store/store'; import { getPrefixedId } from 'features/controlLayers/konva/util'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; +import { discordLink, githubIssuesLink } from 'features/system/store/constants'; import { toast, toastApi } from 'features/toast/toast'; import { navigationApi } from 'features/ui/layouts/navigation-api'; import { t } from 'i18next'; @@ -191,3 +192,10 @@ const HFUnauthorizedToastDescription = () => { ); }; + +export const DiscordLink = () => { + return ; +}; +export const GitHubIssuesLink = () => { + return ; +}; diff --git a/invokeai/frontend/web/src/services/events/setEventListeners.tsx b/invokeai/frontend/web/src/services/events/setEventListeners.tsx index 1db0e9b6f5..0cfa4cef26 100644 --- a/invokeai/frontend/web/src/services/events/setEventListeners.tsx +++ b/invokeai/frontend/web/src/services/events/setEventListeners.tsx @@ -1,4 +1,4 @@ -import { ExternalLink } from '@invoke-ai/ui-library'; +import { ExternalLink, Flex, Text } from '@invoke-ai/ui-library'; import { isAnyOf } from '@reduxjs/toolkit'; import { logger } from 'app/logging/logger'; import { socketConnected } from 'app/store/middleware/listenerMiddleware/listeners/socketConnected'; @@ -20,13 +20,14 @@ import ErrorToastDescription, { getTitle } from 'features/toast/ErrorToastDescri import { toast } from 'features/toast/toast'; import { t } from 'i18next'; import { LRUCache } from 'lru-cache'; +import { Trans } from 'react-i18next'; import type { ApiTagDescription } from 'services/api'; import { api, LIST_ALL_TAG, LIST_TAG } from 'services/api'; import { modelsApi } from 'services/api/endpoints/models'; import { queueApi } from 'services/api/endpoints/queue'; import { workflowsApi } from 'services/api/endpoints/workflows'; import { buildOnInvocationComplete } from 'services/events/onInvocationComplete'; -import { buildOnModelInstallError } from 'services/events/onModelInstallError'; +import { buildOnModelInstallError, DiscordLink, GitHubIssuesLink } from 'services/events/onModelInstallError'; import type { ClientToServerEvents, ServerToClientEvents } from 'services/events/types'; import type { Socket } from 'socket.io-client'; import type { JsonObject } from 'type-fest'; @@ -292,7 +293,41 @@ export const setEventListeners = ({ socket, store, setIsConnected }: SetEventLis socket.on('model_install_complete', (data) => { log.debug({ data }, 'Model install complete'); - const { id } = data; + const { id, config } = data; + + if ( + config.type === 'unknown' || + config.base === 'unknown' || + /** + * Checking if type/base are 'unknown' technically narrows the config such that it's not possible for a config + * that passes to the `config.[type|base] === 'unknown'` checks. In the future, if we have more model config + * classes, this may change, so we will continue to check all three. Any one being 'unknown' is concerning + * enough to warrant a toast. + */ + /* @ts-expect-error See note above */ + config.format === 'unknown' + ) { + toast({ + id: 'UNKNOWN_MODEL', + title: t('modelManager.unidentifiedModelTitle'), + description: ( + + + + + + , GitHubIssuesLink: }} + /> + + + ), + status: 'error', + isClosable: true, + duration: null, + }); + } const installs = selectModelInstalls(getState()).data;