diff --git a/invokeai/frontend/web/src/features/metadata/components/MetadataControlNets.tsx b/invokeai/frontend/web/src/features/metadata/components/MetadataControlNets.tsx
index 1255e8e7cb..1e1c45632f 100644
--- a/invokeai/frontend/web/src/features/metadata/components/MetadataControlNets.tsx
+++ b/invokeai/frontend/web/src/features/metadata/components/MetadataControlNets.tsx
@@ -1,4 +1,3 @@
-import { Text } from '@invoke-ai/ui-library';
import type { ControlNetConfig } from 'features/controlAdapters/store/types';
import { MetadataItemView } from 'features/metadata/components/MetadataItemView';
import type { MetadataHandlers } from 'features/metadata/types';
@@ -56,11 +55,18 @@ const MetadataViewControlNet = ({
handlers.recallItem(controlNet, true);
}, [handlers, controlNet]);
- const renderedValue = useMemo(() => {
- if (!handlers.renderItemValue) {
- return null;
- }
- return {handlers.renderItemValue(controlNet)};
+ const [renderedValue, setRenderedValue] = useState(null);
+ useEffect(() => {
+ const _renderValue = async () => {
+ if (!handlers.renderItemValue) {
+ setRenderedValue(null);
+ return;
+ }
+ const rendered = await handlers.renderItemValue(controlNet);
+ setRenderedValue(rendered);
+ };
+
+ _renderValue();
}, [handlers, controlNet]);
return ;
diff --git a/invokeai/frontend/web/src/features/metadata/components/MetadataIPAdapters.tsx b/invokeai/frontend/web/src/features/metadata/components/MetadataIPAdapters.tsx
index 69f9ec986d..8c3705d698 100644
--- a/invokeai/frontend/web/src/features/metadata/components/MetadataIPAdapters.tsx
+++ b/invokeai/frontend/web/src/features/metadata/components/MetadataIPAdapters.tsx
@@ -1,4 +1,3 @@
-import { Text } from '@invoke-ai/ui-library';
import type { IPAdapterConfig } from 'features/controlAdapters/store/types';
import { MetadataItemView } from 'features/metadata/components/MetadataItemView';
import type { MetadataHandlers } from 'features/metadata/types';
@@ -51,11 +50,18 @@ const MetadataViewIPAdapter = ({
handlers.recallItem(ipAdapter, true);
}, [handlers, ipAdapter]);
- const renderedValue = useMemo(() => {
- if (!handlers.renderItemValue) {
- return null;
- }
- return {handlers.renderItemValue(ipAdapter)};
+ const [renderedValue, setRenderedValue] = useState(null);
+ useEffect(() => {
+ const _renderValue = async () => {
+ if (!handlers.renderItemValue) {
+ setRenderedValue(null);
+ return;
+ }
+ const rendered = await handlers.renderItemValue(ipAdapter);
+ setRenderedValue(rendered);
+ };
+
+ _renderValue();
}, [handlers, ipAdapter]);
return ;
diff --git a/invokeai/frontend/web/src/features/metadata/components/MetadataLoRAs.tsx b/invokeai/frontend/web/src/features/metadata/components/MetadataLoRAs.tsx
index 40f6bc427b..7e78985c49 100644
--- a/invokeai/frontend/web/src/features/metadata/components/MetadataLoRAs.tsx
+++ b/invokeai/frontend/web/src/features/metadata/components/MetadataLoRAs.tsx
@@ -1,4 +1,3 @@
-import { Text } from '@invoke-ai/ui-library';
import type { LoRA } from 'features/lora/store/loraSlice';
import { MetadataItemView } from 'features/metadata/components/MetadataItemView';
import type { MetadataHandlers } from 'features/metadata/types';
@@ -51,11 +50,18 @@ const MetadataViewLoRA = ({
handlers.recallItem(lora, true);
}, [handlers, lora]);
- const renderedValue = useMemo(() => {
- if (!handlers.renderItemValue) {
- return null;
- }
- return {handlers.renderItemValue(lora)};
+ const [renderedValue, setRenderedValue] = useState(null);
+ useEffect(() => {
+ const _renderValue = async () => {
+ if (!handlers.renderItemValue) {
+ setRenderedValue(null);
+ return;
+ }
+ const rendered = await handlers.renderItemValue(lora);
+ setRenderedValue(rendered);
+ };
+
+ _renderValue();
}, [handlers, lora]);
return ;
diff --git a/invokeai/frontend/web/src/features/metadata/components/MetadataT2IAdapters.tsx b/invokeai/frontend/web/src/features/metadata/components/MetadataT2IAdapters.tsx
index 209ab4f2ed..4464fbbd8f 100644
--- a/invokeai/frontend/web/src/features/metadata/components/MetadataT2IAdapters.tsx
+++ b/invokeai/frontend/web/src/features/metadata/components/MetadataT2IAdapters.tsx
@@ -1,4 +1,3 @@
-import { Text } from '@invoke-ai/ui-library';
import type { T2IAdapterConfig } from 'features/controlAdapters/store/types';
import { MetadataItemView } from 'features/metadata/components/MetadataItemView';
import type { MetadataHandlers } from 'features/metadata/types';
@@ -55,12 +54,19 @@ const MetadataViewT2IAdapter = ({
}
handlers.recallItem(t2iAdapter, true);
}, [handlers, t2iAdapter]);
+
+ const [renderedValue, setRenderedValue] = useState(null);
+ useEffect(() => {
+ const _renderValue = async () => {
+ if (!handlers.renderItemValue) {
+ setRenderedValue(null);
+ return;
+ }
+ const rendered = await handlers.renderItemValue(t2iAdapter);
+ setRenderedValue(rendered);
+ };
- const renderedValue = useMemo(() => {
- if (!handlers.renderItemValue) {
- return null;
- }
- return {handlers.renderItemValue(t2iAdapter)};
+ _renderValue();
}, [handlers, t2iAdapter]);
return ;
diff --git a/invokeai/frontend/web/src/features/metadata/hooks/useMetadataItem.tsx b/invokeai/frontend/web/src/features/metadata/hooks/useMetadataItem.tsx
index 178ac5155a..63d987569b 100644
--- a/invokeai/frontend/web/src/features/metadata/hooks/useMetadataItem.tsx
+++ b/invokeai/frontend/web/src/features/metadata/hooks/useMetadataItem.tsx
@@ -3,10 +3,14 @@ import type { MetadataHandlers } from 'features/metadata/types';
import { MetadataParseFailedToken, MetadataParsePendingToken } from 'features/metadata/util/parsers';
import { useCallback, useEffect, useMemo, useState } from 'react';
+const pendingRenderedValue = Loading;
+const failedRenderedValue = Parsing Failed;
+
export const useMetadataItem = (metadata: unknown, handlers: MetadataHandlers) => {
const [value, setValue] = useState(
MetadataParsePendingToken
);
+ const [renderedValue, setRenderedValue] = useState(pendingRenderedValue);
useEffect(() => {
const _parse = async () => {
@@ -24,20 +28,27 @@ export const useMetadataItem = (metadata: unknown, handlers: MetadataHandler
const label = useMemo(() => handlers.getLabel(), [handlers]);
- const renderedValue = useMemo(() => {
- if (value === MetadataParsePendingToken) {
- return Loading;
- }
- if (value === MetadataParseFailedToken) {
- return Parsing Failed;
- }
+ useEffect(() => {
+ const _renderValue = async () => {
+ if (value === MetadataParsePendingToken) {
+ setRenderedValue(pendingRenderedValue);
+ return;
+ }
+ if (value === MetadataParseFailedToken) {
+ setRenderedValue(failedRenderedValue);
+ return;
+ }
- const rendered = handlers.renderValue(value);
+ const rendered = await handlers.renderValue(value);
- if (typeof rendered === 'string') {
- return {rendered};
- }
- return rendered;
+ if (typeof rendered === 'string') {
+ setRenderedValue({rendered});
+ return;
+ }
+ setRenderedValue(rendered);
+ };
+
+ _renderValue();
}, [handlers, value]);
const onRecall = useCallback(() => {
diff --git a/invokeai/frontend/web/src/features/metadata/types.ts b/invokeai/frontend/web/src/features/metadata/types.ts
index 366f11701e..4390a525b7 100644
--- a/invokeai/frontend/web/src/features/metadata/types.ts
+++ b/invokeai/frontend/web/src/features/metadata/types.ts
@@ -1,7 +1,7 @@
/**
* Renders a value of type T as a React node.
*/
-export type MetadataRenderValueFunc = (value: T) => React.ReactNode;
+export type MetadataRenderValueFunc = (value: T) => Promise;
/**
* Gets the label of the current metadata item as a string.
diff --git a/invokeai/frontend/web/src/features/metadata/util/handlers.ts b/invokeai/frontend/web/src/features/metadata/util/handlers.ts
index 49d85f4eb3..8f520a6b4e 100644
--- a/invokeai/frontend/web/src/features/metadata/util/handlers.ts
+++ b/invokeai/frontend/web/src/features/metadata/util/handlers.ts
@@ -11,18 +11,38 @@ import type {
MetadataRenderValueFunc,
MetadataValidateFunc,
} from 'features/metadata/types';
+import { fetchModelConfig } from 'features/metadata/util/modelFetchingHelpers';
import { validators } from 'features/metadata/util/validators';
+import type { ModelIdentifierWithBase } from 'features/nodes/types/common';
import { t } from 'i18next';
-import type { AnyModelConfig } from 'services/api/types';
import { parsers } from './parsers';
import { recallers } from './recallers';
-const renderModelConfigValue: MetadataRenderValueFunc = (value) =>
- `${value.name} (${value.base.toUpperCase()}, ${value.key})`;
-const renderLoRAValue: MetadataRenderValueFunc = (value) => `${value.model.key} (${value.weight})`;
-const renderControlAdapterValue: MetadataRenderValueFunc = (value) =>
- `${value.model?.key} (${value.weight})`;
+const renderModelConfigValue: MetadataRenderValueFunc = async (value) => {
+ try {
+ const modelConfig = await fetchModelConfig(value.key);
+ return `${modelConfig.name} (${modelConfig.base.toUpperCase()})`;
+ } catch {
+ return `${value.key} (${value.base.toUpperCase()})`;
+ }
+};
+const renderLoRAValue: MetadataRenderValueFunc = async (value) => {
+ try {
+ const modelConfig = await fetchModelConfig(value.model.key);
+ return `${modelConfig.name} (${modelConfig.base.toUpperCase()}) - ${value.weight}`;
+ } catch {
+ return `${value.model.key} (${value.model.base.toUpperCase()}) - ${value.weight}`;
+ }
+};
+const renderControlAdapterValue: MetadataRenderValueFunc = async (value) => {
+ try {
+ const modelConfig = await fetchModelConfig(value.model?.key ?? 'none');
+ return `${modelConfig.name} (${modelConfig.base.toUpperCase()}) - ${value.weight}`;
+ } catch {
+ return `${value.model?.key} (${value.model?.base.toUpperCase()}) - ${value.weight}`;
+ }
+};
const parameterSetToast = (parameter: string, description?: string) => {
toast({
@@ -130,6 +150,8 @@ const buildRecallItem =
}
};
+const resolveToString = (value: unknown) => new Promise((resolve) => resolve(String(value)));
+
const buildHandlers: BuildMetadataHandlers = ({
getLabel,
parser,
@@ -146,8 +168,8 @@ const buildHandlers: BuildMetadataHandlers = ({
recall: recaller ? buildRecall({ recaller, validator, getLabel }) : undefined,
recallItem: itemRecaller ? buildRecallItem({ itemRecaller, itemValidator, getLabel }) : undefined,
getLabel,
- renderValue: renderValue ?? String,
- renderItemValue: renderItemValue ?? String,
+ renderValue: renderValue ?? resolveToString,
+ renderItemValue: renderItemValue ?? resolveToString,
});
export const handlers = {
diff --git a/invokeai/frontend/web/src/features/metadata/util/parsers.ts b/invokeai/frontend/web/src/features/metadata/util/parsers.ts
index df0485a0c4..619b8cc26c 100644
--- a/invokeai/frontend/web/src/features/metadata/util/parsers.ts
+++ b/invokeai/frontend/web/src/features/metadata/util/parsers.ts
@@ -12,7 +12,6 @@ import type { MetadataParseFunc } from 'features/metadata/types';
import {
fetchModelConfigWithTypeGuard,
getModelKey,
- getModelKeyAndBase,
} from 'features/metadata/util/modelFetchingHelpers';
import {
zControlField,
@@ -26,17 +25,20 @@ import type {
ParameterHeight,
ParameterHRFEnabled,
ParameterHRFMethod,
+ ParameterModel,
ParameterNegativePrompt,
ParameterNegativeStylePromptSDXL,
ParameterPositivePrompt,
ParameterPositiveStylePromptSDXL,
ParameterScheduler,
+ ParameterSDXLRefinerModel,
ParameterSDXLRefinerNegativeAestheticScore,
ParameterSDXLRefinerPositiveAestheticScore,
ParameterSDXLRefinerStart,
ParameterSeed,
ParameterSteps,
ParameterStrength,
+ ParameterVAEModel,
ParameterWidth,
} from 'features/parameters/types/parameterSchemas';
import {
@@ -60,7 +62,6 @@ import {
isParameterWidth,
} from 'features/parameters/types/parameterSchemas';
import { get, isArray, isString } from 'lodash-es';
-import type { NonRefinerMainModelConfig, RefinerMainModelConfig, VAEModelConfig } from 'services/api/types';
import {
isControlNetModelConfig,
isIPAdapterModelConfig,
@@ -163,25 +164,28 @@ const parseRefinerNegativeAestheticScore: MetadataParseFunc = (metadata) =>
getProperty(metadata, 'refiner_start', isParameterSDXLRefinerStart);
-const parseMainModel: MetadataParseFunc = async (metadata) => {
+const parseMainModel: MetadataParseFunc = async (metadata) => {
const model = await getProperty(metadata, 'model', undefined);
const key = await getModelKey(model, 'main');
const mainModelConfig = await fetchModelConfigWithTypeGuard(key, isNonRefinerMainModelConfig);
- return mainModelConfig;
+ const modelIdentifier = zModelIdentifierWithBase.parse(mainModelConfig);
+ return modelIdentifier;
};
-const parseRefinerModel: MetadataParseFunc = async (metadata) => {
+const parseRefinerModel: MetadataParseFunc = async (metadata) => {
const refiner_model = await getProperty(metadata, 'refiner_model', undefined);
const key = await getModelKey(refiner_model, 'main');
const refinerModelConfig = await fetchModelConfigWithTypeGuard(key, isRefinerMainModelModelConfig);
- return refinerModelConfig;
+ const modelIdentifier = zModelIdentifierWithBase.parse(refinerModelConfig);
+ return modelIdentifier;
};
-const parseVAEModel: MetadataParseFunc = async (metadata) => {
+const parseVAEModel: MetadataParseFunc = async (metadata) => {
const vae = await getProperty(metadata, 'vae', undefined);
const key = await getModelKey(vae, 'vae');
const vaeModelConfig = await fetchModelConfigWithTypeGuard(key, isVAEModelConfig);
- return vaeModelConfig;
+ const modelIdentifier = zModelIdentifierWithBase.parse(vaeModelConfig);
+ return modelIdentifier;
};
const parseLoRA: MetadataParseFunc = async (metadataItem) => {
@@ -194,7 +198,7 @@ const parseLoRA: MetadataParseFunc = async (metadataItem) => {
const loraModelConfig = await fetchModelConfigWithTypeGuard(key, isLoRAModelConfig);
return {
- model: getModelKeyAndBase(loraModelConfig),
+ model: zModelIdentifierWithBase.parse(loraModelConfig),
weight: isParameterLoRAWeight(weight) ? weight : defaultLoRAConfig.weight,
isEnabled: true,
};
diff --git a/invokeai/frontend/web/src/features/metadata/util/recallers.ts b/invokeai/frontend/web/src/features/metadata/util/recallers.ts
index 5e156dc035..cc867933b1 100644
--- a/invokeai/frontend/web/src/features/metadata/util/recallers.ts
+++ b/invokeai/frontend/web/src/features/metadata/util/recallers.ts
@@ -5,7 +5,6 @@ import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/
import type { LoRA } from 'features/lora/store/loraSlice';
import { loraRecalled } from 'features/lora/store/loraSlice';
import type { MetadataRecallFunc } from 'features/metadata/types';
-import { zModelIdentifierWithBase } from 'features/nodes/types/common';
import { modelSelected } from 'features/parameters/store/actions';
import {
heightRecalled,
@@ -26,17 +25,20 @@ import type {
ParameterHeight,
ParameterHRFEnabled,
ParameterHRFMethod,
+ ParameterModel,
ParameterNegativePrompt,
ParameterNegativeStylePromptSDXL,
ParameterPositivePrompt,
ParameterPositiveStylePromptSDXL,
ParameterScheduler,
+ ParameterSDXLRefinerModel,
ParameterSDXLRefinerNegativeAestheticScore,
ParameterSDXLRefinerPositiveAestheticScore,
ParameterSDXLRefinerStart,
ParameterSeed,
ParameterSteps,
ParameterStrength,
+ ParameterVAEModel,
ParameterWidth,
} from 'features/parameters/types/parameterSchemas';
import {
@@ -50,7 +52,6 @@ import {
setRefinerStart,
setRefinerSteps,
} from 'features/sdxl/store/sdxlSlice';
-import type { NonRefinerMainModelConfig, RefinerMainModelConfig, VAEModelConfig } from 'services/api/types';
const recallPositivePrompt: MetadataRecallFunc = (positivePrompt) => {
getStore().dispatch(setPositivePrompt(positivePrompt));
@@ -140,23 +141,20 @@ const recallRefinerStart: MetadataRecallFunc = (refin
getStore().dispatch(setRefinerStart(refinerStart));
};
-const recallModel: MetadataRecallFunc = (model) => {
- const modelIdentifier = zModelIdentifierWithBase.parse(model);
- getStore().dispatch(modelSelected(modelIdentifier));
+const recallModel: MetadataRecallFunc = (model) => {
+ getStore().dispatch(modelSelected(model));
};
-const recallRefinerModel: MetadataRecallFunc = (refinerModel) => {
- const modelIdentifier = zModelIdentifierWithBase.parse(refinerModel);
- getStore().dispatch(refinerModelChanged(modelIdentifier));
+const recallRefinerModel: MetadataRecallFunc = (refinerModel) => {
+ getStore().dispatch(refinerModelChanged(refinerModel));
};
-const recallVAE: MetadataRecallFunc = (vaeModel) => {
+const recallVAE: MetadataRecallFunc = (vaeModel) => {
if (!vaeModel) {
getStore().dispatch(vaeSelected(null));
return;
}
- const modelIdentifier = zModelIdentifierWithBase.parse(vaeModel);
- getStore().dispatch(vaeSelected(modelIdentifier));
+ getStore().dispatch(vaeSelected(vaeModel));
};
const recallLoRA: MetadataRecallFunc = (lora) => {
diff --git a/invokeai/frontend/web/src/features/metadata/util/validators.ts b/invokeai/frontend/web/src/features/metadata/util/validators.ts
index 1f34e38651..9a9ffae723 100644
--- a/invokeai/frontend/web/src/features/metadata/util/validators.ts
+++ b/invokeai/frontend/web/src/features/metadata/util/validators.ts
@@ -3,7 +3,8 @@ import type { ControlNetConfig, IPAdapterConfig, T2IAdapterConfig } from 'featur
import type { LoRA } from 'features/lora/store/loraSlice';
import type { MetadataValidateFunc } from 'features/metadata/types';
import { InvalidModelConfigError } from 'features/metadata/util/modelFetchingHelpers';
-import type { BaseModelType, RefinerMainModelConfig, VAEModelConfig } from 'services/api/types';
+import type { ParameterSDXLRefinerModel, ParameterVAEModel } from 'features/parameters/types/parameterSchemas';
+import type { BaseModelType } from 'services/api/types';
/**
* Checks the given base model type against the currently-selected model's base type and throws an error if they are
@@ -21,12 +22,12 @@ const validateBaseCompatibility = (base?: BaseModelType, message?: string) => {
}
};
-const validateRefinerModel: MetadataValidateFunc = (refinerModel) => {
+const validateRefinerModel: MetadataValidateFunc = (refinerModel) => {
validateBaseCompatibility('sdxl', 'Refiner incompatible with currently-selected model');
return new Promise((resolve) => resolve(refinerModel));
};
-const validateVAEModel: MetadataValidateFunc = (vaeModel) => {
+const validateVAEModel: MetadataValidateFunc = (vaeModel) => {
validateBaseCompatibility(vaeModel.base, 'VAE incompatible with currently-selected model');
return new Promise((resolve) => resolve(vaeModel));
};