mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): use prompt bug when prompt has colon
This bug is related to the format in which we stored prompts for some time: an array of weighted subprompts. This caused some strife when recalling a prompt if the prompt had colons in it, due to our recently introduced handling of negative prompts. Currently there is no need to store a prompt as anything other than a string, so we revert to doing that. Compatibility with structured prompts is maintained via helper hook.
This commit is contained in:
parent
ab018ccdfe
commit
7ffaa17551
@ -1383,13 +1383,6 @@ class InvokeAIWebServer:
|
|||||||
# semantic drift
|
# semantic drift
|
||||||
rfc_dict["sampler"] = parameters["sampler_name"]
|
rfc_dict["sampler"] = parameters["sampler_name"]
|
||||||
|
|
||||||
# display weighted subprompts (liable to change)
|
|
||||||
subprompts = split_weighted_subprompts(
|
|
||||||
parameters["prompt"], skip_normalize=True
|
|
||||||
)
|
|
||||||
subprompts = [{"prompt": x[0], "weight": x[1]} for x in subprompts]
|
|
||||||
rfc_dict["prompt"] = subprompts
|
|
||||||
|
|
||||||
# 'variations' should always exist and be an array, empty or consisting of {'seed': seed, 'weight': weight} pairs
|
# 'variations' should always exist and be an array, empty or consisting of {'seed': seed, 'weight': weight} pairs
|
||||||
variations = []
|
variations = []
|
||||||
|
|
||||||
|
3
invokeai/frontend/src/app/invokeai.d.ts
vendored
3
invokeai/frontend/src/app/invokeai.d.ts
vendored
@ -29,7 +29,8 @@ export declare type PromptItem = {
|
|||||||
weight: number;
|
weight: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type Prompt = Array<PromptItem>;
|
// TECHDEBT: We need to retain compatibility with plain prompt strings and the structure Prompt type
|
||||||
|
export declare type Prompt = Array<PromptItem> | string;
|
||||||
|
|
||||||
export declare type SeedWeightPair = {
|
export declare type SeedWeightPair = {
|
||||||
seed: number;
|
seed: number;
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/invokeai';
|
||||||
import promptToString from './promptToString';
|
import promptToString from './promptToString';
|
||||||
|
|
||||||
export function getPromptAndNegative(input_prompt: InvokeAI.Prompt) {
|
export function getPromptAndNegative(inputPrompt: InvokeAI.Prompt) {
|
||||||
let prompt: string = promptToString(input_prompt);
|
let prompt: string =
|
||||||
let negativePrompt: string | null = null;
|
typeof inputPrompt === 'string' ? inputPrompt : promptToString(inputPrompt);
|
||||||
|
|
||||||
|
let negativePrompt = '';
|
||||||
|
|
||||||
// Matches all negative prompts, 1st capturing group is the prompt itself
|
// Matches all negative prompts, 1st capturing group is the prompt itself
|
||||||
const negativePromptRegExp = new RegExp(/\[([^\][]*)]/, 'gi');
|
const negativePromptRegExp = new RegExp(/\[([^\][]*)]/, 'gi');
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/invokeai';
|
||||||
|
|
||||||
const promptToString = (prompt: InvokeAI.Prompt): string => {
|
const promptToString = (prompt: InvokeAI.Prompt): string => {
|
||||||
|
if (typeof prompt === 'string') {
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
if (prompt.length === 1) {
|
if (prompt.length === 1) {
|
||||||
return prompt[0].prompt;
|
return prompt[0].prompt;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
|||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import IAIPopover from 'common/components/IAIPopover';
|
import IAIPopover from 'common/components/IAIPopover';
|
||||||
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
|
||||||
import {
|
import {
|
||||||
setDoesCanvasNeedScaling,
|
setDoesCanvasNeedScaling,
|
||||||
setInitialCanvasImage,
|
setInitialCanvasImage,
|
||||||
@ -20,8 +19,6 @@ import UpscaleSettings from 'features/parameters/components/AdvancedParameters/U
|
|||||||
import {
|
import {
|
||||||
setAllParameters,
|
setAllParameters,
|
||||||
setInitialImage,
|
setInitialImage,
|
||||||
setNegativePrompt,
|
|
||||||
setPrompt,
|
|
||||||
setSeed,
|
setSeed,
|
||||||
} from 'features/parameters/store/generationSlice';
|
} from 'features/parameters/store/generationSlice';
|
||||||
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
|
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
|
||||||
@ -53,6 +50,8 @@ import {
|
|||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import { gallerySelector } from '../store/gallerySelectors';
|
import { gallerySelector } from '../store/gallerySelectors';
|
||||||
import DeleteImageModal from './DeleteImageModal';
|
import DeleteImageModal from './DeleteImageModal';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import useSetBothPrompts from 'features/parameters/hooks/usePrompt';
|
||||||
|
|
||||||
const currentImageButtonsSelector = createSelector(
|
const currentImageButtonsSelector = createSelector(
|
||||||
[
|
[
|
||||||
@ -125,6 +124,7 @@ const CurrentImageButtons = () => {
|
|||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const setBothPrompts = useSetBothPrompts();
|
||||||
|
|
||||||
const handleClickUseAsInitialImage = () => {
|
const handleClickUseAsInitialImage = () => {
|
||||||
if (!currentImage) return;
|
if (!currentImage) return;
|
||||||
@ -253,18 +253,11 @@ const CurrentImageButtons = () => {
|
|||||||
[currentImage]
|
[currentImage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleClickUsePrompt = () => {
|
const handleClickUsePrompt = useCallback(() => {
|
||||||
if (currentImage?.metadata?.image?.prompt) {
|
if (currentImage?.metadata?.image?.prompt) {
|
||||||
const [prompt, negativePrompt] = getPromptAndNegative(
|
setBothPrompts(currentImage?.metadata?.image?.prompt);
|
||||||
currentImage?.metadata?.image?.prompt
|
|
||||||
);
|
|
||||||
|
|
||||||
prompt && dispatch(setPrompt(prompt));
|
|
||||||
negativePrompt
|
|
||||||
? dispatch(setNegativePrompt(negativePrompt))
|
|
||||||
: dispatch(setNegativePrompt(''));
|
|
||||||
}
|
}
|
||||||
};
|
}, [currentImage?.metadata?.image?.prompt, setBothPrompts]);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'p',
|
'p',
|
||||||
|
@ -8,8 +8,6 @@ import {
|
|||||||
setAllImageToImageParameters,
|
setAllImageToImageParameters,
|
||||||
setAllParameters,
|
setAllParameters,
|
||||||
setInitialImage,
|
setInitialImage,
|
||||||
setNegativePrompt,
|
|
||||||
setPrompt,
|
|
||||||
setSeed,
|
setSeed,
|
||||||
} from 'features/parameters/store/generationSlice';
|
} from 'features/parameters/store/generationSlice';
|
||||||
import { DragEvent, memo, useState } from 'react';
|
import { DragEvent, memo, useState } from 'react';
|
||||||
@ -18,7 +16,6 @@ import DeleteImageModal from './DeleteImageModal';
|
|||||||
|
|
||||||
import * as ContextMenu from '@radix-ui/react-context-menu';
|
import * as ContextMenu from '@radix-ui/react-context-menu';
|
||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/invokeai';
|
||||||
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
|
||||||
import {
|
import {
|
||||||
resizeAndScaleCanvas,
|
resizeAndScaleCanvas,
|
||||||
setInitialCanvasImage,
|
setInitialCanvasImage,
|
||||||
@ -26,6 +23,7 @@ import {
|
|||||||
import { hoverableImageSelector } from 'features/gallery/store/gallerySelectors';
|
import { hoverableImageSelector } from 'features/gallery/store/gallerySelectors';
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import useSetBothPrompts from 'features/parameters/hooks/usePrompt';
|
||||||
|
|
||||||
interface HoverableImageProps {
|
interface HoverableImageProps {
|
||||||
image: InvokeAI.Image;
|
image: InvokeAI.Image;
|
||||||
@ -55,23 +53,16 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
const [isHovered, setIsHovered] = useState<boolean>(false);
|
const [isHovered, setIsHovered] = useState<boolean>(false);
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const setBothPrompts = useSetBothPrompts();
|
||||||
|
|
||||||
const handleMouseOver = () => setIsHovered(true);
|
const handleMouseOver = () => setIsHovered(true);
|
||||||
|
|
||||||
const handleMouseOut = () => setIsHovered(false);
|
const handleMouseOut = () => setIsHovered(false);
|
||||||
|
|
||||||
const handleUsePrompt = () => {
|
const handleUsePrompt = () => {
|
||||||
if (image.metadata) {
|
if (image.metadata?.image?.prompt) {
|
||||||
const [prompt, negativePrompt] = getPromptAndNegative(
|
setBothPrompts(image.metadata?.image?.prompt);
|
||||||
image.metadata?.image?.prompt
|
|
||||||
);
|
|
||||||
|
|
||||||
prompt && dispatch(setPrompt(prompt));
|
|
||||||
negativePrompt
|
|
||||||
? dispatch(setNegativePrompt(negativePrompt))
|
|
||||||
: dispatch(setNegativePrompt(''));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
|
@ -12,6 +12,7 @@ import * as InvokeAI from 'app/invokeai';
|
|||||||
import { useAppDispatch } from 'app/storeHooks';
|
import { useAppDispatch } from 'app/storeHooks';
|
||||||
import promptToString from 'common/util/promptToString';
|
import promptToString from 'common/util/promptToString';
|
||||||
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
||||||
|
import useSetBothPrompts from 'features/parameters/hooks/usePrompt';
|
||||||
import {
|
import {
|
||||||
setCfgScale,
|
setCfgScale,
|
||||||
setHeight,
|
setHeight,
|
||||||
@ -19,7 +20,6 @@ import {
|
|||||||
setInitialImage,
|
setInitialImage,
|
||||||
setMaskPath,
|
setMaskPath,
|
||||||
setPerlin,
|
setPerlin,
|
||||||
setPrompt,
|
|
||||||
setSampler,
|
setSampler,
|
||||||
setSeamless,
|
setSeamless,
|
||||||
setSeed,
|
setSeed,
|
||||||
@ -129,6 +129,8 @@ const ImageMetadataViewer = memo(
|
|||||||
({ image, styleClass }: ImageMetadataViewerProps) => {
|
({ image, styleClass }: ImageMetadataViewerProps) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const setBothPrompts = useSetBothPrompts();
|
||||||
|
|
||||||
useHotkeys('esc', () => {
|
useHotkeys('esc', () => {
|
||||||
dispatch(setShouldShowImageDetails(false));
|
dispatch(setShouldShowImageDetails(false));
|
||||||
});
|
});
|
||||||
@ -152,7 +154,6 @@ const ImageMetadataViewer = memo(
|
|||||||
seed,
|
seed,
|
||||||
steps,
|
steps,
|
||||||
strength,
|
strength,
|
||||||
denoise_str,
|
|
||||||
threshold,
|
threshold,
|
||||||
type,
|
type,
|
||||||
variations,
|
variations,
|
||||||
@ -189,8 +190,10 @@ const ImageMetadataViewer = memo(
|
|||||||
<MetadataItem
|
<MetadataItem
|
||||||
label="Prompt"
|
label="Prompt"
|
||||||
labelPosition="top"
|
labelPosition="top"
|
||||||
value={promptToString(prompt)}
|
value={
|
||||||
onClick={() => dispatch(setPrompt(prompt))}
|
typeof prompt === 'string' ? prompt : promptToString(prompt)
|
||||||
|
}
|
||||||
|
onClick={() => setBothPrompts(prompt)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{seed !== undefined && (
|
{seed !== undefined && (
|
||||||
|
26
invokeai/frontend/src/features/parameters/hooks/usePrompt.ts
Normal file
26
invokeai/frontend/src/features/parameters/hooks/usePrompt.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
||||||
|
|
||||||
|
import * as InvokeAI from 'app/invokeai';
|
||||||
|
import promptToString from 'common/util/promptToString';
|
||||||
|
import { useAppDispatch } from 'app/storeHooks';
|
||||||
|
import { setNegativePrompt, setPrompt } from '../store/generationSlice';
|
||||||
|
|
||||||
|
// TECHDEBT: We have two metadata prompt formats and need to handle recalling either of them.
|
||||||
|
// This hook provides a function to do that.
|
||||||
|
const useSetBothPrompts = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
return (inputPrompt: InvokeAI.Prompt) => {
|
||||||
|
const promptString =
|
||||||
|
typeof inputPrompt === 'string'
|
||||||
|
? inputPrompt
|
||||||
|
: promptToString(inputPrompt);
|
||||||
|
|
||||||
|
const [prompt, negativePrompt] = getPromptAndNegative(promptString);
|
||||||
|
|
||||||
|
dispatch(setPrompt(prompt));
|
||||||
|
dispatch(setNegativePrompt(negativePrompt));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSetBothPrompts;
|
Loading…
Reference in New Issue
Block a user