feat(ui): toast on queue item errors, improved error descriptions

Show error toasts on queue item error events instead of invocation error events. This allows errors that occurred outside node execution to be surfaced to the user.

The error description component is updated to show the new error message if available. Commercial handling is retained, but local now uses the same component to display the error message itself.
This commit is contained in:
psychedelicious
2024-05-24 19:03:35 +10:00
parent 50dd569411
commit f5a775ae4e
4 changed files with 86 additions and 75 deletions

View File

@ -3,44 +3,16 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import { deepClone } from 'common/util/deepClone';
import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useExecutionState';
import { zNodeStatus } from 'features/nodes/types/invocation';
import { toast } from 'features/toast/toast';
import ToastWithSessionRefDescription from 'features/toast/ToastWithSessionRefDescription';
import { t } from 'i18next';
import { startCase } from 'lodash-es';
import { socketInvocationError } from 'services/events/actions';
const log = logger('socketio');
const getTitle = (errorType: string) => {
if (errorType === 'OutOfMemoryError') {
return t('toast.outOfMemoryError');
}
return t('toast.serverError');
};
const getDescription = (errorType: string, sessionId: string, isLocal?: boolean) => {
if (!isLocal) {
if (errorType === 'OutOfMemoryError') {
return ToastWithSessionRefDescription({
message: t('toast.outOfMemoryDescription'),
sessionId,
});
}
return ToastWithSessionRefDescription({
message: errorType,
sessionId,
});
}
return errorType;
};
export const addInvocationErrorEventListener = (startAppListening: AppStartListening) => {
startAppListening({
actionCreator: socketInvocationError,
effect: (action, { getState }) => {
effect: (action) => {
log.error(action.payload, `Invocation error (${action.payload.data.node.type})`);
const { source_node_id, error_type, error_message, error_traceback, graph_execution_state_id } =
action.payload.data;
const { source_node_id, error_type, error_message, error_traceback } = action.payload.data;
const nes = deepClone($nodeExecutionStates.get()[source_node_id]);
if (nes) {
nes.status = zNodeStatus.enum.FAILED;
@ -53,19 +25,6 @@ export const addInvocationErrorEventListener = (startAppListening: AppStartListe
};
upsertExecutionState(nes.nodeId, nes);
}
const errorType = startCase(error_type);
const sessionId = graph_execution_state_id;
const { isLocal } = getState().config;
toast({
id: `INVOCATION_ERROR_${errorType}`,
title: getTitle(errorType),
status: 'error',
duration: null,
description: getDescription(errorType, sessionId, isLocal),
updateDescription: isLocal ? true : false,
});
},
});
};

View File

@ -3,6 +3,8 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import { deepClone } from 'common/util/deepClone';
import { $nodeExecutionStates } from 'features/nodes/hooks/useExecutionState';
import { zNodeStatus } from 'features/nodes/types/invocation';
import ErrorToastDescription, { getTitleFromErrorType } from 'features/toast/ErrorToastDescription';
import { toast } from 'features/toast/toast';
import { forEach } from 'lodash-es';
import { queueApi, queueItemsAdapter } from 'services/api/endpoints/queue';
import { socketQueueItemStatusChanged } from 'services/events/actions';
@ -12,7 +14,7 @@ const log = logger('socketio');
export const addSocketQueueItemStatusChangedEventListener = (startAppListening: AppStartListening) => {
startAppListening({
actionCreator: socketQueueItemStatusChanged,
effect: async (action, { dispatch }) => {
effect: async (action, { dispatch, getState }) => {
// we've got new status for the queue item, batch and queue
const { queue_item, batch_status, queue_status } = action.payload.data;
@ -54,7 +56,7 @@ export const addSocketQueueItemStatusChangedEventListener = (startAppListening:
])
);
if (['in_progress'].includes(action.payload.data.queue_item.status)) {
if (queue_item.status === 'in_progress') {
forEach($nodeExecutionStates.get(), (nes) => {
if (!nes) {
return;
@ -67,6 +69,26 @@ export const addSocketQueueItemStatusChangedEventListener = (startAppListening:
clone.outputs = [];
$nodeExecutionStates.setKey(clone.nodeId, clone);
});
} else if (queue_item.status === 'failed' && queue_item.error_type) {
const { error_type, error_message, session_id } = queue_item;
const isLocal = getState().config.isLocal ?? true;
const sessionId = session_id;
toast({
id: `INVOCATION_ERROR_${error_type}`,
title: getTitleFromErrorType(error_type),
status: 'error',
duration: null,
description: (
<ErrorToastDescription
errorType={error_type}
errorMessage={error_message}
sessionId={sessionId}
isLocal={false}
/>
),
updateDescription: isLocal ? true : false,
});
}
},
});