From 627750eded45a81d2d2b841a641a009c8022b365 Mon Sep 17 00:00:00 2001 From: Martin Kristiansen Date: Sat, 16 Sep 2023 11:47:05 -0400 Subject: [PATCH 1/6] Adding excludes to flake8 config --- pyproject.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 63ac3b7c12..2ea5455c3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -198,6 +198,13 @@ output = "coverage/index.xml" max-line-length = 120 ignore = ["E203", "E266", "E501", "W503"] select = ["B", "C", "E", "F", "W", "T4"] +exclude = [ + ".git", + "__pycache__", + "build", + "dist", + "invokeai/frontend/web/node_modules/" +] [tool.black] line-length = 120 From 5a961bb58e695f306a95b2ea2ce300dfeae5e46e Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Fri, 15 Sep 2023 16:52:30 -0400 Subject: [PATCH 2/6] first pass to recall LoRAs --- .../ImageMetadataActions.tsx | 79 ++++++------------- .../web/src/features/lora/store/loraSlice.ts | 8 ++ .../web/src/features/nodes/types/types.ts | 16 ++-- .../parameters/hooks/useRecallParameters.ts | 52 +++++++++++- 4 files changed, 90 insertions(+), 65 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx index c1124477e2..a0ea69157c 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx @@ -1,8 +1,9 @@ -import { CoreMetadata } from 'features/nodes/types/types'; +import { CoreMetadata, LoRAMetadataType } from 'features/nodes/types/types'; import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters'; import { memo, useCallback } from 'react'; import ImageMetadataItem from './ImageMetadataItem'; import { useTranslation } from 'react-i18next'; +import { isValidLoRAModel } from '../../../parameters/types/parameterSchemas'; type Props = { metadata?: CoreMetadata; @@ -24,6 +25,7 @@ const ImageMetadataActions = (props: Props) => { recallWidth, recallHeight, recallStrength, + recallLoRA, } = useRecallParameters(); const handleRecallPositivePrompt = useCallback(() => { @@ -66,6 +68,13 @@ const ImageMetadataActions = (props: Props) => { recallStrength(metadata?.strength); }, [metadata?.strength, recallStrength]); + const handleRecallLoRA = useCallback( + (lora: LoRAMetadataType) => { + recallLoRA(lora); + }, + [recallLoRA] + ); + if (!metadata || Object.keys(metadata).length === 0) { return null; } @@ -130,20 +139,6 @@ const ImageMetadataActions = (props: Props) => { onClick={handleRecallHeight} /> )} - {/* {metadata.threshold !== undefined && ( - dispatch(setThreshold(Number(metadata.threshold)))} - /> - )} - {metadata.perlin !== undefined && ( - dispatch(setPerlin(Number(metadata.perlin)))} - /> - )} */} {metadata.scheduler && ( { onClick={handleRecallCfgScale} /> )} - {/* {metadata.variations && metadata.variations.length > 0 && ( - handleRecallLoRA(lora)} + /> + ); + } + })} ); }; diff --git a/invokeai/frontend/web/src/features/lora/store/loraSlice.ts b/invokeai/frontend/web/src/features/lora/store/loraSlice.ts index 10a1671933..bbe019b1c3 100644 --- a/invokeai/frontend/web/src/features/lora/store/loraSlice.ts +++ b/invokeai/frontend/web/src/features/lora/store/loraSlice.ts @@ -27,6 +27,13 @@ export const loraSlice = createSlice({ const { model_name, id, base_model } = action.payload; state.loras[id] = { id, model_name, base_model, ...defaultLoRAConfig }; }, + loraRecalled: ( + state, + action: PayloadAction + ) => { + const { model_name, id, base_model, weight } = action.payload; + state.loras[id] = { id, model_name, base_model, weight }; + }, loraRemoved: (state, action: PayloadAction) => { const id = action.payload; delete state.loras[id]; @@ -62,6 +69,7 @@ export const { loraWeightChanged, loraWeightReset, lorasCleared, + loraRecalled, } = loraSlice.actions; export default loraSlice.reducer; diff --git a/invokeai/frontend/web/src/features/nodes/types/types.ts b/invokeai/frontend/web/src/features/nodes/types/types.ts index a4b71457f7..6a03c07d57 100644 --- a/invokeai/frontend/web/src/features/nodes/types/types.ts +++ b/invokeai/frontend/web/src/features/nodes/types/types.ts @@ -1057,6 +1057,13 @@ export const isInvocationFieldSchema = ( export type InvocationEdgeExtra = { type: 'default' | 'collapsed' }; +const zLoRAObject = z.object({ + lora: zLoRAModelField.deepPartial(), + weight: z.number(), +}); + +export type LoRAMetadataType = z.infer; + export const zCoreMetadata = z .object({ app_version: z.string().nullish(), @@ -1076,14 +1083,7 @@ export const zCoreMetadata = z .union([zMainModel.deepPartial(), zOnnxModel.deepPartial()]) .nullish(), controlnets: z.array(zControlField.deepPartial()).nullish(), - loras: z - .array( - z.object({ - lora: zLoRAModelField.deepPartial(), - weight: z.number(), - }) - ) - .nullish(), + loras: z.array(zLoRAObject).nullish(), vae: zVaeModelField.nullish(), strength: z.number().nullish(), init_image: z.string().nullish(), diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts index 203ff2cb1b..d7fcc2c9a4 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts @@ -1,6 +1,10 @@ import { useAppToaster } from 'app/components/Toaster'; import { useAppDispatch } from 'app/store/storeHooks'; -import { CoreMetadata } from 'features/nodes/types/types'; +import { + CoreMetadata, + LoRAMetadataType, + LoraInfo, +} from 'features/nodes/types/types'; import { refinerModelChanged, setNegativeStylePromptSDXL, @@ -30,6 +34,7 @@ import { import { isValidCfgScale, isValidHeight, + isValidLoRAModel, isValidMainModel, isValidNegativePrompt, isValidPositivePrompt, @@ -45,6 +50,8 @@ import { isValidStrength, isValidWidth, } from '../types/parameterSchemas'; +import { loraRecalled } from '../../lora/store/loraSlice'; +import { useGetLoRAModelsQuery } from '../../../services/api/endpoints/models'; export const useRecallParameters = () => { const dispatch = useAppDispatch(); @@ -307,6 +314,48 @@ export const useRecallParameters = () => { [dispatch, parameterSetToast, parameterNotSetToast] ); + /** + * Recall LoRA with toast + */ + + const { data: loraModels } = useGetLoRAModelsQuery(); + + const recallLoRA = useCallback( + (lora: LoRAMetadataType) => { + if (!isValidLoRAModel(lora.lora)) { + parameterNotSetToast(); + return; + } + + if (!loraModels || !loraModels.entities) { + return; + } + + const matchingId = Object.keys(loraModels.entities).find((loraId) => { + const matchesBaseModel = + loraModels.entities[loraId]?.base_model === lora.lora.base_model; + const matchesModelName = + loraModels.entities[loraId]?.model_name === lora.lora.model_name; + return matchesBaseModel && matchesModelName; + }); + + if (!matchingId) { +return; +} + + const fullLoRA = loraModels.entities[matchingId]; + + if (!fullLoRA) { +return; +} + + dispatch(loraRecalled({ ...fullLoRA, weight: lora.weight })); + + parameterSetToast(); + }, + [dispatch, parameterSetToast, parameterNotSetToast, loraModels] + ); + /* * Sets image as initial image with toast */ @@ -444,6 +493,7 @@ export const useRecallParameters = () => { recallWidth, recallHeight, recallStrength, + recallLoRA, recallAllParameters, sendToImageToImage, }; From fdf9833c39617cbf5a97095df98fe4920d61d376 Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Fri, 15 Sep 2023 16:53:42 -0400 Subject: [PATCH 3/6] add toast --- .../features/parameters/hooks/useRecallParameters.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts index d7fcc2c9a4..9045646ad5 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts @@ -328,6 +328,7 @@ export const useRecallParameters = () => { } if (!loraModels || !loraModels.entities) { + parameterNotSetToast(); return; } @@ -340,14 +341,16 @@ export const useRecallParameters = () => { }); if (!matchingId) { -return; -} + parameterNotSetToast(); + return; + } const fullLoRA = loraModels.entities[matchingId]; if (!fullLoRA) { -return; -} + parameterNotSetToast(); + return; + } dispatch(loraRecalled({ ...fullLoRA, weight: lora.weight })); From cc0482ae8bf80b38ab262e7e46b70e47b0425273 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 18 Sep 2023 15:24:11 +1000 Subject: [PATCH 4/6] feat(ui): simplify lora recall check --- .../ImageMetadataActions.tsx | 2 +- .../parameters/hooks/useRecallParameters.ts | 59 ++++++++----------- .../web/src/services/api/endpoints/models.ts | 2 +- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx index a0ea69157c..cf58334667 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx @@ -1,9 +1,9 @@ import { CoreMetadata, LoRAMetadataType } from 'features/nodes/types/types'; import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters'; import { memo, useCallback } from 'react'; -import ImageMetadataItem from './ImageMetadataItem'; import { useTranslation } from 'react-i18next'; import { isValidLoRAModel } from '../../../parameters/types/parameterSchemas'; +import ImageMetadataItem from './ImageMetadataItem'; type Props = { metadata?: CoreMetadata; diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts index 9045646ad5..fc33200bd3 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts @@ -1,10 +1,6 @@ import { useAppToaster } from 'app/components/Toaster'; import { useAppDispatch } from 'app/store/storeHooks'; -import { - CoreMetadata, - LoRAMetadataType, - LoraInfo, -} from 'features/nodes/types/types'; +import { CoreMetadata, LoRAMetadataType } from 'features/nodes/types/types'; import { refinerModelChanged, setNegativeStylePromptSDXL, @@ -19,6 +15,11 @@ import { import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { ImageDTO } from 'services/api/types'; +import { + loraModelsAdapter, + useGetLoRAModelsQuery, +} from '../../../services/api/endpoints/models'; +import { loraRecalled } from '../../lora/store/loraSlice'; import { initialImageSelected, modelSelected } from '../store/actions'; import { setCfgScale, @@ -50,8 +51,6 @@ import { isValidStrength, isValidWidth, } from '../types/parameterSchemas'; -import { loraRecalled } from '../../lora/store/loraSlice'; -import { useGetLoRAModelsQuery } from '../../../services/api/endpoints/models'; export const useRecallParameters = () => { const dispatch = useAppDispatch(); @@ -318,45 +317,39 @@ export const useRecallParameters = () => { * Recall LoRA with toast */ - const { data: loraModels } = useGetLoRAModelsQuery(); + const { loras } = useGetLoRAModelsQuery(undefined, { + selectFromResult: (result) => ({ + loras: result.data + ? loraModelsAdapter.getSelectors().selectAll(result.data) + : [], + }), + }); const recallLoRA = useCallback( - (lora: LoRAMetadataType) => { - if (!isValidLoRAModel(lora.lora)) { + (loraMetadataItem: LoRAMetadataType) => { + if (!isValidLoRAModel(loraMetadataItem.lora)) { parameterNotSetToast(); return; } - if (!loraModels || !loraModels.entities) { + const { base_model, model_name } = loraMetadataItem.lora; + + const matchingLoRA = loras.find( + (l) => l.base_model === base_model && l.model_name === model_name + ); + + if (!matchingLoRA) { parameterNotSetToast(); return; } - const matchingId = Object.keys(loraModels.entities).find((loraId) => { - const matchesBaseModel = - loraModels.entities[loraId]?.base_model === lora.lora.base_model; - const matchesModelName = - loraModels.entities[loraId]?.model_name === lora.lora.model_name; - return matchesBaseModel && matchesModelName; - }); - - if (!matchingId) { - parameterNotSetToast(); - return; - } - - const fullLoRA = loraModels.entities[matchingId]; - - if (!fullLoRA) { - parameterNotSetToast(); - return; - } - - dispatch(loraRecalled({ ...fullLoRA, weight: lora.weight })); + dispatch( + loraRecalled({ ...matchingLoRA, weight: loraMetadataItem.weight }) + ); parameterSetToast(); }, - [dispatch, parameterSetToast, parameterNotSetToast, loraModels] + [loras, dispatch, parameterSetToast, parameterNotSetToast] ); /* diff --git a/invokeai/frontend/web/src/services/api/endpoints/models.ts b/invokeai/frontend/web/src/services/api/endpoints/models.ts index 9be8bd13f6..9db7762344 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/models.ts +++ b/invokeai/frontend/web/src/services/api/endpoints/models.ts @@ -128,7 +128,7 @@ export const mainModelsAdapter = createEntityAdapter({ const onnxModelsAdapter = createEntityAdapter({ sortComparer: (a, b) => a.model_name.localeCompare(b.model_name), }); -const loraModelsAdapter = createEntityAdapter({ +export const loraModelsAdapter = createEntityAdapter({ sortComparer: (a, b) => a.model_name.localeCompare(b.model_name), }); export const controlNetModelsAdapter = From 94f16b1c69784ca417ae8613164e6c09fa523b54 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 18 Sep 2023 15:54:40 +1000 Subject: [PATCH 5/6] feat(ui): provide feedback when recalling invalid lora --- .../ImageMetadataActions.tsx | 4 +- .../web/src/features/nodes/types/types.ts | 6 +- .../parameters/hooks/useRecallParameters.ts | 105 +++++++++++++----- 3 files changed, 83 insertions(+), 32 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx index cf58334667..4f1bd39b8c 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx @@ -1,4 +1,4 @@ -import { CoreMetadata, LoRAMetadataType } from 'features/nodes/types/types'; +import { CoreMetadata, LoRAMetadataItem } from 'features/nodes/types/types'; import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -69,7 +69,7 @@ const ImageMetadataActions = (props: Props) => { }, [metadata?.strength, recallStrength]); const handleRecallLoRA = useCallback( - (lora: LoRAMetadataType) => { + (lora: LoRAMetadataItem) => { recallLoRA(lora); }, [recallLoRA] diff --git a/invokeai/frontend/web/src/features/nodes/types/types.ts b/invokeai/frontend/web/src/features/nodes/types/types.ts index 6a03c07d57..c92a41391c 100644 --- a/invokeai/frontend/web/src/features/nodes/types/types.ts +++ b/invokeai/frontend/web/src/features/nodes/types/types.ts @@ -1057,12 +1057,12 @@ export const isInvocationFieldSchema = ( export type InvocationEdgeExtra = { type: 'default' | 'collapsed' }; -const zLoRAObject = z.object({ +const zLoRAMetadataItem = z.object({ lora: zLoRAModelField.deepPartial(), weight: z.number(), }); -export type LoRAMetadataType = z.infer; +export type LoRAMetadataItem = z.infer; export const zCoreMetadata = z .object({ @@ -1083,7 +1083,7 @@ export const zCoreMetadata = z .union([zMainModel.deepPartial(), zOnnxModel.deepPartial()]) .nullish(), controlnets: z.array(zControlField.deepPartial()).nullish(), - loras: z.array(zLoRAObject).nullish(), + loras: z.array(zLoRAMetadataItem).nullish(), vae: zVaeModelField.nullish(), strength: z.number().nullish(), init_image: z.string().nullish(), diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts index fc33200bd3..38764ed856 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts @@ -1,6 +1,8 @@ +import { createSelector } from '@reduxjs/toolkit'; import { useAppToaster } from 'app/components/Toaster'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { CoreMetadata, LoRAMetadataType } from 'features/nodes/types/types'; +import { stateSelector } from 'app/store/store'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { CoreMetadata, LoRAMetadataItem } from 'features/nodes/types/types'; import { refinerModelChanged, setNegativeStylePromptSDXL, @@ -12,7 +14,7 @@ import { setRefinerStart, setRefinerSteps, } from 'features/sdxl/store/sdxlSlice'; -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { ImageDTO } from 'services/api/types'; import { @@ -52,10 +54,16 @@ import { isValidWidth, } from '../types/parameterSchemas'; +const selector = createSelector(stateSelector, ({ generation }) => { + const { model } = generation; + return { model }; +}); + export const useRecallParameters = () => { const dispatch = useAppDispatch(); const toaster = useAppToaster(); const { t } = useTranslation(); + const { model } = useAppSelector(selector); const parameterSetToast = useCallback(() => { toaster({ @@ -66,14 +74,18 @@ export const useRecallParameters = () => { }); }, [t, toaster]); - const parameterNotSetToast = useCallback(() => { - toaster({ - title: t('toast.parameterNotSet'), - status: 'warning', - duration: 2500, - isClosable: true, - }); - }, [t, toaster]); + const parameterNotSetToast = useCallback( + (description?: string) => { + toaster({ + title: t('toast.parameterNotSet'), + description, + status: 'warning', + duration: 2500, + isClosable: true, + }); + }, + [t, toaster] + ); const allParameterSetToast = useCallback(() => { toaster({ @@ -84,14 +96,18 @@ export const useRecallParameters = () => { }); }, [t, toaster]); - const allParameterNotSetToast = useCallback(() => { - toaster({ - title: t('toast.parametersNotSet'), - status: 'warning', - duration: 2500, - isClosable: true, - }); - }, [t, toaster]); + const allParameterNotSetToast = useCallback( + (description?: string) => { + toaster({ + title: t('toast.parametersNotSet'), + status: 'warning', + description, + duration: 2500, + isClosable: true, + }); + }, + [t, toaster] + ); /** * Recall both prompts with toast @@ -325,11 +341,10 @@ export const useRecallParameters = () => { }), }); - const recallLoRA = useCallback( - (loraMetadataItem: LoRAMetadataType) => { + const prepareLoRAMetadataItem = useCallback( + (loraMetadataItem: LoRAMetadataItem) => { if (!isValidLoRAModel(loraMetadataItem.lora)) { - parameterNotSetToast(); - return; + return { lora: null, error: 'Invalid LoRA model' }; } const { base_model, model_name } = loraMetadataItem.lora; @@ -339,17 +354,40 @@ export const useRecallParameters = () => { ); if (!matchingLoRA) { - parameterNotSetToast(); + return { lora: null, error: 'LoRA model is not installed' }; + } + + const isCompatibleBaseModel = + matchingLoRA?.base_model === model?.base_model; + + if (!isCompatibleBaseModel) { + return { + lora: null, + error: 'LoRA incompatible with currently-selected model', + }; + } + + return { lora: matchingLoRA, error: null }; + }, + [loras, model?.base_model] + ); + + const recallLoRA = useCallback( + (loraMetadataItem: LoRAMetadataItem) => { + const result = prepareLoRAMetadataItem(loraMetadataItem); + + if (!result.lora) { + parameterNotSetToast(result.error); return; } dispatch( - loraRecalled({ ...matchingLoRA, weight: loraMetadataItem.weight }) + loraRecalled({ ...result.lora, weight: loraMetadataItem.weight }) ); parameterSetToast(); }, - [loras, dispatch, parameterSetToast, parameterNotSetToast] + [prepareLoRAMetadataItem, dispatch, parameterSetToast, parameterNotSetToast] ); /* @@ -389,6 +427,7 @@ export const useRecallParameters = () => { refiner_positive_aesthetic_score, refiner_negative_aesthetic_score, refiner_start, + loras, } = metadata; if (isValidCfgScale(cfg_scale)) { @@ -470,9 +509,21 @@ export const useRecallParameters = () => { dispatch(setRefinerStart(refiner_start)); } + loras?.forEach((lora) => { + const result = prepareLoRAMetadataItem(lora); + if (result.lora) { + dispatch(loraRecalled({ ...result.lora, weight: lora.weight })); + } + }); + allParameterSetToast(); }, - [allParameterNotSetToast, allParameterSetToast, dispatch] + [ + allParameterNotSetToast, + allParameterSetToast, + dispatch, + prepareLoRAMetadataItem, + ] ); return { From 18698744339fe98d0707e6d769d23ed6ad5a93cc Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 18 Sep 2023 15:58:55 +1000 Subject: [PATCH 6/6] chore(ui): lint --- .../web/src/features/parameters/hooks/useRecallParameters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts index 38764ed856..bc850df0d0 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts @@ -14,7 +14,7 @@ import { setRefinerStart, setRefinerSteps, } from 'features/sdxl/store/sdxlSlice'; -import { useCallback, useMemo } from 'react'; +import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { ImageDTO } from 'services/api/types'; import {