fix(chat): prevent image placeholder from sticking after generation
This commit is contained in:
parent
73d0d9d8c2
commit
b5991d2a2a
@ -75,14 +75,43 @@ function sortMessagesByCreatedAt(messages: ChatMessage[]): ChatMessage[] {
|
||||
|
||||
function hasImageMarkdown(parts?: Array<any>) {
|
||||
if (!parts || parts.length === 0) return false;
|
||||
return parts.some(
|
||||
(part) =>
|
||||
return parts.some((part) => {
|
||||
if (!part) return false;
|
||||
|
||||
if (
|
||||
part?.type === "text" &&
|
||||
typeof part.text === "string" &&
|
||||
(part.text.includes("![thumb|") ||
|
||||
part.text.includes("![thumb|||") ||
|
||||
part.text.includes("![Generated Image"))
|
||||
);
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (part?.type === "image" && typeof part.url === "string" && part.url.length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
part?.type === "file" &&
|
||||
typeof part.url === "string" &&
|
||||
((typeof part.mediaType === "string" && part.mediaType.startsWith("image/")) ||
|
||||
/\.(png|jpe?g|webp|gif|bmp|svg)(\?|$)/i.test(part.url))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function getMessageTextLength(message?: ChatMessage | null) {
|
||||
if (!message?.parts || message.parts.length === 0) return 0;
|
||||
return message.parts
|
||||
.filter((part: any) => part?.type === "text" && typeof part.text === "string")
|
||||
.map((part: any) => part.text.trim())
|
||||
.join("\n")
|
||||
.length;
|
||||
}
|
||||
|
||||
export function Chat({
|
||||
@ -465,7 +494,7 @@ export function Chat({
|
||||
content: messageData.parts?.[0]?.text || "",
|
||||
createdAt: new Date(),
|
||||
parts: messageData.parts || [],
|
||||
attachments: [],
|
||||
attachments: Array.isArray(messageData.attachments) ? messageData.attachments : [],
|
||||
} as ChatMessage;
|
||||
|
||||
setAppendedMessages((prev) => {
|
||||
@ -562,6 +591,15 @@ export function Chat({
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[Chat] Failed to reload messages:", error);
|
||||
} finally {
|
||||
// 兜底:即使消息重载失败,也不能让占位图卡死在处理中状态
|
||||
if (
|
||||
imageGenerationPhaseRef.current === "queued" ||
|
||||
imageGenerationPhaseRef.current === "processing" ||
|
||||
imageGenerationPhaseRef.current === "finalizing"
|
||||
) {
|
||||
updateImageGenerationPhase("idle");
|
||||
}
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
@ -1450,8 +1488,25 @@ export function Chat({
|
||||
|
||||
// 3. 最后添加 appendedMessages(data-appendMessage 的消息)
|
||||
appendedMessages.forEach(m => {
|
||||
if (!messageMap.has(m.id)) {
|
||||
const existing = messageMap.get(m.id);
|
||||
if (!existing) {
|
||||
messageMap.set(m.id, m);
|
||||
return;
|
||||
}
|
||||
|
||||
const existingHasImage = hasImageMarkdown((existing as any).parts);
|
||||
const nextHasImage = hasImageMarkdown((m as any).parts);
|
||||
|
||||
const shouldReplace =
|
||||
(nextHasImage && !existingHasImage) ||
|
||||
getMessageTextLength(m) > getMessageTextLength(existing);
|
||||
|
||||
if (shouldReplace) {
|
||||
messageMap.set(m.id, {
|
||||
...existing,
|
||||
...m,
|
||||
createdAt: (existing as any).createdAt || (m as any).createdAt,
|
||||
} as ChatMessage);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -153,6 +153,33 @@ function PureMessages({
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_START -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_END -->\n*/g, "")
|
||||
.trim();
|
||||
const hasImageContentInMessage = (message?: ChatMessage | null) => {
|
||||
if (!message?.parts || message.parts.length === 0) return false;
|
||||
return message.parts.some((part: any) => {
|
||||
if (part?.type === "text" && typeof part.text === "string") {
|
||||
return (
|
||||
part.text.includes("![thumb|") ||
|
||||
part.text.includes("![thumb|||") ||
|
||||
part.text.includes("![Generated Image")
|
||||
);
|
||||
}
|
||||
|
||||
if (part?.type === "image" && typeof part.url === "string" && part.url.length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
part?.type === "file" &&
|
||||
typeof part.url === "string" &&
|
||||
((typeof part.mediaType === "string" && part.mediaType.startsWith("image/")) ||
|
||||
/\.(png|jpe?g|webp|gif|bmp|svg)(\?|$)/i.test(part.url))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@ -234,22 +261,10 @@ function PureMessages({
|
||||
const lastUserIndex = messages.findLastIndex(m => m.role === "user");
|
||||
// 检查之后所有的 assistant 消息是否包含图片
|
||||
const assistantMessagesAfterUser = messages.slice(lastUserIndex + 1).filter(m => m.role === "assistant");
|
||||
hasImageContent = assistantMessagesAfterUser.some(msg =>
|
||||
msg.parts?.some((part: any) => part.type === "text" && part.text && (
|
||||
part.text.includes("![thumb|") ||
|
||||
part.text.includes("![thumb|||") ||
|
||||
part.text.includes("![Generated Image")
|
||||
))
|
||||
);
|
||||
hasImageContent = assistantMessagesAfterUser.some((msg) => hasImageContentInMessage(msg));
|
||||
} else {
|
||||
// 普通模式:只检查最后一条消息
|
||||
hasImageContent = lastMessage?.parts && lastMessage.parts.some(
|
||||
(part: any) => part.type === "text" && part.text && (
|
||||
part.text.includes("![thumb|") ||
|
||||
part.text.includes("![thumb|||") ||
|
||||
part.text.includes("![Generated Image")
|
||||
)
|
||||
) || false;
|
||||
hasImageContent = hasImageContentInMessage(lastMessage);
|
||||
}
|
||||
|
||||
// 对于其他模型,检查是否有实际的文本内容
|
||||
@ -373,14 +388,7 @@ function PureMessages({
|
||||
);
|
||||
|
||||
const hasImageContent = assistantMessagesAfterUser.some((msg) =>
|
||||
msg.parts?.some(
|
||||
(part: any) =>
|
||||
part.type === "text" &&
|
||||
part.text &&
|
||||
(part.text.includes("![thumb|") ||
|
||||
part.text.includes("![thumb|||") ||
|
||||
part.text.includes("![Generated Image"))
|
||||
)
|
||||
hasImageContentInMessage(msg)
|
||||
);
|
||||
const hasGenerationStart = assistantMessagesAfterUser.some((msg) =>
|
||||
msg.parts?.some(
|
||||
@ -602,14 +610,7 @@ function PureMessages({
|
||||
? messages.slice(lastUserIndexForMarkers + 1).filter((msg) => msg.role === "assistant")
|
||||
: messages.filter((msg) => msg.role === "assistant");
|
||||
const hasImageContent = assistantMessagesAfterUser.some((msg) =>
|
||||
msg.parts?.some(
|
||||
(part: any) =>
|
||||
part.type === "text" &&
|
||||
part.text &&
|
||||
(part.text.includes("![thumb|") ||
|
||||
part.text.includes("![thumb|||") ||
|
||||
part.text.includes("![Generated Image"))
|
||||
)
|
||||
hasImageContentInMessage(msg)
|
||||
);
|
||||
const hasAssistantTextReplyWithContent = assistantMessagesAfterUser.some((msg) =>
|
||||
msg.parts?.some((part: any) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user