mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): improve error handling (#4699)
* feat(ui): add error handling for enqueueBatch route, remove sessions This re-implements the handling for the session create/invoke errors, but for batches. Also remove all references to the old sessions routes in the UI. * feat(ui): improve canvas image error UI * make canvas error state gray instead of red --------- Co-authored-by: Mary Hipp <maryhipp@Marys-MacBook-Air.local>
This commit is contained in:
parent
105a4234b0
commit
a4cdaa245e
@ -58,6 +58,7 @@
|
|||||||
"githubLabel": "Github",
|
"githubLabel": "Github",
|
||||||
"hotkeysLabel": "Hotkeys",
|
"hotkeysLabel": "Hotkeys",
|
||||||
"imagePrompt": "Image Prompt",
|
"imagePrompt": "Image Prompt",
|
||||||
|
"imageFailedToLoad": "Unable to Load Image",
|
||||||
"img2img": "Image To Image",
|
"img2img": "Image To Image",
|
||||||
"langArabic": "العربية",
|
"langArabic": "العربية",
|
||||||
"langBrPortuguese": "Português do Brasil",
|
"langBrPortuguese": "Português do Brasil",
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { ReactNode, memo, useEffect, useMemo } from 'react';
|
import { ReactNode, memo, useEffect, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { theme as invokeAITheme } from 'theme/theme';
|
import { TOAST_OPTIONS, theme as invokeAITheme } from 'theme/theme';
|
||||||
|
|
||||||
import '@fontsource-variable/inter';
|
import '@fontsource-variable/inter';
|
||||||
import { MantineProvider } from '@mantine/core';
|
import { MantineProvider } from '@mantine/core';
|
||||||
@ -39,7 +39,11 @@ function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MantineProvider theme={mantineTheme}>
|
<MantineProvider theme={mantineTheme}>
|
||||||
<ChakraProvider theme={theme} colorModeManager={manager}>
|
<ChakraProvider
|
||||||
|
theme={theme}
|
||||||
|
colorModeManager={manager}
|
||||||
|
toastOptions={TOAST_OPTIONS}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</ChakraProvider>
|
</ChakraProvider>
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
|
@ -54,21 +54,6 @@ import { addModelSelectedListener } from './listeners/modelSelected';
|
|||||||
import { addModelsLoadedListener } from './listeners/modelsLoaded';
|
import { addModelsLoadedListener } from './listeners/modelsLoaded';
|
||||||
import { addDynamicPromptsListener } from './listeners/promptChanged';
|
import { addDynamicPromptsListener } from './listeners/promptChanged';
|
||||||
import { addReceivedOpenAPISchemaListener } from './listeners/receivedOpenAPISchema';
|
import { addReceivedOpenAPISchemaListener } from './listeners/receivedOpenAPISchema';
|
||||||
import {
|
|
||||||
addSessionCanceledFulfilledListener,
|
|
||||||
addSessionCanceledPendingListener,
|
|
||||||
addSessionCanceledRejectedListener,
|
|
||||||
} from './listeners/sessionCanceled';
|
|
||||||
import {
|
|
||||||
addSessionCreatedFulfilledListener,
|
|
||||||
addSessionCreatedPendingListener,
|
|
||||||
addSessionCreatedRejectedListener,
|
|
||||||
} from './listeners/sessionCreated';
|
|
||||||
import {
|
|
||||||
addSessionInvokedFulfilledListener,
|
|
||||||
addSessionInvokedPendingListener,
|
|
||||||
addSessionInvokedRejectedListener,
|
|
||||||
} from './listeners/sessionInvoked';
|
|
||||||
import { addSocketConnectedEventListener as addSocketConnectedListener } from './listeners/socketio/socketConnected';
|
import { addSocketConnectedEventListener as addSocketConnectedListener } from './listeners/socketio/socketConnected';
|
||||||
import { addSocketDisconnectedEventListener as addSocketDisconnectedListener } from './listeners/socketio/socketDisconnected';
|
import { addSocketDisconnectedEventListener as addSocketDisconnectedListener } from './listeners/socketio/socketDisconnected';
|
||||||
import { addGeneratorProgressEventListener as addGeneratorProgressListener } from './listeners/socketio/socketGeneratorProgress';
|
import { addGeneratorProgressEventListener as addGeneratorProgressListener } from './listeners/socketio/socketGeneratorProgress';
|
||||||
@ -86,6 +71,7 @@ import { addStagingAreaImageSavedListener } from './listeners/stagingAreaImageSa
|
|||||||
import { addTabChangedListener } from './listeners/tabChanged';
|
import { addTabChangedListener } from './listeners/tabChanged';
|
||||||
import { addUpscaleRequestedListener } from './listeners/upscaleRequested';
|
import { addUpscaleRequestedListener } from './listeners/upscaleRequested';
|
||||||
import { addWorkflowLoadedListener } from './listeners/workflowLoaded';
|
import { addWorkflowLoadedListener } from './listeners/workflowLoaded';
|
||||||
|
import { addBatchEnqueuedListener } from './listeners/batchEnqueued';
|
||||||
|
|
||||||
export const listenerMiddleware = createListenerMiddleware();
|
export const listenerMiddleware = createListenerMiddleware();
|
||||||
|
|
||||||
@ -136,6 +122,7 @@ addEnqueueRequestedCanvasListener();
|
|||||||
addEnqueueRequestedNodes();
|
addEnqueueRequestedNodes();
|
||||||
addEnqueueRequestedLinear();
|
addEnqueueRequestedLinear();
|
||||||
addAnyEnqueuedListener();
|
addAnyEnqueuedListener();
|
||||||
|
addBatchEnqueuedListener();
|
||||||
|
|
||||||
// Canvas actions
|
// Canvas actions
|
||||||
addCanvasSavedToGalleryListener();
|
addCanvasSavedToGalleryListener();
|
||||||
@ -175,21 +162,6 @@ addSessionRetrievalErrorEventListener();
|
|||||||
addInvocationRetrievalErrorEventListener();
|
addInvocationRetrievalErrorEventListener();
|
||||||
addSocketQueueItemStatusChangedEventListener();
|
addSocketQueueItemStatusChangedEventListener();
|
||||||
|
|
||||||
// Session Created
|
|
||||||
addSessionCreatedPendingListener();
|
|
||||||
addSessionCreatedFulfilledListener();
|
|
||||||
addSessionCreatedRejectedListener();
|
|
||||||
|
|
||||||
// Session Invoked
|
|
||||||
addSessionInvokedPendingListener();
|
|
||||||
addSessionInvokedFulfilledListener();
|
|
||||||
addSessionInvokedRejectedListener();
|
|
||||||
|
|
||||||
// Session Canceled
|
|
||||||
addSessionCanceledPendingListener();
|
|
||||||
addSessionCanceledFulfilledListener();
|
|
||||||
addSessionCanceledRejectedListener();
|
|
||||||
|
|
||||||
// ControlNet
|
// ControlNet
|
||||||
addControlNetImageProcessedListener();
|
addControlNetImageProcessedListener();
|
||||||
addControlNetAutoProcessListener();
|
addControlNetAutoProcessListener();
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
import { createStandaloneToast } from '@chakra-ui/react';
|
||||||
|
import { logger } from 'app/logging/logger';
|
||||||
|
import { parseify } from 'common/util/serialize';
|
||||||
|
import { zPydanticValidationError } from 'features/system/store/zodSchemas';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { get, truncate, upperFirst } from 'lodash-es';
|
||||||
|
import { queueApi } from 'services/api/endpoints/queue';
|
||||||
|
import { TOAST_OPTIONS, theme } from 'theme/theme';
|
||||||
|
import { startAppListening } from '..';
|
||||||
|
|
||||||
|
const { toast } = createStandaloneToast({
|
||||||
|
theme: theme,
|
||||||
|
defaultOptions: TOAST_OPTIONS.defaultOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const addBatchEnqueuedListener = () => {
|
||||||
|
// success
|
||||||
|
startAppListening({
|
||||||
|
matcher: queueApi.endpoints.enqueueBatch.matchFulfilled,
|
||||||
|
effect: async (action) => {
|
||||||
|
const response = action.payload;
|
||||||
|
const arg = action.meta.arg.originalArgs;
|
||||||
|
logger('queue').debug(
|
||||||
|
{ enqueueResult: parseify(response) },
|
||||||
|
'Batch enqueued'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!toast.isActive('batch-queued')) {
|
||||||
|
toast({
|
||||||
|
id: 'batch-queued',
|
||||||
|
title: t('queue.batchQueued'),
|
||||||
|
description: t('queue.batchQueuedDesc', {
|
||||||
|
item_count: response.enqueued,
|
||||||
|
direction: arg.prepend ? t('queue.front') : t('queue.back'),
|
||||||
|
}),
|
||||||
|
duration: 1000,
|
||||||
|
status: 'success',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// error
|
||||||
|
startAppListening({
|
||||||
|
matcher: queueApi.endpoints.enqueueBatch.matchRejected,
|
||||||
|
effect: async (action) => {
|
||||||
|
const response = action.payload;
|
||||||
|
const arg = action.meta.arg.originalArgs;
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
toast({
|
||||||
|
title: t('queue.batchFailedToQueue'),
|
||||||
|
status: 'error',
|
||||||
|
description: 'Unknown Error',
|
||||||
|
});
|
||||||
|
logger('queue').error(
|
||||||
|
{ batchConfig: parseify(arg), error: parseify(response) },
|
||||||
|
t('queue.batchFailedToQueue')
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = zPydanticValidationError.safeParse(response);
|
||||||
|
if (result.success) {
|
||||||
|
result.data.data.detail.map((e) => {
|
||||||
|
toast({
|
||||||
|
id: 'batch-failed-to-queue',
|
||||||
|
title: truncate(upperFirst(e.msg), { length: 128 }),
|
||||||
|
status: 'error',
|
||||||
|
description: truncate(
|
||||||
|
`Path:
|
||||||
|
${e.loc.join('.')}`,
|
||||||
|
{ length: 128 }
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let detail = 'Unknown Error';
|
||||||
|
if (response.status === 403 && 'body' in response) {
|
||||||
|
detail = get(response, 'body.detail', 'Unknown Error');
|
||||||
|
} else if (response.status === 403 && 'error' in response) {
|
||||||
|
detail = get(response, 'error.detail', 'Unknown Error');
|
||||||
|
}
|
||||||
|
toast({
|
||||||
|
title: t('queue.batchFailedToQueue'),
|
||||||
|
status: 'error',
|
||||||
|
description: detail,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
logger('queue').error(
|
||||||
|
{ batchConfig: parseify(arg), error: parseify(response) },
|
||||||
|
t('queue.batchFailedToQueue')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
@ -12,8 +12,6 @@ import { getCanvasGenerationMode } from 'features/canvas/util/getCanvasGeneratio
|
|||||||
import { canvasGraphBuilt } from 'features/nodes/store/actions';
|
import { canvasGraphBuilt } from 'features/nodes/store/actions';
|
||||||
import { buildCanvasGraph } from 'features/nodes/util/graphBuilders/buildCanvasGraph';
|
import { buildCanvasGraph } from 'features/nodes/util/graphBuilders/buildCanvasGraph';
|
||||||
import { prepareLinearUIBatch } from 'features/nodes/util/graphBuilders/buildLinearBatchConfig';
|
import { prepareLinearUIBatch } from 'features/nodes/util/graphBuilders/buildLinearBatchConfig';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
|
||||||
import { t } from 'i18next';
|
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
import { queueApi } from 'services/api/endpoints/queue';
|
import { queueApi } from 'services/api/endpoints/queue';
|
||||||
import { ImageDTO } from 'services/api/types';
|
import { ImageDTO } from 'services/api/types';
|
||||||
@ -140,8 +138,6 @@ export const addEnqueueRequestedCanvasListener = () => {
|
|||||||
const enqueueResult = await req.unwrap();
|
const enqueueResult = await req.unwrap();
|
||||||
req.reset();
|
req.reset();
|
||||||
|
|
||||||
log.debug({ enqueueResult: parseify(enqueueResult) }, 'Batch enqueued');
|
|
||||||
|
|
||||||
const batchId = enqueueResult.batch.batch_id as string; // we know the is a string, backend provides it
|
const batchId = enqueueResult.batch.batch_id as string; // we know the is a string, backend provides it
|
||||||
|
|
||||||
// Prep the canvas staging area if it is not yet initialized
|
// Prep the canvas staging area if it is not yet initialized
|
||||||
@ -158,28 +154,8 @@ export const addEnqueueRequestedCanvasListener = () => {
|
|||||||
|
|
||||||
// Associate the session with the canvas session ID
|
// Associate the session with the canvas session ID
|
||||||
dispatch(canvasBatchIdAdded(batchId));
|
dispatch(canvasBatchIdAdded(batchId));
|
||||||
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
title: t('queue.batchQueued'),
|
|
||||||
description: t('queue.batchQueuedDesc', {
|
|
||||||
item_count: enqueueResult.enqueued,
|
|
||||||
direction: prepend ? t('queue.front') : t('queue.back'),
|
|
||||||
}),
|
|
||||||
status: 'success',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch {
|
} catch {
|
||||||
log.error(
|
// no-op
|
||||||
{ batchConfig: parseify(batchConfig) },
|
|
||||||
t('queue.batchFailedToQueue')
|
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
title: t('queue.batchFailedToQueue'),
|
|
||||||
status: 'error',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
|
||||||
import { enqueueRequested } from 'app/store/actions';
|
import { enqueueRequested } from 'app/store/actions';
|
||||||
import { parseify } from 'common/util/serialize';
|
|
||||||
import { prepareLinearUIBatch } from 'features/nodes/util/graphBuilders/buildLinearBatchConfig';
|
import { prepareLinearUIBatch } from 'features/nodes/util/graphBuilders/buildLinearBatchConfig';
|
||||||
import { buildLinearImageToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearImageToImageGraph';
|
import { buildLinearImageToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearImageToImageGraph';
|
||||||
import { buildLinearSDXLImageToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearSDXLImageToImageGraph';
|
import { buildLinearSDXLImageToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearSDXLImageToImageGraph';
|
||||||
import { buildLinearSDXLTextToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearSDXLTextToImageGraph';
|
import { buildLinearSDXLTextToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearSDXLTextToImageGraph';
|
||||||
import { buildLinearTextToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearTextToImageGraph';
|
import { buildLinearTextToImageGraph } from 'features/nodes/util/graphBuilders/buildLinearTextToImageGraph';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
|
||||||
import { t } from 'i18next';
|
|
||||||
import { queueApi } from 'services/api/endpoints/queue';
|
import { queueApi } from 'services/api/endpoints/queue';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
|
|
||||||
@ -18,7 +14,6 @@ export const addEnqueueRequestedLinear = () => {
|
|||||||
(action.payload.tabName === 'txt2img' ||
|
(action.payload.tabName === 'txt2img' ||
|
||||||
action.payload.tabName === 'img2img'),
|
action.payload.tabName === 'img2img'),
|
||||||
effect: async (action, { getState, dispatch }) => {
|
effect: async (action, { getState, dispatch }) => {
|
||||||
const log = logger('queue');
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const model = state.generation.model;
|
const model = state.generation.model;
|
||||||
const { prepend } = action.payload;
|
const { prepend } = action.payload;
|
||||||
@ -41,38 +36,12 @@ export const addEnqueueRequestedLinear = () => {
|
|||||||
|
|
||||||
const batchConfig = prepareLinearUIBatch(state, graph, prepend);
|
const batchConfig = prepareLinearUIBatch(state, graph, prepend);
|
||||||
|
|
||||||
try {
|
const req = dispatch(
|
||||||
const req = dispatch(
|
queueApi.endpoints.enqueueBatch.initiate(batchConfig, {
|
||||||
queueApi.endpoints.enqueueBatch.initiate(batchConfig, {
|
fixedCacheKey: 'enqueueBatch',
|
||||||
fixedCacheKey: 'enqueueBatch',
|
})
|
||||||
})
|
);
|
||||||
);
|
req.reset();
|
||||||
const enqueueResult = await req.unwrap();
|
|
||||||
req.reset();
|
|
||||||
|
|
||||||
log.debug({ enqueueResult: parseify(enqueueResult) }, 'Batch enqueued');
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
title: t('queue.batchQueued'),
|
|
||||||
description: t('queue.batchQueuedDesc', {
|
|
||||||
item_count: enqueueResult.enqueued,
|
|
||||||
direction: prepend ? t('queue.front') : t('queue.back'),
|
|
||||||
}),
|
|
||||||
status: 'success',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
log.error(
|
|
||||||
{ batchConfig: parseify(batchConfig) },
|
|
||||||
t('queue.batchFailedToQueue')
|
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
title: t('queue.batchFailedToQueue'),
|
|
||||||
status: 'error',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
|
||||||
import { enqueueRequested } from 'app/store/actions';
|
import { enqueueRequested } from 'app/store/actions';
|
||||||
import { parseify } from 'common/util/serialize';
|
|
||||||
import { buildNodesGraph } from 'features/nodes/util/graphBuilders/buildNodesGraph';
|
import { buildNodesGraph } from 'features/nodes/util/graphBuilders/buildNodesGraph';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
|
||||||
import { t } from 'i18next';
|
|
||||||
import { queueApi } from 'services/api/endpoints/queue';
|
import { queueApi } from 'services/api/endpoints/queue';
|
||||||
import { BatchConfig } from 'services/api/types';
|
import { BatchConfig } from 'services/api/types';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
@ -13,9 +9,7 @@ export const addEnqueueRequestedNodes = () => {
|
|||||||
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
||||||
enqueueRequested.match(action) && action.payload.tabName === 'nodes',
|
enqueueRequested.match(action) && action.payload.tabName === 'nodes',
|
||||||
effect: async (action, { getState, dispatch }) => {
|
effect: async (action, { getState, dispatch }) => {
|
||||||
const log = logger('queue');
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { prepend } = action.payload;
|
|
||||||
const graph = buildNodesGraph(state.nodes);
|
const graph = buildNodesGraph(state.nodes);
|
||||||
const batchConfig: BatchConfig = {
|
const batchConfig: BatchConfig = {
|
||||||
batch: {
|
batch: {
|
||||||
@ -25,38 +19,12 @@ export const addEnqueueRequestedNodes = () => {
|
|||||||
prepend: action.payload.prepend,
|
prepend: action.payload.prepend,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
const req = dispatch(
|
||||||
const req = dispatch(
|
queueApi.endpoints.enqueueBatch.initiate(batchConfig, {
|
||||||
queueApi.endpoints.enqueueBatch.initiate(batchConfig, {
|
fixedCacheKey: 'enqueueBatch',
|
||||||
fixedCacheKey: 'enqueueBatch',
|
})
|
||||||
})
|
);
|
||||||
);
|
req.reset();
|
||||||
const enqueueResult = await req.unwrap();
|
|
||||||
req.reset();
|
|
||||||
|
|
||||||
log.debug({ enqueueResult: parseify(enqueueResult) }, 'Batch enqueued');
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
title: t('queue.batchQueued'),
|
|
||||||
description: t('queue.batchQueuedDesc', {
|
|
||||||
item_count: enqueueResult.enqueued,
|
|
||||||
direction: prepend ? t('queue.front') : t('queue.back'),
|
|
||||||
}),
|
|
||||||
status: 'success',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
log.error(
|
|
||||||
{ batchConfig: parseify(batchConfig) },
|
|
||||||
'Failed to enqueue batch'
|
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
title: t('queue.batchFailedToQueue'),
|
|
||||||
status: 'error',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
|
||||||
import { serializeError } from 'serialize-error';
|
|
||||||
import { sessionCanceled } from 'services/api/thunks/session';
|
|
||||||
import { startAppListening } from '..';
|
|
||||||
|
|
||||||
export const addSessionCanceledPendingListener = () => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: sessionCanceled.pending,
|
|
||||||
effect: () => {
|
|
||||||
//
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addSessionCanceledFulfilledListener = () => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: sessionCanceled.fulfilled,
|
|
||||||
effect: (action) => {
|
|
||||||
const log = logger('session');
|
|
||||||
const { session_id } = action.meta.arg;
|
|
||||||
log.debug({ session_id }, `Session canceled (${session_id})`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addSessionCanceledRejectedListener = () => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: sessionCanceled.rejected,
|
|
||||||
effect: (action) => {
|
|
||||||
const log = logger('session');
|
|
||||||
const { session_id } = action.meta.arg;
|
|
||||||
if (action.payload) {
|
|
||||||
const { error } = action.payload;
|
|
||||||
log.error(
|
|
||||||
{
|
|
||||||
session_id,
|
|
||||||
error: serializeError(error),
|
|
||||||
},
|
|
||||||
`Problem canceling session`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,45 +0,0 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
|
||||||
import { parseify } from 'common/util/serialize';
|
|
||||||
import { serializeError } from 'serialize-error';
|
|
||||||
import { sessionCreated } from 'services/api/thunks/session';
|
|
||||||
import { startAppListening } from '..';
|
|
||||||
|
|
||||||
export const addSessionCreatedPendingListener = () => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: sessionCreated.pending,
|
|
||||||
effect: () => {
|
|
||||||
//
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addSessionCreatedFulfilledListener = () => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: sessionCreated.fulfilled,
|
|
||||||
effect: (action) => {
|
|
||||||
const log = logger('session');
|
|
||||||
const session = action.payload;
|
|
||||||
log.debug(
|
|
||||||
{ session: parseify(session) },
|
|
||||||
`Session created (${session.id})`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addSessionCreatedRejectedListener = () => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: sessionCreated.rejected,
|
|
||||||
effect: (action) => {
|
|
||||||
const log = logger('session');
|
|
||||||
if (action.payload) {
|
|
||||||
const { error, status } = action.payload;
|
|
||||||
const graph = parseify(action.meta.arg);
|
|
||||||
log.error(
|
|
||||||
{ graph, status, error: serializeError(error) },
|
|
||||||
`Problem creating session`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,44 +0,0 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
|
||||||
import { serializeError } from 'serialize-error';
|
|
||||||
import { sessionInvoked } from 'services/api/thunks/session';
|
|
||||||
import { startAppListening } from '..';
|
|
||||||
|
|
||||||
export const addSessionInvokedPendingListener = () => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: sessionInvoked.pending,
|
|
||||||
effect: () => {
|
|
||||||
//
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addSessionInvokedFulfilledListener = () => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: sessionInvoked.fulfilled,
|
|
||||||
effect: (action) => {
|
|
||||||
const log = logger('session');
|
|
||||||
const { session_id } = action.meta.arg;
|
|
||||||
log.debug({ session_id }, `Session invoked (${session_id})`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addSessionInvokedRejectedListener = () => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: sessionInvoked.rejected,
|
|
||||||
effect: (action) => {
|
|
||||||
const log = logger('session');
|
|
||||||
const { session_id } = action.meta.arg;
|
|
||||||
if (action.payload) {
|
|
||||||
const { error } = action.payload;
|
|
||||||
log.error(
|
|
||||||
{
|
|
||||||
session_id,
|
|
||||||
error: serializeError(error),
|
|
||||||
},
|
|
||||||
`Problem invoking session`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,54 +0,0 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
|
||||||
import { AppThunkDispatch } from 'app/store/store';
|
|
||||||
import { parseify } from 'common/util/serialize';
|
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
|
||||||
import { t } from 'i18next';
|
|
||||||
import { queueApi } from 'services/api/endpoints/queue';
|
|
||||||
import { BatchConfig } from 'services/api/types';
|
|
||||||
|
|
||||||
export const enqueueBatch = async (
|
|
||||||
batchConfig: BatchConfig,
|
|
||||||
dispatch: AppThunkDispatch
|
|
||||||
) => {
|
|
||||||
const log = logger('session');
|
|
||||||
const { prepend } = batchConfig;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const req = dispatch(
|
|
||||||
queueApi.endpoints.enqueueBatch.initiate(batchConfig, {
|
|
||||||
fixedCacheKey: 'enqueueBatch',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const enqueueResult = await req.unwrap();
|
|
||||||
req.reset();
|
|
||||||
|
|
||||||
dispatch(
|
|
||||||
queueApi.endpoints.resumeProcessor.initiate(undefined, {
|
|
||||||
fixedCacheKey: 'resumeProcessor',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
log.debug({ enqueueResult: parseify(enqueueResult) }, 'Batch enqueued');
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
title: t('queue.batchQueued'),
|
|
||||||
description: t('queue.batchQueuedDesc', {
|
|
||||||
item_count: enqueueResult.enqueued,
|
|
||||||
direction: prepend ? t('queue.front') : t('queue.back'),
|
|
||||||
}),
|
|
||||||
status: 'success',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
log.error(
|
|
||||||
{ batchConfig: parseify(batchConfig) },
|
|
||||||
t('queue.batchFailedToQueue')
|
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
title: t('queue.batchFailedToQueue'),
|
|
||||||
status: 'error',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,26 +1,27 @@
|
|||||||
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
||||||
import { Image, Rect } from 'react-konva';
|
import { memo } from 'react';
|
||||||
|
import { Image } from 'react-konva';
|
||||||
|
import { $authToken } from 'services/api/client';
|
||||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||||
import useImage from 'use-image';
|
import useImage from 'use-image';
|
||||||
import { CanvasImage } from '../store/canvasTypes';
|
import { CanvasImage } from '../store/canvasTypes';
|
||||||
import { $authToken } from 'services/api/client';
|
import IAICanvasImageErrorFallback from './IAICanvasImageErrorFallback';
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
type IAICanvasImageProps = {
|
type IAICanvasImageProps = {
|
||||||
canvasImage: CanvasImage;
|
canvasImage: CanvasImage;
|
||||||
};
|
};
|
||||||
const IAICanvasImage = (props: IAICanvasImageProps) => {
|
const IAICanvasImage = (props: IAICanvasImageProps) => {
|
||||||
const { width, height, x, y, imageName } = props.canvasImage;
|
const { x, y, imageName } = props.canvasImage;
|
||||||
const { currentData: imageDTO, isError } = useGetImageDTOQuery(
|
const { currentData: imageDTO, isError } = useGetImageDTOQuery(
|
||||||
imageName ?? skipToken
|
imageName ?? skipToken
|
||||||
);
|
);
|
||||||
const [image] = useImage(
|
const [image, status] = useImage(
|
||||||
imageDTO?.image_url ?? '',
|
imageDTO?.image_url ?? '',
|
||||||
$authToken.get() ? 'use-credentials' : 'anonymous'
|
$authToken.get() ? 'use-credentials' : 'anonymous'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isError) {
|
if (isError || status === 'failed') {
|
||||||
return <Rect x={x} y={y} width={width} height={height} fill="red" />;
|
return <IAICanvasImageErrorFallback canvasImage={props.canvasImage} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Image x={x} y={y} image={image} listening={false} />;
|
return <Image x={x} y={y} image={image} listening={false} />;
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
import { useColorModeValue, useToken } from '@chakra-ui/react';
|
||||||
|
import { memo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Group, Rect, Text } from 'react-konva';
|
||||||
|
import { CanvasImage } from '../store/canvasTypes';
|
||||||
|
|
||||||
|
type IAICanvasImageErrorFallbackProps = {
|
||||||
|
canvasImage: CanvasImage;
|
||||||
|
};
|
||||||
|
const IAICanvasImageErrorFallback = ({
|
||||||
|
canvasImage,
|
||||||
|
}: IAICanvasImageErrorFallbackProps) => {
|
||||||
|
const [errorColorLight, errorColorDark, fontColorLight, fontColorDark] =
|
||||||
|
useToken('colors', ['gray.400', 'gray.500', 'base.700', 'base.900']);
|
||||||
|
const errorColor = useColorModeValue(errorColorLight, errorColorDark);
|
||||||
|
const fontColor = useColorModeValue(fontColorLight, fontColorDark);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<Group>
|
||||||
|
<Rect
|
||||||
|
x={canvasImage.x}
|
||||||
|
y={canvasImage.y}
|
||||||
|
width={canvasImage.width}
|
||||||
|
height={canvasImage.height}
|
||||||
|
fill={errorColor}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
x={canvasImage.x}
|
||||||
|
y={canvasImage.y}
|
||||||
|
width={canvasImage.width}
|
||||||
|
height={canvasImage.height}
|
||||||
|
align="center"
|
||||||
|
verticalAlign="middle"
|
||||||
|
fontFamily='"Inter Variable", sans-serif'
|
||||||
|
fontSize={canvasImage.width / 16}
|
||||||
|
fontStyle="600"
|
||||||
|
text={t('common.imageFailedToLoad')}
|
||||||
|
fill={fontColor}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(IAICanvasImageErrorFallback);
|
@ -8,7 +8,6 @@ import { setAspectRatio } from 'features/parameters/store/generationSlice';
|
|||||||
import { IRect, Vector2d } from 'konva/lib/types';
|
import { IRect, Vector2d } from 'konva/lib/types';
|
||||||
import { clamp, cloneDeep } from 'lodash-es';
|
import { clamp, cloneDeep } from 'lodash-es';
|
||||||
import { RgbaColor } from 'react-colorful';
|
import { RgbaColor } from 'react-colorful';
|
||||||
import { sessionCanceled } from 'services/api/thunks/session';
|
|
||||||
import { ImageDTO } from 'services/api/types';
|
import { ImageDTO } from 'services/api/types';
|
||||||
import calculateCoordinates from '../util/calculateCoordinates';
|
import calculateCoordinates from '../util/calculateCoordinates';
|
||||||
import calculateScale from '../util/calculateScale';
|
import calculateScale from '../util/calculateScale';
|
||||||
@ -786,11 +785,6 @@ export const canvasSlice = createSlice({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder.addCase(sessionCanceled.pending, (state) => {
|
|
||||||
if (!state.layerState.stagingArea.images.length) {
|
|
||||||
state.layerState.stagingArea = initialLayerState.stagingArea;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.addCase(setAspectRatio, (state, action) => {
|
builder.addCase(setAspectRatio, (state, action) => {
|
||||||
const ratio = action.payload;
|
const ratio = action.payload;
|
||||||
if (ratio) {
|
if (ratio) {
|
||||||
|
@ -6,7 +6,6 @@ import {
|
|||||||
import { cloneDeep, forEach } from 'lodash-es';
|
import { cloneDeep, forEach } from 'lodash-es';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
import { components } from 'services/api/schema';
|
import { components } from 'services/api/schema';
|
||||||
import { isAnySessionRejected } from 'services/api/thunks/session';
|
|
||||||
import { ImageDTO } from 'services/api/types';
|
import { ImageDTO } from 'services/api/types';
|
||||||
import { appSocketInvocationError } from 'services/events/actions';
|
import { appSocketInvocationError } from 'services/events/actions';
|
||||||
import { controlNetImageProcessed } from './actions';
|
import { controlNetImageProcessed } from './actions';
|
||||||
@ -418,10 +417,6 @@ export const controlNetSlice = createSlice({
|
|||||||
state.pendingControlImages = [];
|
state.pendingControlImages = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.addMatcher(isAnySessionRejected, (state) => {
|
|
||||||
state.pendingControlImages = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.addMatcher(
|
builder.addMatcher(
|
||||||
imagesApi.endpoints.deleteImage.matchFulfilled,
|
imagesApi.endpoints.deleteImage.matchFulfilled,
|
||||||
(state, action) => {
|
(state, action) => {
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { UseToastOptions } from '@chakra-ui/react';
|
import { UseToastOptions } from '@chakra-ui/react';
|
||||||
import { PayloadAction, createSlice, isAnyOf } from '@reduxjs/toolkit';
|
import { PayloadAction, createSlice, isAnyOf } from '@reduxjs/toolkit';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { get, startCase, truncate, upperFirst } from 'lodash-es';
|
import { startCase } from 'lodash-es';
|
||||||
import { LogLevelName } from 'roarr';
|
import { LogLevelName } from 'roarr';
|
||||||
import { isAnySessionRejected } from 'services/api/thunks/session';
|
|
||||||
import {
|
import {
|
||||||
appSocketConnected,
|
appSocketConnected,
|
||||||
appSocketDisconnected,
|
appSocketDisconnected,
|
||||||
@ -20,8 +19,7 @@ import {
|
|||||||
} from 'services/events/actions';
|
} from 'services/events/actions';
|
||||||
import { calculateStepPercentage } from '../util/calculateStepPercentage';
|
import { calculateStepPercentage } from '../util/calculateStepPercentage';
|
||||||
import { makeToast } from '../util/makeToast';
|
import { makeToast } from '../util/makeToast';
|
||||||
import { SystemState, LANGUAGES } from './types';
|
import { LANGUAGES, SystemState } from './types';
|
||||||
import { zPydanticValidationError } from './zodSchemas';
|
|
||||||
|
|
||||||
export const initialSystemState: SystemState = {
|
export const initialSystemState: SystemState = {
|
||||||
isInitialized: false,
|
isInitialized: false,
|
||||||
@ -175,50 +173,6 @@ export const systemSlice = createSlice({
|
|||||||
|
|
||||||
// *** Matchers - must be after all cases ***
|
// *** Matchers - must be after all cases ***
|
||||||
|
|
||||||
/**
|
|
||||||
* Session Invoked - REJECTED
|
|
||||||
* Session Created - REJECTED
|
|
||||||
*/
|
|
||||||
builder.addMatcher(isAnySessionRejected, (state, action) => {
|
|
||||||
let errorDescription = undefined;
|
|
||||||
const duration = 5000;
|
|
||||||
|
|
||||||
if (action.payload?.status === 422) {
|
|
||||||
const result = zPydanticValidationError.safeParse(action.payload);
|
|
||||||
if (result.success) {
|
|
||||||
result.data.error.detail.map((e) => {
|
|
||||||
state.toastQueue.push(
|
|
||||||
makeToast({
|
|
||||||
title: truncate(upperFirst(e.msg), { length: 128 }),
|
|
||||||
status: 'error',
|
|
||||||
description: truncate(
|
|
||||||
`Path:
|
|
||||||
${e.loc.join('.')}`,
|
|
||||||
{ length: 128 }
|
|
||||||
),
|
|
||||||
duration,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (action.payload?.error) {
|
|
||||||
errorDescription = action.payload?.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.toastQueue.push(
|
|
||||||
makeToast({
|
|
||||||
title: t('toast.serverError'),
|
|
||||||
status: 'error',
|
|
||||||
description: truncate(
|
|
||||||
get(errorDescription, 'detail', 'Unknown Error'),
|
|
||||||
{ length: 128 }
|
|
||||||
),
|
|
||||||
duration,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any server error
|
* Any server error
|
||||||
*/
|
*/
|
||||||
|
@ -2,7 +2,7 @@ import { z } from 'zod';
|
|||||||
|
|
||||||
export const zPydanticValidationError = z.object({
|
export const zPydanticValidationError = z.object({
|
||||||
status: z.literal(422),
|
status: z.literal(422),
|
||||||
error: z.object({
|
data: z.object({
|
||||||
detail: z.array(
|
detail: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
loc: z.array(z.string()),
|
loc: z.array(z.string()),
|
||||||
|
@ -1,184 +0,0 @@
|
|||||||
import { createAsyncThunk, isAnyOf } from '@reduxjs/toolkit';
|
|
||||||
import { $queueId } from 'features/queue/store/queueNanoStore';
|
|
||||||
import { isObject } from 'lodash-es';
|
|
||||||
import { $client } from 'services/api/client';
|
|
||||||
import { paths } from 'services/api/schema';
|
|
||||||
import { O } from 'ts-toolbelt';
|
|
||||||
|
|
||||||
type CreateSessionArg = {
|
|
||||||
graph: NonNullable<
|
|
||||||
paths['/api/v1/sessions/']['post']['requestBody']
|
|
||||||
>['content']['application/json'];
|
|
||||||
};
|
|
||||||
|
|
||||||
type CreateSessionResponse = O.Required<
|
|
||||||
NonNullable<
|
|
||||||
paths['/api/v1/sessions/']['post']['requestBody']
|
|
||||||
>['content']['application/json'],
|
|
||||||
'id'
|
|
||||||
>;
|
|
||||||
|
|
||||||
type CreateSessionThunkConfig = {
|
|
||||||
rejectValue: { arg: CreateSessionArg; status: number; error: unknown };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* `SessionsService.createSession()` thunk
|
|
||||||
*/
|
|
||||||
export const sessionCreated = createAsyncThunk<
|
|
||||||
CreateSessionResponse,
|
|
||||||
CreateSessionArg,
|
|
||||||
CreateSessionThunkConfig
|
|
||||||
>('api/sessionCreated', async (arg, { rejectWithValue }) => {
|
|
||||||
const { graph } = arg;
|
|
||||||
const { POST } = $client.get();
|
|
||||||
const { data, error, response } = await POST('/api/v1/sessions/', {
|
|
||||||
body: graph,
|
|
||||||
params: { query: { queue_id: $queueId.get() } },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return rejectWithValue({ arg, status: response.status, error });
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
|
|
||||||
type InvokedSessionArg = {
|
|
||||||
session_id: paths['/api/v1/sessions/{session_id}/invoke']['put']['parameters']['path']['session_id'];
|
|
||||||
};
|
|
||||||
|
|
||||||
type InvokedSessionResponse =
|
|
||||||
paths['/api/v1/sessions/{session_id}/invoke']['put']['responses']['200']['content']['application/json'];
|
|
||||||
|
|
||||||
type InvokedSessionThunkConfig = {
|
|
||||||
rejectValue: {
|
|
||||||
arg: InvokedSessionArg;
|
|
||||||
error: unknown;
|
|
||||||
status: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const isErrorWithStatus = (error: unknown): error is { status: number } =>
|
|
||||||
isObject(error) && 'status' in error;
|
|
||||||
|
|
||||||
const isErrorWithDetail = (error: unknown): error is { detail: string } =>
|
|
||||||
isObject(error) && 'detail' in error;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* `SessionsService.invokeSession()` thunk
|
|
||||||
*/
|
|
||||||
export const sessionInvoked = createAsyncThunk<
|
|
||||||
InvokedSessionResponse,
|
|
||||||
InvokedSessionArg,
|
|
||||||
InvokedSessionThunkConfig
|
|
||||||
>('api/sessionInvoked', async (arg, { rejectWithValue }) => {
|
|
||||||
const { session_id } = arg;
|
|
||||||
const { PUT } = $client.get();
|
|
||||||
const { error, response } = await PUT(
|
|
||||||
'/api/v1/sessions/{session_id}/invoke',
|
|
||||||
{
|
|
||||||
params: {
|
|
||||||
query: { queue_id: $queueId.get(), all: true },
|
|
||||||
path: { session_id },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
if (isErrorWithStatus(error) && error.status === 403) {
|
|
||||||
return rejectWithValue({
|
|
||||||
arg,
|
|
||||||
status: response.status,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
error: (error as any).body.detail,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (isErrorWithDetail(error) && response.status === 403) {
|
|
||||||
return rejectWithValue({
|
|
||||||
arg,
|
|
||||||
status: response.status,
|
|
||||||
error: error.detail,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
return rejectWithValue({ arg, status: response.status, error });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
type CancelSessionArg =
|
|
||||||
paths['/api/v1/sessions/{session_id}/invoke']['delete']['parameters']['path'];
|
|
||||||
|
|
||||||
type CancelSessionResponse =
|
|
||||||
paths['/api/v1/sessions/{session_id}/invoke']['delete']['responses']['200']['content']['application/json'];
|
|
||||||
|
|
||||||
type CancelSessionThunkConfig = {
|
|
||||||
rejectValue: {
|
|
||||||
arg: CancelSessionArg;
|
|
||||||
error: unknown;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* `SessionsService.cancelSession()` thunk
|
|
||||||
*/
|
|
||||||
export const sessionCanceled = createAsyncThunk<
|
|
||||||
CancelSessionResponse,
|
|
||||||
CancelSessionArg,
|
|
||||||
CancelSessionThunkConfig
|
|
||||||
>('api/sessionCanceled', async (arg, { rejectWithValue }) => {
|
|
||||||
const { session_id } = arg;
|
|
||||||
const { DELETE } = $client.get();
|
|
||||||
const { data, error } = await DELETE('/api/v1/sessions/{session_id}/invoke', {
|
|
||||||
params: {
|
|
||||||
path: { session_id },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return rejectWithValue({ arg, error });
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
|
|
||||||
type ListSessionsArg = {
|
|
||||||
params: paths['/api/v1/sessions/']['get']['parameters'];
|
|
||||||
};
|
|
||||||
|
|
||||||
type ListSessionsResponse =
|
|
||||||
paths['/api/v1/sessions/']['get']['responses']['200']['content']['application/json'];
|
|
||||||
|
|
||||||
type ListSessionsThunkConfig = {
|
|
||||||
rejectValue: {
|
|
||||||
arg: ListSessionsArg;
|
|
||||||
error: unknown;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* `SessionsService.listSessions()` thunk
|
|
||||||
*/
|
|
||||||
export const listedSessions = createAsyncThunk<
|
|
||||||
ListSessionsResponse,
|
|
||||||
ListSessionsArg,
|
|
||||||
ListSessionsThunkConfig
|
|
||||||
>('api/listSessions', async (arg, { rejectWithValue }) => {
|
|
||||||
const { params } = arg;
|
|
||||||
const { GET } = $client.get();
|
|
||||||
const { data, error } = await GET('/api/v1/sessions/', {
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return rejectWithValue({ arg, error });
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
|
|
||||||
export const isAnySessionRejected = isAnyOf(
|
|
||||||
sessionCreated.rejected,
|
|
||||||
sessionInvoked.rejected
|
|
||||||
);
|
|
@ -1,5 +1,4 @@
|
|||||||
import { ThemeOverride } from '@chakra-ui/react';
|
import { ThemeOverride, ToastProviderProps } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { InvokeAIColors } from './colors/colors';
|
import { InvokeAIColors } from './colors/colors';
|
||||||
import { accordionTheme } from './components/accordion';
|
import { accordionTheme } from './components/accordion';
|
||||||
import { buttonTheme } from './components/button';
|
import { buttonTheme } from './components/button';
|
||||||
@ -149,3 +148,7 @@ export const theme: ThemeOverride = {
|
|||||||
Tooltip: tooltipTheme,
|
Tooltip: tooltipTheme,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const TOAST_OPTIONS: ToastProviderProps = {
|
||||||
|
defaultOptions: { isClosable: true },
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user