mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-01-15 06:18:03 -05:00
refactor(ui): simplified metadata parsing (WIP)
This commit is contained in:
@@ -9,7 +9,7 @@ import type {
|
||||
UnrecallableMetadataHandler,
|
||||
} from 'features/metadata/parsing';
|
||||
import {
|
||||
MetadataHanders,
|
||||
MetadataHandlers,
|
||||
useCollectionMetadataDatum,
|
||||
useSingleMetadataDatum,
|
||||
useUnrecallableMetadataDatum,
|
||||
@@ -30,33 +30,33 @@ const ImageMetadataActions = (props: Props) => {
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" ps={8}>
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={MetadataHanders.CreatedBy} />
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={MetadataHanders.GenerationMode} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.PositivePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.NegativePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.PositiveStylePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.NegativeStylePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.MainModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.VAEModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.Width} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.Height} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.Seed} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.Steps} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.Scheduler} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.CFGScale} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.CFGRescaleMultiplier} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.Guidance} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.DenoisingStrength} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.SeamlessX} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.SeamlessY} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.RefinerModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.RefinerCFGScale} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.RefinerPositiveAestheticScore} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.RefinerNegativeAestheticScore} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.RefinerScheduler} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.RefinerDenoisingStart} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHanders.RefinerSteps} />
|
||||
<CollectionMetadataDatum metadata={metadata} handler={MetadataHanders.LoRAs} />
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={MetadataHandlers.CreatedBy} />
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={MetadataHandlers.GenerationMode} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.PositivePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.NegativePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.PositiveStylePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.NegativeStylePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.MainModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.VAEModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Width} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Height} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Seed} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Steps} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Scheduler} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.CFGScale} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.CFGRescaleMultiplier} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Guidance} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.DenoisingStrength} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.SeamlessX} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.SeamlessY} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerCFGScale} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerPositiveAestheticScore} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerNegativeAestheticScore} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerScheduler} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerDenoisingStart} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerSteps} />
|
||||
<CollectionMetadataDatum metadata={metadata} handler={MetadataHandlers.LoRAs} />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -84,7 +84,7 @@ const UnrecallableMetadataParsed = typedMemo(
|
||||
|
||||
return (
|
||||
<Box as="span" lineHeight={1}>
|
||||
<LabelComponent value={data.value} />
|
||||
<LabelComponent />
|
||||
<ValueComponent value={data.value} />
|
||||
</Box>
|
||||
);
|
||||
@@ -127,7 +127,7 @@ const SingleMetadataParsed = typedMemo(
|
||||
onClick={onClick}
|
||||
/>
|
||||
<Box as="span" lineHeight={1}>
|
||||
<LabelComponent value={data.value} />
|
||||
<LabelComponent />
|
||||
<ValueComponent value={data.value} />
|
||||
</Box>
|
||||
</Flex>
|
||||
@@ -148,7 +148,7 @@ const CollectionMetadataDatum = typedMemo(
|
||||
return (
|
||||
<>
|
||||
{data.value.map((value, i) => (
|
||||
<CollectionMetadataParsed key={i} value={value} i={i} handler={handler} data={data} />
|
||||
<CollectionMetadataParsed key={i} value={value} handler={handler} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
@@ -158,17 +158,7 @@ const CollectionMetadataDatum = typedMemo(
|
||||
CollectionMetadataDatum.displayName = 'CollectionMetadataDatum';
|
||||
|
||||
const CollectionMetadataParsed = typedMemo(
|
||||
<T extends any[]>({
|
||||
value,
|
||||
i,
|
||||
data,
|
||||
handler,
|
||||
}: {
|
||||
value: T[number];
|
||||
i: number;
|
||||
data: ParsedSuccessData<T>;
|
||||
handler: CollectionMetadataHandler<T>;
|
||||
}) => {
|
||||
<T extends any[]>({ value, handler }: { value: T[number]; handler: CollectionMetadataHandler<T> }) => {
|
||||
const store = useAppStore();
|
||||
|
||||
const { LabelComponent, ValueComponent } = handler;
|
||||
@@ -187,7 +177,7 @@ const CollectionMetadataParsed = typedMemo(
|
||||
onClick={onClick}
|
||||
/>
|
||||
<Box as="span" lineHeight={1}>
|
||||
<LabelComponent values={data.value} i={i} />
|
||||
<LabelComponent />
|
||||
<ValueComponent value={value} />
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
positivePrompt2Changed,
|
||||
positivePromptChanged,
|
||||
refinerModelChanged,
|
||||
selectBase,
|
||||
setCfgRescaleMultiplier,
|
||||
setCfgScale,
|
||||
setGuidance,
|
||||
@@ -28,6 +29,7 @@ import {
|
||||
setSeamlessYAxis,
|
||||
setSeed,
|
||||
setSteps,
|
||||
shouldConcatPromptsChanged,
|
||||
vaeSelected,
|
||||
} from 'features/controlLayers/store/paramsSlice';
|
||||
import type { LoRA } from 'features/controlLayers/store/types';
|
||||
@@ -77,7 +79,9 @@ import {
|
||||
zParameterSteps,
|
||||
zParameterStrength,
|
||||
} from 'features/parameters/types/parameterSchemas';
|
||||
import type { ComponentType } from 'react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { t } from 'i18next';
|
||||
import type { ComponentType, ReactNode } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { modelsApi } from 'services/api/endpoints/models';
|
||||
@@ -94,15 +98,6 @@ const MetadataLabel = ({ i18nKey }: { i18nKey: string }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const MetadataLabelWithCount = <T extends any[]>({ i18nKey, i }: { i18nKey: string; i: number; values: T }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Text as="span" fontWeight="semibold" whiteSpace="pre-wrap" me={2}>
|
||||
{`${t(i18nKey)} ${i + 1}:`}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
const MetadataPrimitiveValue = ({ value }: { value: string | number | boolean | null | undefined }) => {
|
||||
return <Text as="span">{value}</Text>;
|
||||
};
|
||||
@@ -158,53 +153,60 @@ const buildParsedErrorData = (error: Error): ParsedErrorData => ({
|
||||
|
||||
export type Data<T> = UnparsedData | ParsedSuccessData<T> | ParsedErrorData;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
type SingleMetadataLabelProps<T> = {
|
||||
value: T;
|
||||
};
|
||||
const SingleMetadataKey = Symbol('SingleMetadataKey');
|
||||
type SingleMetadataValueProps<T> = {
|
||||
value: T;
|
||||
};
|
||||
export type SingleMetadataHandler<T> = {
|
||||
[SingleMetadataKey]: true;
|
||||
type: string;
|
||||
parse: (metadata: unknown, store: AppStore) => Promise<T>;
|
||||
recall: (value: T, store: AppStore) => void;
|
||||
LabelComponent: ComponentType<SingleMetadataLabelProps<T>>;
|
||||
LabelComponent: ComponentType;
|
||||
ValueComponent: ComponentType<SingleMetadataValueProps<T>>;
|
||||
};
|
||||
|
||||
type CollectionMetadataLabelProps<T extends any[]> = {
|
||||
values: T;
|
||||
i: number;
|
||||
};
|
||||
const CollectionMetadataKey = Symbol('CollectionMetadataKey');
|
||||
type CollectionMetadataValueProps<T extends any[]> = {
|
||||
value: T[number];
|
||||
};
|
||||
export type CollectionMetadataHandler<T extends any[]> = {
|
||||
[CollectionMetadataKey]: true;
|
||||
type: string;
|
||||
parse: (metadata: unknown, store: AppStore) => Promise<T>;
|
||||
recallAll: (values: T, store: AppStore) => void;
|
||||
recall: (values: T, store: AppStore) => void;
|
||||
recallOne: (value: T[number], store: AppStore) => void;
|
||||
LabelComponent: ComponentType<CollectionMetadataLabelProps<T>>;
|
||||
LabelComponent: ComponentType;
|
||||
ValueComponent: ComponentType<CollectionMetadataValueProps<T>>;
|
||||
};
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
type UnrecallableMetadataLabelProps<T> = {
|
||||
value: T;
|
||||
};
|
||||
const UnrecallableMetadataKey = Symbol('UnrecallableMetadataKey');
|
||||
type UnrecallableMetadataValueProps<T> = {
|
||||
value: T;
|
||||
};
|
||||
export type UnrecallableMetadataHandler<T> = {
|
||||
[UnrecallableMetadataKey]: true;
|
||||
type: string;
|
||||
parse: (metadata: unknown, store: AppStore) => Promise<T>;
|
||||
LabelComponent: ComponentType<UnrecallableMetadataLabelProps<T>>;
|
||||
LabelComponent: ComponentType;
|
||||
ValueComponent: ComponentType<UnrecallableMetadataValueProps<T>>;
|
||||
};
|
||||
|
||||
const isSingleMetadataHandler = (
|
||||
handler: SingleMetadataHandler<any> | CollectionMetadataHandler<any[]> | UnrecallableMetadataHandler<any>
|
||||
): handler is SingleMetadataHandler<any> => {
|
||||
return SingleMetadataKey in handler && handler[SingleMetadataKey] === true;
|
||||
};
|
||||
|
||||
const isCollectionMetadataHandler = (
|
||||
handler: SingleMetadataHandler<any> | CollectionMetadataHandler<any[]> | UnrecallableMetadataHandler<any>
|
||||
): handler is CollectionMetadataHandler<any[]> => {
|
||||
return CollectionMetadataKey in handler && handler[CollectionMetadataKey] === true;
|
||||
};
|
||||
|
||||
//#region Created By
|
||||
const CreatedBy: UnrecallableMetadataHandler<string> = {
|
||||
[UnrecallableMetadataKey]: true,
|
||||
type: 'CreatedBy',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'created_by');
|
||||
@@ -212,12 +214,13 @@ const CreatedBy: UnrecallableMetadataHandler<string> = {
|
||||
return Promise.resolve(parsed);
|
||||
},
|
||||
LabelComponent: () => <MetadataLabel i18nKey="metadata.createdBy" />,
|
||||
ValueComponent: ({ value }: UnrecallableMetadataLabelProps<string>) => <MetadataPrimitiveValue value={value} />,
|
||||
ValueComponent: ({ value }: UnrecallableMetadataValueProps<string>) => <MetadataPrimitiveValue value={value} />,
|
||||
};
|
||||
//#endregion Created By
|
||||
|
||||
//#region Generation Mode
|
||||
const GenerationMode: UnrecallableMetadataHandler<string> = {
|
||||
[UnrecallableMetadataKey]: true,
|
||||
type: 'GenerationMode',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'generation_mode');
|
||||
@@ -225,12 +228,13 @@ const GenerationMode: UnrecallableMetadataHandler<string> = {
|
||||
return Promise.resolve(parsed);
|
||||
},
|
||||
LabelComponent: () => <MetadataLabel i18nKey="metadata.generationMode" />,
|
||||
ValueComponent: ({ value }: UnrecallableMetadataLabelProps<string>) => <MetadataPrimitiveValue value={value} />,
|
||||
ValueComponent: ({ value }: UnrecallableMetadataValueProps<string>) => <MetadataPrimitiveValue value={value} />,
|
||||
};
|
||||
//#endregion Generation Mode
|
||||
|
||||
//#region Positive Prompt
|
||||
const PositivePrompt: SingleMetadataHandler<ParameterPositivePrompt> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'PositivePrompt',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'positive_prompt');
|
||||
@@ -249,6 +253,7 @@ const PositivePrompt: SingleMetadataHandler<ParameterPositivePrompt> = {
|
||||
|
||||
//#region Negative Prompt
|
||||
const NegativePrompt: SingleMetadataHandler<ParameterNegativePrompt> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'NegativePrompt',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'negative_prompt');
|
||||
@@ -267,6 +272,7 @@ const NegativePrompt: SingleMetadataHandler<ParameterNegativePrompt> = {
|
||||
|
||||
//#region SDXL Positive Style Prompt
|
||||
const PositiveStylePrompt: SingleMetadataHandler<ParameterPositiveStylePromptSDXL> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'PositiveStylePrompt',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'positive_style_prompt');
|
||||
@@ -285,6 +291,7 @@ const PositiveStylePrompt: SingleMetadataHandler<ParameterPositiveStylePromptSDX
|
||||
|
||||
//#region SDXL Negative Style Prompt
|
||||
const NegativeStylePrompt: SingleMetadataHandler<ParameterPositiveStylePromptSDXL> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'NegativeStylePrompt',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'negative_style_prompt');
|
||||
@@ -303,6 +310,7 @@ const NegativeStylePrompt: SingleMetadataHandler<ParameterPositiveStylePromptSDX
|
||||
|
||||
//#region CFG Scale
|
||||
const CFGScale: SingleMetadataHandler<ParameterCFGScale> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'CFGScale',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'cfg_scale');
|
||||
@@ -319,6 +327,7 @@ const CFGScale: SingleMetadataHandler<ParameterCFGScale> = {
|
||||
|
||||
//#region CFG Rescale Multiplier
|
||||
const CFGRescaleMultiplier: SingleMetadataHandler<ParameterCFGRescaleMultiplier> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'CFGRescaleMultiplier',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'cfg_rescale_multiplier');
|
||||
@@ -337,6 +346,7 @@ const CFGRescaleMultiplier: SingleMetadataHandler<ParameterCFGRescaleMultiplier>
|
||||
|
||||
//#region Guidance
|
||||
const Guidance: SingleMetadataHandler<ParameterGuidance> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'Guidance',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'guidance');
|
||||
@@ -353,6 +363,7 @@ const Guidance: SingleMetadataHandler<ParameterGuidance> = {
|
||||
|
||||
//#region Scheduler
|
||||
const Scheduler: SingleMetadataHandler<ParameterScheduler> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'Scheduler',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'scheduler');
|
||||
@@ -369,6 +380,7 @@ const Scheduler: SingleMetadataHandler<ParameterScheduler> = {
|
||||
|
||||
//#region Width
|
||||
const Width: SingleMetadataHandler<ParameterWidth> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'Width',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'width');
|
||||
@@ -385,6 +397,7 @@ const Width: SingleMetadataHandler<ParameterWidth> = {
|
||||
|
||||
//#region Height
|
||||
const Height: SingleMetadataHandler<ParameterHeight> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'Height',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'height');
|
||||
@@ -401,6 +414,7 @@ const Height: SingleMetadataHandler<ParameterHeight> = {
|
||||
|
||||
//#region Seed
|
||||
const Seed: SingleMetadataHandler<ParameterSeed> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'Seed',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'seed');
|
||||
@@ -417,6 +431,7 @@ const Seed: SingleMetadataHandler<ParameterSeed> = {
|
||||
|
||||
//#region Steps
|
||||
const Steps: SingleMetadataHandler<ParameterSteps> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'Steps',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'steps');
|
||||
@@ -433,6 +448,7 @@ const Steps: SingleMetadataHandler<ParameterSteps> = {
|
||||
|
||||
//#region DenoisingStrength
|
||||
const DenoisingStrength: SingleMetadataHandler<ParameterStrength> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'DenoisingStrength',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'strength');
|
||||
@@ -449,6 +465,7 @@ const DenoisingStrength: SingleMetadataHandler<ParameterStrength> = {
|
||||
|
||||
//#region SeamlessX
|
||||
const SeamlessX: SingleMetadataHandler<ParameterSeamlessX> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'SeamlessX',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'seamless_x');
|
||||
@@ -465,6 +482,7 @@ const SeamlessX: SingleMetadataHandler<ParameterSeamlessX> = {
|
||||
|
||||
//#region SeamlessY
|
||||
const SeamlessY: SingleMetadataHandler<ParameterSeamlessY> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'SeamlessY',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'seamless_y');
|
||||
@@ -481,12 +499,14 @@ const SeamlessY: SingleMetadataHandler<ParameterSeamlessY> = {
|
||||
|
||||
//#region RefinerModel
|
||||
const RefinerModel: SingleMetadataHandler<ParameterSDXLRefinerModel> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'RefinerModel',
|
||||
parse: async (metadata, store) => {
|
||||
const raw = getProperty(metadata, 'refiner_model');
|
||||
const parsed = await parseModelIdentifier(raw, store, 'main');
|
||||
assert(parsed.type === 'main');
|
||||
assert(parsed.base === 'sdxl-refiner');
|
||||
assert(isCompatibleWithMainModel(parsed, store));
|
||||
return Promise.resolve(parsed);
|
||||
},
|
||||
recall: (value, store) => {
|
||||
@@ -501,6 +521,7 @@ const RefinerModel: SingleMetadataHandler<ParameterSDXLRefinerModel> = {
|
||||
|
||||
//#region RefinerSteps
|
||||
const RefinerSteps: SingleMetadataHandler<ParameterSteps> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'RefinerSteps',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'refiner_steps');
|
||||
@@ -517,6 +538,7 @@ const RefinerSteps: SingleMetadataHandler<ParameterSteps> = {
|
||||
|
||||
//#region RefinerCFGScale
|
||||
const RefinerCFGScale: SingleMetadataHandler<ParameterCFGScale> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'RefinerCFGScale',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'refiner_cfg_scale');
|
||||
@@ -533,6 +555,7 @@ const RefinerCFGScale: SingleMetadataHandler<ParameterCFGScale> = {
|
||||
|
||||
//#region RefinerScheduler
|
||||
const RefinerScheduler: SingleMetadataHandler<ParameterScheduler> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'RefinerScheduler',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'refiner_scheduler');
|
||||
@@ -549,6 +572,7 @@ const RefinerScheduler: SingleMetadataHandler<ParameterScheduler> = {
|
||||
|
||||
//#region RefinerPositiveAestheticScore
|
||||
const RefinerPositiveAestheticScore: SingleMetadataHandler<ParameterSDXLRefinerPositiveAestheticScore> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'RefinerPositiveAestheticScore',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'refiner_positive_aesthetic_score');
|
||||
@@ -567,6 +591,7 @@ const RefinerPositiveAestheticScore: SingleMetadataHandler<ParameterSDXLRefinerP
|
||||
|
||||
//#region RefinerNegativeAestheticScore
|
||||
const RefinerNegativeAestheticScore: SingleMetadataHandler<ParameterSDXLRefinerNegativeAestheticScore> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'RefinerNegativeAestheticScore',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'refiner_negative_aesthetic_score');
|
||||
@@ -585,6 +610,7 @@ const RefinerNegativeAestheticScore: SingleMetadataHandler<ParameterSDXLRefinerN
|
||||
|
||||
//#region RefinerDenoisingStart
|
||||
const RefinerDenoisingStart: SingleMetadataHandler<ParameterSDXLRefinerStart> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'RefinerDenoisingStart',
|
||||
parse: (metadata, _store) => {
|
||||
const raw = getProperty(metadata, 'refiner_start');
|
||||
@@ -603,6 +629,7 @@ const RefinerDenoisingStart: SingleMetadataHandler<ParameterSDXLRefinerStart> =
|
||||
|
||||
//#region MainModel
|
||||
const MainModel: SingleMetadataHandler<ParameterModel> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'MainModel',
|
||||
parse: async (metadata, store) => {
|
||||
const raw = getProperty(metadata, 'model');
|
||||
@@ -622,11 +649,13 @@ const MainModel: SingleMetadataHandler<ParameterModel> = {
|
||||
|
||||
//#region VAEModel
|
||||
const VAEModel: SingleMetadataHandler<ParameterVAEModel> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'VAEModel',
|
||||
parse: async (metadata, store) => {
|
||||
const raw = getProperty(metadata, 'vae');
|
||||
const parsed = await parseModelIdentifier(raw, store, 'vae');
|
||||
assert(parsed.type === 'vae');
|
||||
assert(isCompatibleWithMainModel(parsed, store));
|
||||
return Promise.resolve(parsed);
|
||||
},
|
||||
recall: (value, store) => {
|
||||
@@ -641,6 +670,7 @@ const VAEModel: SingleMetadataHandler<ParameterVAEModel> = {
|
||||
|
||||
//#region LoRAs
|
||||
const LoRAs: CollectionMetadataHandler<LoRA[]> = {
|
||||
[CollectionMetadataKey]: true,
|
||||
type: 'LoRAs',
|
||||
parse: async (metadata, store) => {
|
||||
const rawArray = getProperty(metadata, 'loras');
|
||||
@@ -651,6 +681,7 @@ const LoRAs: CollectionMetadataHandler<LoRA[]> = {
|
||||
for (const rawItem of rawArray) {
|
||||
try {
|
||||
let identifier: ModelIdentifierField | null = null;
|
||||
|
||||
try {
|
||||
// New format - { model: ModelIdenfifierField }
|
||||
const rawIdentifier = getProperty(rawItem, 'model');
|
||||
@@ -662,8 +693,12 @@ const LoRAs: CollectionMetadataHandler<LoRA[]> = {
|
||||
// No need to catch here - if this throws, we move on to the next item
|
||||
identifier = await getModelIdentiferFromKey(key, store);
|
||||
}
|
||||
|
||||
assert(identifier.type === 'lora');
|
||||
assert(isCompatibleWithMainModel(identifier, store));
|
||||
|
||||
const weight = getProperty(rawItem, 'weight');
|
||||
|
||||
loras.push({
|
||||
id: getPrefixedId('lora'),
|
||||
model: identifier,
|
||||
@@ -684,22 +719,20 @@ const LoRAs: CollectionMetadataHandler<LoRA[]> = {
|
||||
recallOne: (value, store) => {
|
||||
store.dispatch(loraRecalled({ lora: value }));
|
||||
},
|
||||
recallAll: (values, store) => {
|
||||
recall: (values, store) => {
|
||||
store.dispatch(loraAllDeleted());
|
||||
for (const lora of values) {
|
||||
store.dispatch(loraRecalled({ lora }));
|
||||
}
|
||||
},
|
||||
LabelComponent: ({ values, i }: CollectionMetadataLabelProps<LoRA[]>) => (
|
||||
<MetadataLabelWithCount i18nKey="models.lora" values={values} i={i} />
|
||||
),
|
||||
LabelComponent: () => <MetadataLabel i18nKey="models.lora" />,
|
||||
ValueComponent: ({ value }: CollectionMetadataValueProps<LoRA[]>) => (
|
||||
<MetadataPrimitiveValue value={`${value.model.name} (${value.model.base.toUpperCase()}) - ${value.weight}`} />
|
||||
),
|
||||
};
|
||||
//#endregion LoRAs
|
||||
|
||||
export const MetadataHanders = {
|
||||
export const MetadataHandlers = {
|
||||
CreatedBy,
|
||||
GenerationMode,
|
||||
PositivePrompt,
|
||||
@@ -727,10 +760,174 @@ export const MetadataHanders = {
|
||||
MainModel,
|
||||
VAEModel,
|
||||
LoRAs,
|
||||
} satisfies Record<
|
||||
string,
|
||||
UnrecallableMetadataHandler<any> | SingleMetadataHandler<any> | CollectionMetadataHandler<any[]>
|
||||
>;
|
||||
} as const;
|
||||
|
||||
const successToast = (parameter: ReactNode) => {
|
||||
toast({
|
||||
id: 'PARAMETER_SET',
|
||||
title: t('toast.parameterSet'),
|
||||
description: t('toast.parameterSetDesc', { parameter }),
|
||||
status: 'info',
|
||||
});
|
||||
};
|
||||
|
||||
const failedToast = (parameter: ReactNode, message?: ReactNode) => {
|
||||
toast({
|
||||
id: 'PARAMETER_NOT_SET',
|
||||
title: t('toast.parameterNotSet'),
|
||||
description: message
|
||||
? t('toast.parameterNotSetDescWithMessage', { parameter, message })
|
||||
: t('toast.parameterNotSetDesc', { parameter }),
|
||||
status: 'warning',
|
||||
});
|
||||
};
|
||||
|
||||
const recallByHandler = async (arg: {
|
||||
metadata: unknown;
|
||||
handler: SingleMetadataHandler<any> | CollectionMetadataHandler<any[]>;
|
||||
store: AppStore;
|
||||
silent?: boolean;
|
||||
}): Promise<boolean> => {
|
||||
const { metadata, handler, store, silent = false } = arg;
|
||||
|
||||
let didRecall = false;
|
||||
|
||||
try {
|
||||
const value = await handler.parse(metadata, store);
|
||||
handler.recall(value, store);
|
||||
didRecall = true;
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
if (didRecall) {
|
||||
successToast(<handler.LabelComponent />);
|
||||
} else {
|
||||
failedToast(<handler.LabelComponent />);
|
||||
}
|
||||
}
|
||||
|
||||
return didRecall;
|
||||
};
|
||||
|
||||
const recallByHandlers = async (arg: {
|
||||
metadata: unknown;
|
||||
handlers: (SingleMetadataHandler<any> | CollectionMetadataHandler<any[]>)[];
|
||||
store: AppStore;
|
||||
silent?: boolean;
|
||||
}): Promise<Map<SingleMetadataHandler<any> | CollectionMetadataHandler<any[]>, unknown>> => {
|
||||
const { metadata, handlers, store, silent = false } = arg;
|
||||
|
||||
const recalled = new Map<SingleMetadataHandler<any> | CollectionMetadataHandler<any[]>, unknown>();
|
||||
|
||||
// It's possible for some metadata item's recall to clobber the recall of another. For example, the model recall
|
||||
// may change the width and height. If we are also recalling the width and height directly, we need to ensure that the
|
||||
// model is recalled first, so it doesn't accidentally override the width and height. This is the only known case
|
||||
// where the order of recall matters.
|
||||
const sortedHandlers = handlers.sort((a, b) => {
|
||||
if (a === MetadataHandlers.MainModel) {
|
||||
return -1; // MainModel should be recalled first
|
||||
} else if (b === MetadataHandlers.MainModel) {
|
||||
return 1; // MainModel should be recalled first
|
||||
} else {
|
||||
return 0; // Keep the original order for other handlers
|
||||
}
|
||||
});
|
||||
|
||||
for (const handler of sortedHandlers) {
|
||||
try {
|
||||
const value = await handler.parse(metadata, store);
|
||||
handler.recall(value, store);
|
||||
recalled.set(handler, value);
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
// If we recalled style prompts, and they were _different_ from the positive prompt, we need to disable prompt concat.
|
||||
const positivePrompt = recalled.get(MetadataHandlers.PositivePrompt);
|
||||
const negativePrompt = recalled.get(MetadataHandlers.NegativePrompt);
|
||||
const positiveStylePrompt = recalled.get(MetadataHandlers.PositiveStylePrompt);
|
||||
const negativeStylePrompt = recalled.get(MetadataHandlers.NegativeStylePrompt);
|
||||
|
||||
if (
|
||||
(positiveStylePrompt && positiveStylePrompt !== positivePrompt) ||
|
||||
(negativeStylePrompt && negativeStylePrompt !== negativePrompt)
|
||||
) {
|
||||
// If we set the negative style prompt or positive style prompt, we should disable prompt concat
|
||||
store.dispatch(shouldConcatPromptsChanged(false));
|
||||
} else {
|
||||
// Otherwise, we should enable prompt concat
|
||||
store.dispatch(shouldConcatPromptsChanged(true));
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
if (recalled.size > 0) {
|
||||
toast({
|
||||
id: 'PARAMETER_SET',
|
||||
title: t('toast.parametersSet'),
|
||||
status: 'info',
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
id: 'PARAMETER_SET',
|
||||
title: t('toast.parametersNotSet'),
|
||||
status: 'warning',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return recalled;
|
||||
};
|
||||
|
||||
const recallPrompts = async (metadata: unknown, store: AppStore) => {
|
||||
const recalled = await recallByHandlers({
|
||||
metadata,
|
||||
handlers: [
|
||||
MetadataHandlers.PositivePrompt,
|
||||
MetadataHandlers.NegativePrompt,
|
||||
MetadataHandlers.PositiveStylePrompt,
|
||||
MetadataHandlers.NegativeStylePrompt,
|
||||
],
|
||||
store,
|
||||
silent: true,
|
||||
});
|
||||
if (recalled.size > 0) {
|
||||
successToast(t('metadata.allPrompts'));
|
||||
}
|
||||
};
|
||||
|
||||
const recallDimensions = async (metadata: unknown, store: AppStore) => {
|
||||
const recalled = await recallByHandlers({
|
||||
metadata,
|
||||
handlers: [MetadataHandlers.Width, MetadataHandlers.Height],
|
||||
store,
|
||||
silent: true,
|
||||
});
|
||||
if (recalled.size > 0) {
|
||||
successToast(t('metadata.imageDimensions'));
|
||||
}
|
||||
};
|
||||
|
||||
const recallAll = async (metadata: unknown, store: AppStore) => {
|
||||
const handlers = Object.values(MetadataHandlers).filter(
|
||||
(handler) => isSingleMetadataHandler(handler) || isCollectionMetadataHandler(handler)
|
||||
);
|
||||
await recallByHandlers({
|
||||
metadata,
|
||||
handlers,
|
||||
store,
|
||||
});
|
||||
};
|
||||
|
||||
export const MetadataUtils = {
|
||||
recallByHandler,
|
||||
recallByHandlers,
|
||||
recallAll,
|
||||
recallPrompts,
|
||||
recallDimensions,
|
||||
} as const;
|
||||
|
||||
export function useSingleMetadataDatum<T>(metadata: unknown, handler: SingleMetadataHandler<T>) {
|
||||
const store = useAppStore();
|
||||
@@ -791,7 +988,7 @@ export function useCollectionMetadataDatum<T extends any[]>(metadata: unknown, h
|
||||
|
||||
const recallAll = useCallback(
|
||||
(values: T) => {
|
||||
handler.recallAll(values, store);
|
||||
handler.recall(values, store);
|
||||
},
|
||||
[handler, store]
|
||||
);
|
||||
@@ -829,34 +1026,38 @@ export function useUnrecallableMetadataDatum<T>(metadata: unknown, handler: Unre
|
||||
return { data };
|
||||
}
|
||||
|
||||
const OPTIONS = { subscribe: false };
|
||||
const options = { subscribe: false };
|
||||
|
||||
const getModelIdentiferFromKey = async (key: string, store: AppStore): Promise<AnyModelConfig> => {
|
||||
const req = store.dispatch(modelsApi.endpoints.getModelConfig.initiate(key, OPTIONS));
|
||||
const req = store.dispatch(modelsApi.endpoints.getModelConfig.initiate(key, options));
|
||||
const modelConfig = await req.unwrap();
|
||||
return modelConfig;
|
||||
};
|
||||
|
||||
const parseModelIdentifier = async (raw: unknown, store: AppStore, type: ModelType): Promise<ModelIdentifierField> => {
|
||||
// First try the current format identifier: key, name, base, type, hash
|
||||
try {
|
||||
// First try the current format identifier: key, name, base, type, hash
|
||||
const { key } = zModelIdentifierField.parse(raw);
|
||||
const req = store.dispatch(modelsApi.endpoints.getModelConfig.initiate(key, OPTIONS));
|
||||
const req = store.dispatch(modelsApi.endpoints.getModelConfig.initiate(key, options));
|
||||
const modelConfig = await req.unwrap();
|
||||
return zModelIdentifierField.parse(modelConfig);
|
||||
} catch {
|
||||
// noop
|
||||
// We'll try to parse the old format identifier next
|
||||
}
|
||||
|
||||
// Fall back to old format identifier: model_name, base_model
|
||||
try {
|
||||
const { model_name: name, base_model: base } = zModelIdentifier.parse(raw);
|
||||
const arg = { name, base, type };
|
||||
const req = store.dispatch(modelsApi.endpoints.getModelConfigByAttrs.initiate(arg, OPTIONS));
|
||||
const modelConfig = await req.unwrap();
|
||||
return zModelIdentifierField.parse(modelConfig);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
throw new Error('Unable to parse model identifier');
|
||||
// No error handling here - this is our last chance to get a model identifier
|
||||
const { model_name, base_model } = zModelIdentifier.parse(raw);
|
||||
const arg = { name: model_name, base: base_model, type };
|
||||
const req = store.dispatch(modelsApi.endpoints.getModelConfigByAttrs.initiate(arg, options));
|
||||
const modelConfig = await req.unwrap();
|
||||
return zModelIdentifierField.parse(modelConfig);
|
||||
};
|
||||
|
||||
const isCompatibleWithMainModel = (candidate: ModelIdentifierField, store: AppStore) => {
|
||||
const base = selectBase(store.getState());
|
||||
if (!base) {
|
||||
return true;
|
||||
}
|
||||
return candidate.base === base;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user