recall CLIP skip

This commit is contained in:
Attila Cseh
2025-08-13 14:36:00 +02:00
committed by psychedelicious
parent 6e32c7993c
commit 3fd10b68cd
10 changed files with 122 additions and 21 deletions

View File

@@ -770,6 +770,7 @@
"allPrompts": "All Prompts",
"cfgScale": "CFG scale",
"cfgRescaleMultiplier": "$t(parameters.cfgRescaleMultiplier)",
"clipSkip": "$t(parameters.clipSkip)",
"createdBy": "Created By",
"generationMode": "Generation Mode",
"guidance": "Guidance",
@@ -1290,6 +1291,7 @@
"remixImage": "Remix Image",
"usePrompt": "Use Prompt",
"useSeed": "Use Seed",
"useClipSkip": "Use CLIP Skip",
"width": "Width",
"gaussianBlur": "Gaussian Blur",
"boxBlur": "Box Blur",

View File

@@ -16,6 +16,7 @@ import {
rgRefImageImageChanged,
} from 'features/controlLayers/store/canvasSlice';
import {
selectCLIPSkip,
selectMainModelConfig,
selectNegativePrompt,
selectPositivePrompt,
@@ -81,6 +82,7 @@ const useSaveCanvas = ({ region, saveToGallery, toastOk, toastError, onSave, wit
if (withMetadata) {
metadata = selectCanvasMetadata(state);
metadata.clip_skip = selectCLIPSkip(state);
metadata.positive_prompt = selectPositivePrompt(state);
metadata.negative_prompt = selectNegativePrompt(state);
metadata.seed = selectSeed(state);

View File

@@ -484,7 +484,7 @@ export const selectCFGScale = createParamsSelector((params) => params.cfgScale);
export const selectGuidance = createParamsSelector((params) => params.guidance);
export const selectSteps = createParamsSelector((params) => params.steps);
export const selectCFGRescaleMultiplier = createParamsSelector((params) => params.cfgRescaleMultiplier);
export const selectCLIPSKip = createParamsSelector((params) => params.clipSkip);
export const selectCLIPSkip = createParamsSelector((params) => params.clipSkip);
export const selectCanvasCoherenceEdgeSize = createParamsSelector((params) => params.canvasCoherenceEdgeSize);
export const selectCanvasCoherenceMinDenoise = createParamsSelector((params) => params.canvasCoherenceMinDenoise);
export const selectCanvasCoherenceMode = createParamsSelector((params) => params.canvasCoherenceMode);

View File

@@ -2,6 +2,7 @@ import { Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
import { SubMenuButtonContent, useSubMenu } from 'common/hooks/useSubMenu';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { useRecallAll } from 'features/gallery/hooks/useRecallAll';
import { useRecallCLIPSkip } from 'features/gallery/hooks/useRecallCLIPSkip';
import { useRecallDimensions } from 'features/gallery/hooks/useRecallDimensions';
import { useRecallPrompts } from 'features/gallery/hooks/useRecallPrompts';
import { useRecallRemix } from 'features/gallery/hooks/useRecallRemix';
@@ -28,6 +29,7 @@ export const ImageMenuItemMetadataRecallActionsCanvasGenerateTabs = memo(() => {
const recallPrompts = useRecallPrompts(imageDTO);
const recallSeed = useRecallSeed(imageDTO);
const recallDimensions = useRecallDimensions(imageDTO);
const recallCLIPSkip = useRecallCLIPSkip(imageDTO);
return (
<MenuItem {...subMenu.parentMenuItemProps} icon={<PiArrowBendUpLeftBold />}>
@@ -55,6 +57,9 @@ export const ImageMenuItemMetadataRecallActionsCanvasGenerateTabs = memo(() => {
<MenuItem icon={<PiRulerBold />} onClick={recallDimensions.recall} isDisabled={!recallDimensions.isEnabled}>
{t('parameters.useSize')}
</MenuItem>
<MenuItem icon={<PiRulerBold />} onClick={recallCLIPSkip.recall} isDisabled={!recallCLIPSkip.isEnabled}>
{t('parameters.useClipSkip')}
</MenuItem>
</MenuList>
</Menu>
</MenuItem>

View File

@@ -0,0 +1,65 @@
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import type { TabName } from 'features/ui/store/uiTypes';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
import type { ImageDTO } from 'services/api/types';
const ALLOWED_TABS: TabName[] = ['canvas', 'generate', 'upscaling'];
export const useRecallCLIPSkip = (imageDTO: ImageDTO) => {
const store = useAppStore();
const tab = useAppSelector(selectActiveTab);
const [hasCLIPSkip, setCLIPSkip] = useState(false);
const { metadata, isLoading } = useDebouncedMetadata(imageDTO.image_name);
useEffect(() => {
const parse = async () => {
try {
await MetadataHandlers.CLIPSkip.parse(metadata, store);
setCLIPSkip(true);
} catch {
setCLIPSkip(false);
}
};
parse();
}, [metadata, store]);
const isEnabled = useMemo(() => {
if (isLoading) {
return false;
}
if (!ALLOWED_TABS.includes(tab)) {
return false;
}
if (!metadata) {
return false;
}
if (!hasCLIPSkip) {
return false;
}
return true;
}, [hasCLIPSkip, isLoading, metadata, tab]);
const recall = useCallback(() => {
if (!metadata) {
return;
}
if (!isEnabled) {
return;
}
MetadataUtils.recallByHandler({ metadata, handler: MetadataHandlers.CLIPSkip, store });
}, [metadata, isEnabled, store]);
return {
recall,
isEnabled,
};
};

View File

@@ -15,6 +15,7 @@ import {
selectBase,
setCfgRescaleMultiplier,
setCfgScale,
setClipSkip,
setGuidance,
setImg2imgStrength,
setRefinerCFGScale,
@@ -41,6 +42,7 @@ import { modelSelected } from 'features/parameters/store/actions';
import type {
ParameterCFGRescaleMultiplier,
ParameterCFGScale,
ParameterCLIPSkip,
ParameterGuidance,
ParameterHeight,
ParameterModel,
@@ -63,6 +65,7 @@ import {
zLoRAWeight,
zParameterCFGRescaleMultiplier,
zParameterCFGScale,
zParameterCLIPSkip,
zParameterGuidance,
zParameterImageDimension,
zParameterNegativePrompt,
@@ -321,6 +324,24 @@ const CFGRescaleMultiplier: SingleMetadataHandler<ParameterCFGRescaleMultiplier>
};
//#endregion CFG Rescale Multiplier
//#region CLIP Skip
const CLIPSkip: SingleMetadataHandler<ParameterCLIPSkip> = {
[SingleMetadataKey]: true,
type: 'CLIPSkip',
parse: (metadata, _store) => {
const raw = getProperty(metadata, 'clip_skip');
const parsed = zParameterCLIPSkip.parse(raw);
return Promise.resolve(parsed);
},
recall: (value, store) => {
store.dispatch(setClipSkip(value));
},
i18nKey: 'metadata.clipSkip',
LabelComponent: MetadataLabel,
ValueComponent: ({ value }: SingleMetadataValueProps<ParameterCLIPSkip>) => <MetadataPrimitiveValue value={value} />,
};
//#endregion CLIP Skip
//#region Guidance
const Guidance: SingleMetadataHandler<ParameterGuidance> = {
[SingleMetadataKey]: true,
@@ -883,6 +904,7 @@ export const MetadataHandlers = {
NegativePrompt,
CFGScale,
CFGRescaleMultiplier,
CLIPSkip,
Guidance,
Scheduler,
Width,

View File

@@ -1,14 +1,14 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { selectCLIPSKip, selectModel, setClipSkip } from 'features/controlLayers/store/paramsSlice';
import { selectCLIPSkip, selectModel, setClipSkip } from 'features/controlLayers/store/paramsSlice';
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
import { selectCLIPSkipConfig } from 'features/system/store/configSlice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParamClipSkip = () => {
const clipSkip = useAppSelector(selectCLIPSKip);
const clipSkip = useAppSelector(selectCLIPSkip);
const config = useAppSelector(selectCLIPSkipConfig);
const model = useAppSelector(selectModel);

View File

@@ -4,8 +4,6 @@ import { buildZodTypeGuard } from 'common/util/zodUtils';
import { zModelIdentifierField, zSchedulerField } from 'features/nodes/types/common';
import { z } from 'zod';
import { CLIP_SKIP_MAP } from './constants';
/**
* Schemas, types and type guards for parameters.
*
@@ -196,20 +194,21 @@ export const [zLoRAWeight, isParameterLoRAWeight] = buildParameter(z.number());
export type ParameterLoRAWeight = z.infer<typeof zLoRAWeight>;
// #endregion
// #region Max. CLIP
export const [zParameterMaxCLIP, isParameterMaxCLIP] = buildParameter(
z.union([
z.discriminatedUnion('model', [
z.object({ model: z.literal('sd-1'), maxClip: z.number().min(0).max(CLIP_SKIP_MAP['sd-1'].maxClip) }),
z.object({ model: z.literal('sd-2'), maxClip: z.number().min(0).max(CLIP_SKIP_MAP['sd-2'].maxClip) }),
z.object({ model: z.literal('sdxl'), maxClip: z.number().min(0).max(CLIP_SKIP_MAP['sdxl'].maxClip) }),
z.object({
model: z.literal('sdxl-refiner'),
maxClip: z.number().min(0).max(CLIP_SKIP_MAP['sdxl-refiner'].maxClip),
}),
]),
z.object({ model: z.string(), maxClip: z.number().min(0).max(0) }),
])
);
export type ParameterMaxCLIP = z.infer<typeof zParameterMaxCLIP>;
// #region CLIP skip
// export const [zParameterCLIPSkip, isParameterCLIPSkip] = buildParameter(
// z.union([
// z.discriminatedUnion('model', [
// z.object({ model: z.literal('sd-1'), clipSkip: z.number().min(0).max(CLIP_SKIP_MAP['sd-1'].maxClip) }),
// z.object({ model: z.literal('sd-2'), clipSkip: z.number().min(0).max(CLIP_SKIP_MAP['sd-2'].maxClip) }),
// z.object({ model: z.literal('sdxl'), clipSkip: z.number().min(0).max(CLIP_SKIP_MAP['sdxl'].maxClip) }),
// z.object({
// model: z.literal('sdxl-refiner'),
// clipSkip: z.number().min(0).max(CLIP_SKIP_MAP['sdxl-refiner'].maxClip),
// }),
// ]),
// z.object({ model: z.string(), clipSkip: z.number().min(0).max(0) }),
// ])
// );
export const [zParameterCLIPSkip, isParameterCLIPSkip] = buildParameter(z.number().int().min(0));
export type ParameterCLIPSkip = z.infer<typeof zParameterCLIPSkip>;
// #endregion

View File

@@ -10292,6 +10292,11 @@ export type components = {
* @description The id of the board the image belongs to, if one exists.
*/
board_id?: string | null;
/**
* Clip Skip
* @description The number of skipped CLIP layers
*/
clip_skip?: number | null;
};
/**
* ImageField

View File

@@ -61,6 +61,7 @@ const _zImageDTO = z.object({
starred: z.boolean(),
has_workflow: z.boolean(),
board_id: z.string().nullish(),
clip_skip: z.number().int().min(0).nullish(),
});
export type ImageDTO = z.infer<typeof _zImageDTO>;
assert<Equals<ImageDTO, S['ImageDTO']>>();