From ce687b28efd829c2e4ce66da657ae20dd5509645 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Mon, 31 Jul 2023 12:51:30 +1200 Subject: [PATCH 1/7] fix: Model Manager Tab Issues --- .../subpanels/ModelManagerPanel/ModelList.tsx | 131 +++++++++++++----- 1 file changed, 98 insertions(+), 33 deletions(-) diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx index 3f100d9072..f29b9c1086 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx @@ -1,4 +1,4 @@ -import { ButtonGroup, Flex, Text } from '@chakra-ui/react'; +import { ButtonGroup, Flex, Spinner, Text } from '@chakra-ui/react'; import { EntityState } from '@reduxjs/toolkit'; import IAIButton from 'common/components/IAIButton'; import IAIInput from 'common/components/IAIInput'; @@ -6,23 +6,23 @@ import { forEach } from 'lodash-es'; import type { ChangeEvent, PropsWithChildren } from 'react'; import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { ALL_BASE_MODELS } from 'services/api/constants'; import { + LoRAModelConfigEntity, MainModelConfigEntity, OnnxModelConfigEntity, + useGetLoRAModelsQuery, useGetMainModelsQuery, useGetOnnxModelsQuery, - useGetLoRAModelsQuery, - LoRAModelConfigEntity, } from 'services/api/endpoints/models'; import ModelListItem from './ModelListItem'; -import { ALL_BASE_MODELS } from 'services/api/constants'; type ModelListProps = { selectedModelId: string | undefined; setSelectedModelId: (name: string | undefined) => void; }; -type ModelFormat = 'images' | 'checkpoint' | 'diffusers' | 'olive' | 'onnx'; +type ModelFormat = 'all' | 'checkpoint' | 'diffusers' | 'olive' | 'onnx'; type ModelType = 'main' | 'lora' | 'onnx'; @@ -33,35 +33,43 @@ const ModelList = (props: ModelListProps) => { const { t } = useTranslation(); const [nameFilter, setNameFilter] = useState(''); const [modelFormatFilter, setModelFormatFilter] = - useState('images'); + useState('all'); - const { filteredDiffusersModels } = useGetMainModelsQuery(ALL_BASE_MODELS, { - selectFromResult: ({ data }) => ({ - filteredDiffusersModels: modelsFilter( - data, - 'main', - 'diffusers', - nameFilter - ), - }), - }); + const { filteredDiffusersModels, isDiffusersModelLoading } = + useGetMainModelsQuery(ALL_BASE_MODELS, { + selectFromResult: ({ data, isLoading }) => ({ + filteredDiffusersModels: modelsFilter( + data, + 'main', + 'diffusers', + nameFilter + ), + isDiffusersModelLoading: isLoading, + }), + }); - const { filteredCheckpointModels } = useGetMainModelsQuery(ALL_BASE_MODELS, { - selectFromResult: ({ data }) => ({ - filteredCheckpointModels: modelsFilter( - data, - 'main', - 'checkpoint', - nameFilter - ), - }), - }); + const { filteredCheckpointModels, isCheckpointModelLoading } = + useGetMainModelsQuery(ALL_BASE_MODELS, { + selectFromResult: ({ data, isLoading }) => ({ + filteredCheckpointModels: modelsFilter( + data, + 'main', + 'checkpoint', + nameFilter + ), + isCheckpointModelLoading: isLoading, + }), + }); - const { filteredLoraModels } = useGetLoRAModelsQuery(undefined, { - selectFromResult: ({ data }) => ({ - filteredLoraModels: modelsFilter(data, 'lora', undefined, nameFilter), - }), - }); + const { filteredLoraModels, isLoadingLoraModels } = useGetLoRAModelsQuery( + undefined, + { + selectFromResult: ({ data, isLoading }) => ({ + filteredLoraModels: modelsFilter(data, 'lora', undefined, nameFilter), + isLoadingLoraModels: isLoading, + }), + } + ); const { filteredOnnxModels } = useGetOnnxModelsQuery(ALL_BASE_MODELS, { selectFromResult: ({ data }) => ({ @@ -79,13 +87,47 @@ const ModelList = (props: ModelListProps) => { setNameFilter(e.target.value); }, []); + const renderModelList = ( + filterArray: Partial[], + isLoading: boolean, + loadingMessage: string, + title: string, + modelList: MainModelConfigEntity[] | LoRAModelConfigEntity[] + ) => { + if (!filterArray.includes(modelFormatFilter)) return; + + if (isLoading) { + return ; + } + + if (modelList.length === 0) return; + + return ( + + + + {title} + + {modelList.map((model) => ( + + ))} + + + ); + }; + return ( setModelFormatFilter('images')} - isChecked={modelFormatFilter === 'images'} + onClick={() => setModelFormatFilter('all')} + isChecked={modelFormatFilter === 'all'} size="sm" > {t('modelManager.allModels')} @@ -287,3 +329,26 @@ const StyledModelContainer = (props: PropsWithChildren) => { ); }; + +const FetchingModelsLoader = ({ + loadingMessage, +}: { + loadingMessage?: string; +}) => { + return ( + + + + + {loadingMessage ? loadingMessage : 'Fetching...'} + + + + ); +}; From f40466983150669b9767a1d9b87c70dbb669fea0 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:02:12 +1200 Subject: [PATCH 2/7] fix: Rename loading vars for consistency --- .../subpanels/ModelManagerPanel/ModelList.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx index f29b9c1086..7a67a6b1ef 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx @@ -35,7 +35,7 @@ const ModelList = (props: ModelListProps) => { const [modelFormatFilter, setModelFormatFilter] = useState('all'); - const { filteredDiffusersModels, isDiffusersModelLoading } = + const { filteredDiffusersModels, isLoadingDiffusersModels } = useGetMainModelsQuery(ALL_BASE_MODELS, { selectFromResult: ({ data, isLoading }) => ({ filteredDiffusersModels: modelsFilter( @@ -44,11 +44,11 @@ const ModelList = (props: ModelListProps) => { 'diffusers', nameFilter ), - isDiffusersModelLoading: isLoading, + isLoadingDiffusersModels: isLoading, }), }); - const { filteredCheckpointModels, isCheckpointModelLoading } = + const { filteredCheckpointModels, isLoadingCheckpointModels } = useGetMainModelsQuery(ALL_BASE_MODELS, { selectFromResult: ({ data, isLoading }) => ({ filteredCheckpointModels: modelsFilter( @@ -57,7 +57,7 @@ const ModelList = (props: ModelListProps) => { 'checkpoint', nameFilter ), - isCheckpointModelLoading: isLoading, + isLoadingCheckpointModels: isLoading, }), }); From dcc274a2b9f5a52f4cd6e0a23fff009fefa96e11 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Mon, 31 Jul 2023 23:09:00 +1200 Subject: [PATCH 3/7] feat: Make ModelListWrapper instead of rendering conditionally --- .../subpanels/ModelManagerPanel/ModelList.tsx | 249 ++++++++---------- .../web/src/services/api/endpoints/models.ts | 9 +- 2 files changed, 123 insertions(+), 135 deletions(-) diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx index 7a67a6b1ef..44626c04c3 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx @@ -71,56 +71,30 @@ const ModelList = (props: ModelListProps) => { } ); - const { filteredOnnxModels } = useGetOnnxModelsQuery(ALL_BASE_MODELS, { - selectFromResult: ({ data }) => ({ - filteredOnnxModels: modelsFilter(data, 'onnx', 'onnx', nameFilter), - }), - }); + const { filteredOnnxModels, isLoadingOnnxModels } = useGetOnnxModelsQuery( + ALL_BASE_MODELS, + { + selectFromResult: ({ data, isLoading }) => ({ + filteredOnnxModels: modelsFilter(data, 'onnx', 'onnx', nameFilter), + isLoadingOnnxModels: isLoading, + }), + } + ); - const { filteredOliveModels } = useGetOnnxModelsQuery(ALL_BASE_MODELS, { - selectFromResult: ({ data }) => ({ - filteredOliveModels: modelsFilter(data, 'onnx', 'olive', nameFilter), - }), - }); + const { filteredOliveModels, isLoadingOliveModels } = useGetOnnxModelsQuery( + ALL_BASE_MODELS, + { + selectFromResult: ({ data, isLoading }) => ({ + filteredOliveModels: modelsFilter(data, 'onnx', 'olive', nameFilter), + isLoadingOliveModels: isLoading, + }), + } + ); const handleSearchFilter = useCallback((e: ChangeEvent) => { setNameFilter(e.target.value); }, []); - const renderModelList = ( - filterArray: Partial[], - isLoading: boolean, - loadingMessage: string, - title: string, - modelList: MainModelConfigEntity[] | LoRAModelConfigEntity[] - ) => { - if (!filterArray.includes(modelFormatFilter)) return; - - if (isLoading) { - return ; - } - - if (modelList.length === 0) return; - - return ( - - - - {title} - - {modelList.map((model) => ( - - ))} - - - ); - }; - return ( @@ -181,95 +155,76 @@ const ModelList = (props: ModelListProps) => { maxHeight={window.innerHeight - 280} overflow="scroll" > - {['images', 'diffusers'].includes(modelFormatFilter) && + {/* Diffusers List */} + {isLoadingDiffusersModels && ( + + )} + {['all', 'diffusers'].includes(modelFormatFilter) && + !isLoadingDiffusersModels && filteredDiffusersModels.length > 0 && ( - - - - Diffusers - - {filteredDiffusersModels.map((model) => ( - - ))} - - + )} - {['images', 'checkpoint'].includes(modelFormatFilter) && + {/* Checkpoints List */} + {isLoadingCheckpointModels && ( + + )} + {['all', 'checkpoint'].includes(modelFormatFilter) && + !isLoadingCheckpointModels && filteredCheckpointModels.length > 0 && ( - - - - Checkpoints - - {filteredCheckpointModels.map((model) => ( - - ))} - - + )} - {['images', 'olive'].includes(modelFormatFilter) && - filteredOliveModels.length > 0 && ( - - - - Olives - - {filteredOliveModels.map((model) => ( - - ))} - - - )} - {['images', 'onnx'].includes(modelFormatFilter) && - filteredOnnxModels.length > 0 && ( - - - - Onnx - - {filteredOnnxModels.map((model) => ( - - ))} - - - )} - {['images', 'lora'].includes(modelFormatFilter) && + + {/* LoRAs List */} + {isLoadingLoraModels && ( + + )} + {['all', 'lora'].includes(modelFormatFilter) && + !isLoadingLoraModels && filteredLoraModels.length > 0 && ( - - - - LoRAs - - {filteredLoraModels.map((model) => ( - - ))} - - + + )} + {/* Olive List */} + {isLoadingOliveModels && ( + + )} + {['all', 'olive'].includes(modelFormatFilter) && + !isLoadingOliveModels && + filteredOliveModels.length > 0 && ( + + )} + {/* Onnx List */} + {isLoadingOnnxModels && ( + + )} + {['all', 'onnx'].includes(modelFormatFilter) && + !isLoadingOnnxModels && + filteredOnnxModels.length > 0 && ( + )} @@ -330,11 +285,37 @@ const StyledModelContainer = (props: PropsWithChildren) => { ); }; -const FetchingModelsLoader = ({ - loadingMessage, -}: { - loadingMessage?: string; -}) => { +type ModelListWrapperProps = { + title: string; + modelList: + | MainModelConfigEntity[] + | LoRAModelConfigEntity[] + | OnnxModelConfigEntity[]; + selected: ModelListProps; +}; + +function ModelListWrapper(props: ModelListWrapperProps) { + const { title, modelList, selected } = props; + return ( + + + + {title} + + {modelList.map((model) => ( + + ))} + + + ); +} + +function FetchingModelsLoader({ loadingMessage }: { loadingMessage?: string }) { return ( ); -}; +} diff --git a/invokeai/frontend/web/src/services/api/endpoints/models.ts b/invokeai/frontend/web/src/services/api/endpoints/models.ts index e994d3fd3a..a7b1323f36 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/models.ts +++ b/invokeai/frontend/web/src/services/api/endpoints/models.ts @@ -181,7 +181,7 @@ export const modelsApi = api.injectEndpoints({ }, providesTags: (result, error, arg) => { const tags: ApiFullTagDescription[] = [ - { id: 'OnnxModel', type: LIST_TAG }, + { type: 'OnnxModel', id: LIST_TAG }, ]; if (result) { @@ -266,6 +266,7 @@ export const modelsApi = api.injectEndpoints({ invalidatesTags: [ { type: 'MainModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG }, + { type: 'OnnxModel', id: LIST_TAG }, ], }), importMainModels: build.mutation< @@ -282,6 +283,7 @@ export const modelsApi = api.injectEndpoints({ invalidatesTags: [ { type: 'MainModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG }, + { type: 'OnnxModel', id: LIST_TAG }, ], }), addMainModels: build.mutation({ @@ -295,6 +297,7 @@ export const modelsApi = api.injectEndpoints({ invalidatesTags: [ { type: 'MainModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG }, + { type: 'OnnxModel', id: LIST_TAG }, ], }), deleteMainModels: build.mutation< @@ -310,6 +313,7 @@ export const modelsApi = api.injectEndpoints({ invalidatesTags: [ { type: 'MainModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG }, + { type: 'OnnxModel', id: LIST_TAG }, ], }), convertMainModels: build.mutation< @@ -326,6 +330,7 @@ export const modelsApi = api.injectEndpoints({ invalidatesTags: [ { type: 'MainModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG }, + { type: 'OnnxModel', id: LIST_TAG }, ], }), mergeMainModels: build.mutation({ @@ -339,6 +344,7 @@ export const modelsApi = api.injectEndpoints({ invalidatesTags: [ { type: 'MainModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG }, + { type: 'OnnxModel', id: LIST_TAG }, ], }), syncModels: build.mutation({ @@ -351,6 +357,7 @@ export const modelsApi = api.injectEndpoints({ invalidatesTags: [ { type: 'MainModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG }, + { type: 'OnnxModel', id: LIST_TAG }, ], }), getLoRAModels: build.query, void>({ From 403a6e88f204a1ebdede0971bbde0b845028a8ed Mon Sep 17 00:00:00 2001 From: Alexandre Macabies Date: Tue, 1 Aug 2023 15:22:59 +0200 Subject: [PATCH 4/7] fix: flake: add opencv with CUDA, new patchmatch dependency. --- flake.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/flake.nix b/flake.nix index 1f35c02c6e..3ccc665812 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,10 @@ cudaPackages.cudnn cudaPackages.cuda_nvrtc cudatoolkit + pkgconfig + libconfig + cmake + blas freeglut glib gperf @@ -42,6 +46,12 @@ libGLU linuxPackages.nvidia_x11 python + (opencv4.override { + enableGtk3 = true; + enableFfmpeg = true; + enableCuda = true; + enableUnfree = true; + }) stdenv.cc stdenv.cc.cc.lib xorg.libX11 From 11a44b944d942b85d493c7e90f3837ee736d894f Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Tue, 1 Aug 2023 01:29:29 -0400 Subject: [PATCH 5/7] fix installation documentation --- README.md | 13 ++++--------- docs/installation/020_INSTALL_MANUAL.md | 16 +++++++++------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3ca1ca1f26..2efaacd031 100644 --- a/README.md +++ b/README.md @@ -184,8 +184,9 @@ the command `npm install -g yarn` if needed) 6. Configure InvokeAI and install a starting set of image generation models (you only need to do this once): ```terminal - invokeai-configure + invokeai-configure --root . ``` + Don't miss the dot at the end! 7. Launch the web server (do it every time you run InvokeAI): @@ -193,15 +194,9 @@ the command `npm install -g yarn` if needed) invokeai-web ``` -8. Build Node.js assets +8. Point your browser to http://localhost:9090 to bring up the web interface. - ```terminal - cd invokeai/frontend/web/ - yarn vite build - ``` - -9. Point your browser to http://localhost:9090 to bring up the web interface. -10. Type `banana sushi` in the box on the top left and click `Invoke`. +9. Type `banana sushi` in the box on the top left and click `Invoke`. Be sure to activate the virtual environment each time before re-launching InvokeAI, using `source .venv/bin/activate` or `.venv\Scripts\activate`. diff --git a/docs/installation/020_INSTALL_MANUAL.md b/docs/installation/020_INSTALL_MANUAL.md index c0fb4c046f..84f19da9bf 100644 --- a/docs/installation/020_INSTALL_MANUAL.md +++ b/docs/installation/020_INSTALL_MANUAL.md @@ -192,8 +192,10 @@ manager, please follow these steps: your outputs. ```terminal - invokeai-configure + invokeai-configure --root . ``` + + Don't miss the dot at the end of the command! The script `invokeai-configure` will interactively guide you through the process of downloading and installing the weights files needed for InvokeAI. @@ -225,12 +227,6 @@ manager, please follow these steps: !!! warning "Make sure that the virtual environment is activated, which should create `(.venv)` in front of your prompt!" - === "CLI" - - ```bash - invokeai - ``` - === "local Webserver" ```bash @@ -243,6 +239,12 @@ manager, please follow these steps: invokeai --web --host 0.0.0.0 ``` + === "CLI" + + ```bash + invokeai + ``` + If you choose the run the web interface, point your browser at http://localhost:9090 in order to load the GUI. From 242d860a472e92d132869f7d8951f43d1298df94 Mon Sep 17 00:00:00 2001 From: Zerdoumi <3418725+sohelzerdoumi@users.noreply.github.com> Date: Tue, 1 Aug 2023 15:57:13 +0200 Subject: [PATCH 6/7] fix https/wss behind reverse proxy --- invokeai/frontend/web/src/services/events/middleware.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/invokeai/frontend/web/src/services/events/middleware.ts b/invokeai/frontend/web/src/services/events/middleware.ts index 0c3b275274..75f2d48de8 100644 --- a/invokeai/frontend/web/src/services/events/middleware.ts +++ b/invokeai/frontend/web/src/services/events/middleware.ts @@ -12,8 +12,9 @@ import { socketSubscribed, socketUnsubscribed } from './actions'; export const socketMiddleware = () => { let areListenersSet = false; - - let socketUrl = `ws://${window.location.host}`; + + let wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; + let socketUrl = `${wsProtocol}://${window.location.host}`; const socketOptions: Parameters[0] = { timeout: 60000, From 4599575e65db2081886b6782f961ddf68359aecb Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 2 Aug 2023 00:08:34 +1000 Subject: [PATCH 7/7] fix(ui): use `const` for `wsProtocol`, lint --- invokeai/frontend/web/src/services/events/middleware.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/invokeai/frontend/web/src/services/events/middleware.ts b/invokeai/frontend/web/src/services/events/middleware.ts index 75f2d48de8..56992be672 100644 --- a/invokeai/frontend/web/src/services/events/middleware.ts +++ b/invokeai/frontend/web/src/services/events/middleware.ts @@ -12,8 +12,8 @@ import { socketSubscribed, socketUnsubscribed } from './actions'; export const socketMiddleware = () => { let areListenersSet = false; - - let wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; + + const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; let socketUrl = `${wsProtocol}://${window.location.host}`; const socketOptions: Parameters[0] = {