Merge branch 'main' into refactor/cleanup-root-detection

This commit is contained in:
Lincoln Stein 2023-08-02 09:46:46 -04:00 committed by GitHub
commit 0db1e97119
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 206 additions and 145 deletions

View File

@ -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): 6. Configure InvokeAI and install a starting set of image generation models (you only need to do this once):
```terminal ```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): 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 invokeai-web
``` ```
8. Build Node.js assets 8. Point your browser to http://localhost:9090 to bring up the web interface.
```terminal 9. Type `banana sushi` in the box on the top left and click `Invoke`.
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`.
Be sure to activate the virtual environment each time before re-launching InvokeAI, Be sure to activate the virtual environment each time before re-launching InvokeAI,
using `source .venv/bin/activate` or `.venv\Scripts\activate`. using `source .venv/bin/activate` or `.venv\Scripts\activate`.

View File

@ -192,9 +192,11 @@ manager, please follow these steps:
your outputs. your outputs.
```terminal ```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 The script `invokeai-configure` will interactively guide you through the
process of downloading and installing the weights files needed for InvokeAI. process of downloading and installing the weights files needed for InvokeAI.
Note that the main Stable Diffusion weights file is protected by a license Note that the main Stable Diffusion weights file is protected by a license
@ -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!" !!! warning "Make sure that the virtual environment is activated, which should create `(.venv)` in front of your prompt!"
=== "CLI"
```bash
invokeai
```
=== "local Webserver" === "local Webserver"
```bash ```bash
@ -243,6 +239,12 @@ manager, please follow these steps:
invokeai --web --host 0.0.0.0 invokeai --web --host 0.0.0.0
``` ```
=== "CLI"
```bash
invokeai
```
If you choose the run the web interface, point your browser at If you choose the run the web interface, point your browser at
http://localhost:9090 in order to load the GUI. http://localhost:9090 in order to load the GUI.

View File

@ -34,6 +34,10 @@
cudaPackages.cudnn cudaPackages.cudnn
cudaPackages.cuda_nvrtc cudaPackages.cuda_nvrtc
cudatoolkit cudatoolkit
pkgconfig
libconfig
cmake
blas
freeglut freeglut
glib glib
gperf gperf
@ -42,6 +46,12 @@
libGLU libGLU
linuxPackages.nvidia_x11 linuxPackages.nvidia_x11
python python
(opencv4.override {
enableGtk3 = true;
enableFfmpeg = true;
enableCuda = true;
enableUnfree = true;
})
stdenv.cc stdenv.cc
stdenv.cc.cc.lib stdenv.cc.cc.lib
xorg.libX11 xorg.libX11

View File

@ -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 { EntityState } from '@reduxjs/toolkit';
import IAIButton from 'common/components/IAIButton'; import IAIButton from 'common/components/IAIButton';
import IAIInput from 'common/components/IAIInput'; import IAIInput from 'common/components/IAIInput';
@ -6,23 +6,23 @@ import { forEach } from 'lodash-es';
import type { ChangeEvent, PropsWithChildren } from 'react'; import type { ChangeEvent, PropsWithChildren } from 'react';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ALL_BASE_MODELS } from 'services/api/constants';
import { import {
LoRAModelConfigEntity,
MainModelConfigEntity, MainModelConfigEntity,
OnnxModelConfigEntity, OnnxModelConfigEntity,
useGetLoRAModelsQuery,
useGetMainModelsQuery, useGetMainModelsQuery,
useGetOnnxModelsQuery, useGetOnnxModelsQuery,
useGetLoRAModelsQuery,
LoRAModelConfigEntity,
} from 'services/api/endpoints/models'; } from 'services/api/endpoints/models';
import ModelListItem from './ModelListItem'; import ModelListItem from './ModelListItem';
import { ALL_BASE_MODELS } from 'services/api/constants';
type ModelListProps = { type ModelListProps = {
selectedModelId: string | undefined; selectedModelId: string | undefined;
setSelectedModelId: (name: string | undefined) => void; setSelectedModelId: (name: string | undefined) => void;
}; };
type ModelFormat = 'images' | 'checkpoint' | 'diffusers' | 'olive' | 'onnx'; type ModelFormat = 'all' | 'checkpoint' | 'diffusers' | 'olive' | 'onnx';
type ModelType = 'main' | 'lora' | 'onnx'; type ModelType = 'main' | 'lora' | 'onnx';
@ -33,47 +33,63 @@ const ModelList = (props: ModelListProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [nameFilter, setNameFilter] = useState<string>(''); const [nameFilter, setNameFilter] = useState<string>('');
const [modelFormatFilter, setModelFormatFilter] = const [modelFormatFilter, setModelFormatFilter] =
useState<CombinedModelFormat>('images'); useState<CombinedModelFormat>('all');
const { filteredDiffusersModels } = useGetMainModelsQuery(ALL_BASE_MODELS, { const { filteredDiffusersModels, isLoadingDiffusersModels } =
selectFromResult: ({ data }) => ({ useGetMainModelsQuery(ALL_BASE_MODELS, {
selectFromResult: ({ data, isLoading }) => ({
filteredDiffusersModels: modelsFilter( filteredDiffusersModels: modelsFilter(
data, data,
'main', 'main',
'diffusers', 'diffusers',
nameFilter nameFilter
), ),
isLoadingDiffusersModels: isLoading,
}), }),
}); });
const { filteredCheckpointModels } = useGetMainModelsQuery(ALL_BASE_MODELS, { const { filteredCheckpointModels, isLoadingCheckpointModels } =
selectFromResult: ({ data }) => ({ useGetMainModelsQuery(ALL_BASE_MODELS, {
selectFromResult: ({ data, isLoading }) => ({
filteredCheckpointModels: modelsFilter( filteredCheckpointModels: modelsFilter(
data, data,
'main', 'main',
'checkpoint', 'checkpoint',
nameFilter nameFilter
), ),
isLoadingCheckpointModels: isLoading,
}), }),
}); });
const { filteredLoraModels } = useGetLoRAModelsQuery(undefined, { const { filteredLoraModels, isLoadingLoraModels } = useGetLoRAModelsQuery(
selectFromResult: ({ data }) => ({ undefined,
{
selectFromResult: ({ data, isLoading }) => ({
filteredLoraModels: modelsFilter(data, 'lora', undefined, nameFilter), filteredLoraModels: modelsFilter(data, 'lora', undefined, nameFilter),
isLoadingLoraModels: isLoading,
}), }),
}); }
);
const { filteredOnnxModels } = useGetOnnxModelsQuery(ALL_BASE_MODELS, { const { filteredOnnxModels, isLoadingOnnxModels } = useGetOnnxModelsQuery(
selectFromResult: ({ data }) => ({ ALL_BASE_MODELS,
{
selectFromResult: ({ data, isLoading }) => ({
filteredOnnxModels: modelsFilter(data, 'onnx', 'onnx', nameFilter), filteredOnnxModels: modelsFilter(data, 'onnx', 'onnx', nameFilter),
isLoadingOnnxModels: isLoading,
}), }),
}); }
);
const { filteredOliveModels } = useGetOnnxModelsQuery(ALL_BASE_MODELS, { const { filteredOliveModels, isLoadingOliveModels } = useGetOnnxModelsQuery(
selectFromResult: ({ data }) => ({ ALL_BASE_MODELS,
{
selectFromResult: ({ data, isLoading }) => ({
filteredOliveModels: modelsFilter(data, 'onnx', 'olive', nameFilter), filteredOliveModels: modelsFilter(data, 'onnx', 'olive', nameFilter),
isLoadingOliveModels: isLoading,
}), }),
}); }
);
const handleSearchFilter = useCallback((e: ChangeEvent<HTMLInputElement>) => { const handleSearchFilter = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setNameFilter(e.target.value); setNameFilter(e.target.value);
@ -84,8 +100,8 @@ const ModelList = (props: ModelListProps) => {
<Flex flexDirection="column" gap={4} paddingInlineEnd={4}> <Flex flexDirection="column" gap={4} paddingInlineEnd={4}>
<ButtonGroup isAttached> <ButtonGroup isAttached>
<IAIButton <IAIButton
onClick={() => setModelFormatFilter('images')} onClick={() => setModelFormatFilter('all')}
isChecked={modelFormatFilter === 'images'} isChecked={modelFormatFilter === 'all'}
size="sm" size="sm"
> >
{t('modelManager.allModels')} {t('modelManager.allModels')}
@ -139,95 +155,76 @@ const ModelList = (props: ModelListProps) => {
maxHeight={window.innerHeight - 280} maxHeight={window.innerHeight - 280}
overflow="scroll" overflow="scroll"
> >
{['images', 'diffusers'].includes(modelFormatFilter) && {/* Diffusers List */}
{isLoadingDiffusersModels && (
<FetchingModelsLoader loadingMessage="Loading Diffusers..." />
)}
{['all', 'diffusers'].includes(modelFormatFilter) &&
!isLoadingDiffusersModels &&
filteredDiffusersModels.length > 0 && ( filteredDiffusersModels.length > 0 && (
<StyledModelContainer> <ModelListWrapper
<Flex sx={{ gap: 2, flexDir: 'column' }}> title="Diffusers"
<Text variant="subtext" fontSize="sm"> modelList={filteredDiffusersModels}
Diffusers selected={{ selectedModelId, setSelectedModelId }}
</Text> key="diffusers"
{filteredDiffusersModels.map((model) => (
<ModelListItem
key={model.id}
model={model}
isSelected={selectedModelId === model.id}
setSelectedModelId={setSelectedModelId}
/> />
))}
</Flex>
</StyledModelContainer>
)} )}
{['images', 'checkpoint'].includes(modelFormatFilter) && {/* Checkpoints List */}
{isLoadingCheckpointModels && (
<FetchingModelsLoader loadingMessage="Loading Checkpoints..." />
)}
{['all', 'checkpoint'].includes(modelFormatFilter) &&
!isLoadingCheckpointModels &&
filteredCheckpointModels.length > 0 && ( filteredCheckpointModels.length > 0 && (
<StyledModelContainer> <ModelListWrapper
<Flex sx={{ gap: 2, flexDir: 'column' }}> title="Checkpoints"
<Text variant="subtext" fontSize="sm"> modelList={filteredCheckpointModels}
Checkpoints selected={{ selectedModelId, setSelectedModelId }}
</Text> key="checkpoints"
{filteredCheckpointModels.map((model) => (
<ModelListItem
key={model.id}
model={model}
isSelected={selectedModelId === model.id}
setSelectedModelId={setSelectedModelId}
/> />
))}
</Flex>
</StyledModelContainer>
)} )}
{['images', 'olive'].includes(modelFormatFilter) &&
filteredOliveModels.length > 0 && ( {/* LoRAs List */}
<StyledModelContainer> {isLoadingLoraModels && (
<Flex sx={{ gap: 2, flexDir: 'column' }}> <FetchingModelsLoader loadingMessage="Loading LoRAs..." />
<Text variant="subtext" fontSize="sm">
Olives
</Text>
{filteredOliveModels.map((model) => (
<ModelListItem
key={model.id}
model={model}
isSelected={selectedModelId === model.id}
setSelectedModelId={setSelectedModelId}
/>
))}
</Flex>
</StyledModelContainer>
)} )}
{['images', 'onnx'].includes(modelFormatFilter) && {['all', 'lora'].includes(modelFormatFilter) &&
filteredOnnxModels.length > 0 && ( !isLoadingLoraModels &&
<StyledModelContainer>
<Flex sx={{ gap: 2, flexDir: 'column' }}>
<Text variant="subtext" fontSize="sm">
Onnx
</Text>
{filteredOnnxModels.map((model) => (
<ModelListItem
key={model.id}
model={model}
isSelected={selectedModelId === model.id}
setSelectedModelId={setSelectedModelId}
/>
))}
</Flex>
</StyledModelContainer>
)}
{['images', 'lora'].includes(modelFormatFilter) &&
filteredLoraModels.length > 0 && ( filteredLoraModels.length > 0 && (
<StyledModelContainer> <ModelListWrapper
<Flex sx={{ gap: 2, flexDir: 'column' }}> title="LoRAs"
<Text variant="subtext" fontSize="sm"> modelList={filteredLoraModels}
LoRAs selected={{ selectedModelId, setSelectedModelId }}
</Text> key="loras"
{filteredLoraModels.map((model) => ( />
<ModelListItem )}
key={model.id} {/* Olive List */}
model={model} {isLoadingOliveModels && (
isSelected={selectedModelId === model.id} <FetchingModelsLoader loadingMessage="Loading Olives..." />
setSelectedModelId={setSelectedModelId} )}
{['all', 'olive'].includes(modelFormatFilter) &&
!isLoadingOliveModels &&
filteredOliveModels.length > 0 && (
<ModelListWrapper
title="Olives"
modelList={filteredOliveModels}
selected={{ selectedModelId, setSelectedModelId }}
key="olive"
/>
)}
{/* Onnx List */}
{isLoadingOnnxModels && (
<FetchingModelsLoader loadingMessage="Loading ONNX..." />
)}
{['all', 'onnx'].includes(modelFormatFilter) &&
!isLoadingOnnxModels &&
filteredOnnxModels.length > 0 && (
<ModelListWrapper
title="ONNX"
modelList={filteredOnnxModels}
selected={{ selectedModelId, setSelectedModelId }}
key="onnx"
/> />
))}
</Flex>
</StyledModelContainer>
)} )}
</Flex> </Flex>
</Flex> </Flex>
@ -287,3 +284,52 @@ const StyledModelContainer = (props: PropsWithChildren) => {
</Flex> </Flex>
); );
}; };
type ModelListWrapperProps = {
title: string;
modelList:
| MainModelConfigEntity[]
| LoRAModelConfigEntity[]
| OnnxModelConfigEntity[];
selected: ModelListProps;
};
function ModelListWrapper(props: ModelListWrapperProps) {
const { title, modelList, selected } = props;
return (
<StyledModelContainer>
<Flex sx={{ gap: 2, flexDir: 'column' }}>
<Text variant="subtext" fontSize="sm">
{title}
</Text>
{modelList.map((model) => (
<ModelListItem
key={model.id}
model={model}
isSelected={selected.selectedModelId === model.id}
setSelectedModelId={selected.setSelectedModelId}
/>
))}
</Flex>
</StyledModelContainer>
);
}
function FetchingModelsLoader({ loadingMessage }: { loadingMessage?: string }) {
return (
<StyledModelContainer>
<Flex
justifyContent="center"
alignItems="center"
flexDirection="column"
p={4}
gap={8}
>
<Spinner />
<Text variant="subtext">
{loadingMessage ? loadingMessage : 'Fetching...'}
</Text>
</Flex>
</StyledModelContainer>
);
}

View File

@ -181,7 +181,7 @@ export const modelsApi = api.injectEndpoints({
}, },
providesTags: (result, error, arg) => { providesTags: (result, error, arg) => {
const tags: ApiFullTagDescription[] = [ const tags: ApiFullTagDescription[] = [
{ id: 'OnnxModel', type: LIST_TAG }, { type: 'OnnxModel', id: LIST_TAG },
]; ];
if (result) { if (result) {
@ -266,6 +266,7 @@ export const modelsApi = api.injectEndpoints({
invalidatesTags: [ invalidatesTags: [
{ type: 'MainModel', id: LIST_TAG }, { type: 'MainModel', id: LIST_TAG },
{ type: 'SDXLRefinerModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG },
{ type: 'OnnxModel', id: LIST_TAG },
], ],
}), }),
importMainModels: build.mutation< importMainModels: build.mutation<
@ -282,6 +283,7 @@ export const modelsApi = api.injectEndpoints({
invalidatesTags: [ invalidatesTags: [
{ type: 'MainModel', id: LIST_TAG }, { type: 'MainModel', id: LIST_TAG },
{ type: 'SDXLRefinerModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG },
{ type: 'OnnxModel', id: LIST_TAG },
], ],
}), }),
addMainModels: build.mutation<AddMainModelResponse, AddMainModelArg>({ addMainModels: build.mutation<AddMainModelResponse, AddMainModelArg>({
@ -295,6 +297,7 @@ export const modelsApi = api.injectEndpoints({
invalidatesTags: [ invalidatesTags: [
{ type: 'MainModel', id: LIST_TAG }, { type: 'MainModel', id: LIST_TAG },
{ type: 'SDXLRefinerModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG },
{ type: 'OnnxModel', id: LIST_TAG },
], ],
}), }),
deleteMainModels: build.mutation< deleteMainModels: build.mutation<
@ -310,6 +313,7 @@ export const modelsApi = api.injectEndpoints({
invalidatesTags: [ invalidatesTags: [
{ type: 'MainModel', id: LIST_TAG }, { type: 'MainModel', id: LIST_TAG },
{ type: 'SDXLRefinerModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG },
{ type: 'OnnxModel', id: LIST_TAG },
], ],
}), }),
convertMainModels: build.mutation< convertMainModels: build.mutation<
@ -326,6 +330,7 @@ export const modelsApi = api.injectEndpoints({
invalidatesTags: [ invalidatesTags: [
{ type: 'MainModel', id: LIST_TAG }, { type: 'MainModel', id: LIST_TAG },
{ type: 'SDXLRefinerModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG },
{ type: 'OnnxModel', id: LIST_TAG },
], ],
}), }),
mergeMainModels: build.mutation<MergeMainModelResponse, MergeMainModelArg>({ mergeMainModels: build.mutation<MergeMainModelResponse, MergeMainModelArg>({
@ -339,6 +344,7 @@ export const modelsApi = api.injectEndpoints({
invalidatesTags: [ invalidatesTags: [
{ type: 'MainModel', id: LIST_TAG }, { type: 'MainModel', id: LIST_TAG },
{ type: 'SDXLRefinerModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG },
{ type: 'OnnxModel', id: LIST_TAG },
], ],
}), }),
syncModels: build.mutation<SyncModelsResponse, void>({ syncModels: build.mutation<SyncModelsResponse, void>({
@ -351,6 +357,7 @@ export const modelsApi = api.injectEndpoints({
invalidatesTags: [ invalidatesTags: [
{ type: 'MainModel', id: LIST_TAG }, { type: 'MainModel', id: LIST_TAG },
{ type: 'SDXLRefinerModel', id: LIST_TAG }, { type: 'SDXLRefinerModel', id: LIST_TAG },
{ type: 'OnnxModel', id: LIST_TAG },
], ],
}), }),
getLoRAModels: build.query<EntityState<LoRAModelConfigEntity>, void>({ getLoRAModels: build.query<EntityState<LoRAModelConfigEntity>, void>({

View File

@ -13,7 +13,8 @@ import { socketSubscribed, socketUnsubscribed } from './actions';
export const socketMiddleware = () => { export const socketMiddleware = () => {
let areListenersSet = false; let areListenersSet = false;
let socketUrl = `ws://${window.location.host}`; const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
let socketUrl = `${wsProtocol}://${window.location.host}`;
const socketOptions: Parameters<typeof io>[0] = { const socketOptions: Parameters<typeof io>[0] = {
timeout: 60000, timeout: 60000,