mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Adds error handling to & improves model switching UI
This commit is contained in:
parent
27a7980dad
commit
0adb7d4676
File diff suppressed because it is too large
Load Diff
1
frontend/dist/assets/index.549d985b.css
vendored
1
frontend/dist/assets/index.549d985b.css
vendored
File diff suppressed because one or more lines are too long
517
frontend/dist/assets/index.647e65c7.js
vendored
517
frontend/dist/assets/index.647e65c7.js
vendored
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index.9dddc756.css
vendored
Normal file
1
frontend/dist/assets/index.9dddc756.css
vendored
Normal file
File diff suppressed because one or more lines are too long
517
frontend/dist/assets/index.c9288511.js
vendored
Normal file
517
frontend/dist/assets/index.c9288511.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
frontend/dist/index.html
vendored
4
frontend/dist/index.html
vendored
@ -6,8 +6,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>InvokeAI - A Stable Diffusion Toolkit</title>
|
||||
<link rel="shortcut icon" type="icon" href="./assets/favicon.0d253ced.ico" />
|
||||
<script type="module" crossorigin src="./assets/index.647e65c7.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.549d985b.css">
|
||||
<script type="module" crossorigin src="./assets/index.c9288511.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.9dddc756.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
11
frontend/src/app/invokeai.d.ts
vendored
11
frontend/src/app/invokeai.d.ts
vendored
@ -142,11 +142,13 @@ export declare type SystemConfig = {
|
||||
model_hash: string;
|
||||
app_id: string;
|
||||
app_version: string;
|
||||
available_models?: ModelList;
|
||||
model_list: ModelList;
|
||||
};
|
||||
|
||||
export declare type ModelStatus = 'active' | 'cached' | 'not loaded';
|
||||
|
||||
export declare type Model = {
|
||||
status: 'active' | 'cached' | 'not loaded';
|
||||
status: ModelStatus;
|
||||
description: string;
|
||||
};
|
||||
|
||||
@ -156,6 +158,11 @@ export declare type ModelList = Record<string, Model>;
|
||||
* These types type data received from the server via socketio.
|
||||
*/
|
||||
|
||||
export declare type ModelChangeResponse = {
|
||||
model_name: string;
|
||||
model_list: ModelList;
|
||||
};
|
||||
|
||||
export declare type SystemStatusResponse = SystemStatus;
|
||||
|
||||
export declare type SystemConfigResponse = SystemConfig;
|
||||
|
@ -31,4 +31,4 @@ export const requestSystemConfig = createAction<undefined>(
|
||||
'socketio/requestSystemConfig'
|
||||
);
|
||||
|
||||
export const setModel = createAction<string>('socketio/setModel');
|
||||
export const requestModelChange = createAction<string>('socketio/requestModelChange');
|
||||
|
@ -8,6 +8,8 @@ import {
|
||||
import {
|
||||
addLogEntry,
|
||||
errorOccurred,
|
||||
setCurrentStatus,
|
||||
setIsCancelable,
|
||||
setIsProcessing,
|
||||
} from '../../features/system/systemSlice';
|
||||
import { inpaintingImageElementRef } from '../../features/tabs/Inpainting/InpaintingCanvas';
|
||||
@ -177,8 +179,11 @@ const makeSocketIOEmitters = (
|
||||
emitRequestSystemConfig: () => {
|
||||
socketio.emit('requestSystemConfig');
|
||||
},
|
||||
emitSetModel: (modelName: string) => {
|
||||
socketio.emit('setModel', modelName);
|
||||
emitRequestModelChange: (modelName: string) => {
|
||||
dispatch(setCurrentStatus('Changing Model'));
|
||||
dispatch(setIsProcessing(true));
|
||||
dispatch(setIsCancelable(false));
|
||||
socketio.emit('requestModelChange', modelName);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -13,6 +13,8 @@ import {
|
||||
setSystemConfig,
|
||||
processingCanceled,
|
||||
errorOccurred,
|
||||
setModelList,
|
||||
setIsCancelable,
|
||||
} from '../../features/system/systemSlice';
|
||||
|
||||
import {
|
||||
@ -289,6 +291,34 @@ const makeSocketIOListeners = (
|
||||
onSystemConfig: (data: InvokeAI.SystemConfig) => {
|
||||
dispatch(setSystemConfig(data));
|
||||
},
|
||||
onModelChanged: (data: InvokeAI.ModelChangeResponse) => {
|
||||
const { model_name, model_list } = data;
|
||||
dispatch(setModelList(model_list));
|
||||
dispatch(setCurrentStatus('Connected'));
|
||||
dispatch(setIsProcessing(false));
|
||||
dispatch(setIsCancelable(false));
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Model changed: ${model_name}`,
|
||||
level: 'info',
|
||||
})
|
||||
);
|
||||
},
|
||||
onModelChangeFailed: (data: InvokeAI.ModelChangeResponse) => {
|
||||
const { model_name, model_list } = data;
|
||||
dispatch(setModelList(model_list));
|
||||
dispatch(setIsProcessing(false));
|
||||
dispatch(setIsCancelable(false));
|
||||
dispatch(errorOccurred());
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Model change failed: ${model_name}`,
|
||||
level: 'error',
|
||||
})
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -46,6 +46,8 @@ export const socketioMiddleware = () => {
|
||||
onInitialImageUploaded,
|
||||
onMaskImageUploaded,
|
||||
onSystemConfig,
|
||||
onModelChanged,
|
||||
onModelChangeFailed,
|
||||
} = makeSocketIOListeners(store);
|
||||
|
||||
const {
|
||||
@ -59,7 +61,7 @@ export const socketioMiddleware = () => {
|
||||
emitUploadInitialImage,
|
||||
emitUploadMaskImage,
|
||||
emitRequestSystemConfig,
|
||||
emitSetModel,
|
||||
emitRequestModelChange,
|
||||
} = makeSocketIOEmitters(store, socketio);
|
||||
|
||||
/**
|
||||
@ -114,6 +116,14 @@ export const socketioMiddleware = () => {
|
||||
onSystemConfig(data);
|
||||
});
|
||||
|
||||
socketio.on('modelChanged', (data: InvokeAI.ModelChangeResponse) => {
|
||||
onModelChanged(data);
|
||||
});
|
||||
|
||||
socketio.on('modelChangeFailed', (data: InvokeAI.ModelChangeResponse) => {
|
||||
onModelChangeFailed(data);
|
||||
});
|
||||
|
||||
areListenersSet = true;
|
||||
}
|
||||
|
||||
@ -171,8 +181,8 @@ export const socketioMiddleware = () => {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'socketio/setModel': {
|
||||
emitSetModel(action.payload);
|
||||
case 'socketio/requestModelChange': {
|
||||
emitRequestModelChange(action.payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +44,8 @@ const memoEqualityCheck = (
|
||||
*/
|
||||
const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { activeTabName, galleryImageObjectFit } = useAppSelector(
|
||||
hoverableImageSelector
|
||||
);
|
||||
const { activeTabName, galleryImageObjectFit, galleryImageMinimumWidth } =
|
||||
useAppSelector(hoverableImageSelector);
|
||||
const { image, isSelected } = props;
|
||||
const { url, uuid, metadata } = image;
|
||||
|
||||
@ -173,7 +172,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isHovered && (
|
||||
{isHovered && galleryImageMinimumWidth >= 64 && (
|
||||
<div className="hoverable-image-delete-button">
|
||||
<Tooltip label={'Delete image'} hasArrow>
|
||||
<DeleteImageModal image={image}>
|
||||
|
@ -42,6 +42,7 @@ export const hoverableImageSelector = createSelector(
|
||||
(options: OptionsState, gallery: GalleryState) => {
|
||||
return {
|
||||
galleryImageObjectFit: gallery.galleryImageObjectFit,
|
||||
galleryImageMinimumWidth: gallery.galleryImageMinimumWidth,
|
||||
activeTabName: tabMap[options.activeTab],
|
||||
};
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ const cancelButtonSelector = createSelector(
|
||||
return {
|
||||
isProcessing: system.isProcessing,
|
||||
isConnected: system.isConnected,
|
||||
isCancelable: system.isCancelable,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -24,17 +25,18 @@ const cancelButtonSelector = createSelector(
|
||||
|
||||
export default function CancelButton() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { isProcessing, isConnected } = useAppSelector(cancelButtonSelector);
|
||||
const { isProcessing, isConnected, isCancelable } =
|
||||
useAppSelector(cancelButtonSelector);
|
||||
const handleClickCancel = () => dispatch(cancelProcessing());
|
||||
|
||||
useHotkeys(
|
||||
'shift+x',
|
||||
() => {
|
||||
if (isConnected || isProcessing) {
|
||||
if ((isConnected || isProcessing) && isCancelable) {
|
||||
handleClickCancel();
|
||||
}
|
||||
},
|
||||
[isConnected, isProcessing]
|
||||
[isConnected, isProcessing, isCancelable]
|
||||
);
|
||||
|
||||
return (
|
||||
@ -42,7 +44,7 @@ export default function CancelButton() {
|
||||
icon={<MdCancel />}
|
||||
tooltip="Cancel"
|
||||
aria-label="Cancel"
|
||||
isDisabled={!isConnected || !isProcessing}
|
||||
isDisabled={!isConnected || !isProcessing || !isCancelable}
|
||||
onClick={handleClickCancel}
|
||||
styleClass="cancel-btn"
|
||||
/>
|
||||
|
43
frontend/src/features/system/SettingsModal/ModelList.scss
Normal file
43
frontend/src/features/system/SettingsModal/ModelList.scss
Normal file
@ -0,0 +1,43 @@
|
||||
.model-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 0.5rem;
|
||||
|
||||
.model-list-header {
|
||||
}
|
||||
|
||||
.model-list-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 0.5rem;
|
||||
|
||||
.model-list-item {
|
||||
display: flex;
|
||||
column-gap: 0.5rem;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.model-list-item-name {
|
||||
}
|
||||
.model-list-item-description {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.model-list-item-status {
|
||||
&.active {
|
||||
color: var(--status-good-color);
|
||||
}
|
||||
|
||||
&.cached {
|
||||
color: var(--status-working-color);
|
||||
}
|
||||
|
||||
&.not-loaded {
|
||||
color: var(--text-color-secondary);
|
||||
}
|
||||
}
|
||||
.model-list-item-load-btn {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
63
frontend/src/features/system/SettingsModal/ModelList.tsx
Normal file
63
frontend/src/features/system/SettingsModal/ModelList.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import { Button, Tooltip, Spacer, Heading } from '@chakra-ui/react';
|
||||
import _ from 'lodash';
|
||||
import { Model, ModelStatus } from '../../../app/invokeai';
|
||||
import { requestModelChange } from '../../../app/socketio/actions';
|
||||
import { RootState, useAppDispatch, useAppSelector } from '../../../app/store';
|
||||
|
||||
type ModelListItemProps = {
|
||||
name: string;
|
||||
status: ModelStatus;
|
||||
description: string;
|
||||
};
|
||||
|
||||
const ModelListItem = (props: ModelListItemProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { name, status, description } = props;
|
||||
const handleChangeModel = () => {
|
||||
dispatch(requestModelChange(name));
|
||||
};
|
||||
return (
|
||||
<div className="model-list-item">
|
||||
<Tooltip label={description} hasArrow placement="top">
|
||||
<div className="model-list-item-name">{name}</div>
|
||||
</Tooltip>
|
||||
<Spacer />
|
||||
<div className={`model-list-item-status ${status.split(' ').join('-')}`}>
|
||||
{status}
|
||||
</div>
|
||||
<div className="model-list-item-load-btn">
|
||||
<Button
|
||||
size={'sm'}
|
||||
onClick={handleChangeModel}
|
||||
isDisabled={status === 'active'}
|
||||
>
|
||||
Load
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ModelList = () => {
|
||||
const { model_list } = useAppSelector((state: RootState) => state.system);
|
||||
|
||||
return (
|
||||
<div className="model-list">
|
||||
<Heading size={'md'} className="model-list-header">
|
||||
Available Models
|
||||
</Heading>
|
||||
<div className="model-list-list">
|
||||
{_.map(model_list, (model: Model, key) => (
|
||||
<ModelListItem
|
||||
key={key}
|
||||
name={key}
|
||||
status={model.status}
|
||||
description={model.description}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModelList;
|
@ -2,11 +2,13 @@
|
||||
|
||||
.settings-modal {
|
||||
background-color: var(--settings-modal-bg) !important;
|
||||
max-height: 36rem;
|
||||
font-family: Inter;
|
||||
|
||||
.settings-modal-content {
|
||||
display: grid;
|
||||
row-gap: 2rem;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.settings-modal-header {
|
||||
|
@ -14,10 +14,8 @@ import {
|
||||
} from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import _, { isEqual } from 'lodash';
|
||||
import { ChangeEvent, cloneElement, ReactElement } from 'react';
|
||||
import { setModel } from '../../../app/socketio/actions';
|
||||
import { RootState, useAppDispatch, useAppSelector } from '../../../app/store';
|
||||
import IAISelect from '../../../common/components/IAISelect';
|
||||
import { cloneElement, ReactElement } from 'react';
|
||||
import { RootState, useAppSelector } from '../../../app/store';
|
||||
import { persistor } from '../../../main';
|
||||
import {
|
||||
setShouldConfirmOnDelete,
|
||||
@ -25,6 +23,7 @@ import {
|
||||
setShouldDisplayInProgress,
|
||||
SystemState,
|
||||
} from '../systemSlice';
|
||||
import ModelList from './ModelList';
|
||||
import SettingsModalItem from './SettingsModalItem';
|
||||
|
||||
const systemSelector = createSelector(
|
||||
@ -34,15 +33,13 @@ const systemSelector = createSelector(
|
||||
shouldDisplayInProgress,
|
||||
shouldConfirmOnDelete,
|
||||
shouldDisplayGuides,
|
||||
available_models,
|
||||
model_list,
|
||||
} = system;
|
||||
return {
|
||||
shouldDisplayInProgress,
|
||||
shouldConfirmOnDelete,
|
||||
shouldDisplayGuides,
|
||||
models: available_models
|
||||
? _.map(available_models, (model, key) => key)
|
||||
: [],
|
||||
models: _.map(model_list, (_model, key) => key),
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -62,7 +59,6 @@ type SettingsModalProps = {
|
||||
* Secondary post-reset modal is included here.
|
||||
*/
|
||||
const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
isOpen: isSettingsModalOpen,
|
||||
onOpen: onSettingsModalOpen,
|
||||
@ -79,7 +75,6 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
shouldDisplayInProgress,
|
||||
shouldConfirmOnDelete,
|
||||
shouldDisplayGuides,
|
||||
models,
|
||||
} = useAppSelector(systemSelector);
|
||||
|
||||
/**
|
||||
@ -93,10 +88,6 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleChangeModel = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
dispatch(setModel(e.target.value));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{cloneElement(children, {
|
||||
@ -109,11 +100,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
<ModalHeader className="settings-modal-header">Settings</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody className="settings-modal-content">
|
||||
<IAISelect
|
||||
label="Model"
|
||||
validValues={models}
|
||||
onChange={handleChangeModel}
|
||||
/>
|
||||
<ModelList />
|
||||
<div className="settings-modal-items">
|
||||
<SettingsModalItem
|
||||
settingTitle="Display In-Progress Images (slower)"
|
||||
|
@ -35,7 +35,7 @@ export interface SystemState
|
||||
currentStatusHasSteps: boolean;
|
||||
shouldDisplayGuides: boolean;
|
||||
wasErrorSeen: boolean;
|
||||
available_models?: InvokeAI.ModelList;
|
||||
isCancelable: boolean;
|
||||
}
|
||||
|
||||
const initialSystemState = {
|
||||
@ -61,8 +61,10 @@ const initialSystemState = {
|
||||
model_hash: '',
|
||||
app_id: '',
|
||||
app_version: '',
|
||||
model_list: {},
|
||||
hasError: false,
|
||||
wasErrorSeen: true,
|
||||
isCancelable: true,
|
||||
};
|
||||
|
||||
const initialState: SystemState = initialSystemState;
|
||||
@ -158,6 +160,15 @@ export const systemSlice = createSlice({
|
||||
state.currentStatusHasSteps = false;
|
||||
state.currentStatus = 'Processing canceled';
|
||||
},
|
||||
setModelList: (
|
||||
state,
|
||||
action: PayloadAction<InvokeAI.ModelList | Record<string, never>>
|
||||
) => {
|
||||
state.model_list = action.payload;
|
||||
},
|
||||
setIsCancelable: (state, action: PayloadAction<boolean>) => {
|
||||
state.isCancelable = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -177,6 +188,8 @@ export const {
|
||||
processingCanceled,
|
||||
errorOccurred,
|
||||
errorSeen,
|
||||
setModelList,
|
||||
setIsCancelable,
|
||||
} = systemSlice.actions;
|
||||
|
||||
export default systemSlice.reducer;
|
||||
|
@ -13,6 +13,7 @@
|
||||
@use '../features/system/SiteHeader.scss';
|
||||
@use '../features/system/StatusIndicator.scss';
|
||||
@use '../features/system/SettingsModal/SettingsModal.scss';
|
||||
@use '../features/system/SettingsModal/ModelList.scss';
|
||||
@use '../features/system/HotkeysModal/HotkeysModal.scss';
|
||||
@use '../features/system/Console.scss';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user