fix: close image placeholder on generation end
Emit generation end markers for success/failure and scope marker handling to the latest request so placeholders stop on errors.
This commit is contained in:
parent
35b190011a
commit
9fae58cfd3
@ -912,6 +912,16 @@ export async function POST(request: Request) {
|
||||
}),
|
||||
});
|
||||
|
||||
// 🆕 图片生成结束标记(成功)
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
id: generateUUID(),
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: "<!-- IMAGE_GENERATION_END -->" }],
|
||||
}),
|
||||
});
|
||||
|
||||
// 保存消息到数据库(现在是 S3 URL,可以完整保存)
|
||||
try {
|
||||
await saveMessages({
|
||||
@ -998,6 +1008,16 @@ export async function POST(request: Request) {
|
||||
}),
|
||||
});
|
||||
|
||||
// 🆕 图片生成结束标记(故障转移成功)
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
id: generateUUID(),
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: "<!-- IMAGE_GENERATION_END -->" }],
|
||||
}),
|
||||
});
|
||||
|
||||
// 保存消息到数据库
|
||||
try {
|
||||
await saveMessages({
|
||||
@ -1056,6 +1076,16 @@ export async function POST(request: Request) {
|
||||
}),
|
||||
});
|
||||
|
||||
// 🆕 图片生成结束标记(失败)
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
id: generateUUID(),
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: "<!-- IMAGE_GENERATION_END -->" }],
|
||||
}),
|
||||
});
|
||||
|
||||
// 尝试保存错误消息
|
||||
try {
|
||||
await saveMessages({
|
||||
@ -1271,6 +1301,16 @@ export async function POST(request: Request) {
|
||||
}),
|
||||
});
|
||||
|
||||
// 🆕 图片生成结束标记(成功)
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
id: generateUUID(),
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: "<!-- IMAGE_GENERATION_END -->" }],
|
||||
}),
|
||||
});
|
||||
|
||||
// 保存图片消息到数据库
|
||||
try {
|
||||
await saveMessages({
|
||||
@ -1322,6 +1362,16 @@ export async function POST(request: Request) {
|
||||
}),
|
||||
});
|
||||
|
||||
// 🆕 图片生成结束标记(失败)
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
id: generateUUID(),
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: "<!-- IMAGE_GENERATION_END -->" }],
|
||||
}),
|
||||
});
|
||||
|
||||
try {
|
||||
await saveMessages({
|
||||
messages: [
|
||||
@ -1502,6 +1552,16 @@ export async function POST(request: Request) {
|
||||
}),
|
||||
});
|
||||
|
||||
// 🆕 图片生成结束标记(成功)
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
id: generateUUID(),
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: "<!-- IMAGE_GENERATION_END -->" }],
|
||||
}),
|
||||
});
|
||||
|
||||
// 保存图片消息到数据库
|
||||
try {
|
||||
await saveMessages({
|
||||
@ -1552,6 +1612,16 @@ export async function POST(request: Request) {
|
||||
}),
|
||||
});
|
||||
|
||||
// 🆕 图片生成结束标记(失败)
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
id: generateUUID(),
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: "<!-- IMAGE_GENERATION_END -->" }],
|
||||
}),
|
||||
});
|
||||
|
||||
// 尝试保存错误消息
|
||||
try {
|
||||
await saveMessages({
|
||||
|
||||
@ -1065,13 +1065,20 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
};
|
||||
|
||||
const isLoading = status === 'streaming' || status === 'submitted';
|
||||
const hasGeneratingMarker = messages.some(msg =>
|
||||
const lastUserIndexForMarkers = messages.findLastIndex(m => m.role === "user");
|
||||
const messagesAfterLastUser = lastUserIndexForMarkers >= 0 ? messages.slice(lastUserIndexForMarkers + 1) : messages;
|
||||
const hasGenerationStart = messagesAfterLastUser.some(msg =>
|
||||
msg.role === "assistant" && msg.parts?.some(
|
||||
(part: any) => part.type === "text" && part.text?.includes("<!-- IMAGE_GENERATION_START -->")
|
||||
)
|
||||
);
|
||||
const hasGenerationEnd = messagesAfterLastUser.some(msg =>
|
||||
msg.role === "assistant" && msg.parts?.some(
|
||||
(part: any) => part.type === "text" && part.text?.includes("<!-- IMAGE_GENERATION_END -->")
|
||||
)
|
||||
);
|
||||
const currentModel = allModels.find(m => m.id === selectedModelId);
|
||||
const shouldShowImageLoading = hasGeneratingMarker && !!currentModel;
|
||||
const shouldShowImageLoading = hasGenerationStart && !hasGenerationEnd && !!currentModel;
|
||||
const loadingImageCount = imageParams.imageCount || 1;
|
||||
|
||||
return (
|
||||
@ -1184,6 +1191,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
text: part.text
|
||||
.replace(/\n*<!-- GENERATING_IMAGE -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_START -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_END -->\n*/g, "")
|
||||
.trim()
|
||||
};
|
||||
}
|
||||
|
||||
@ -311,6 +311,9 @@ export function Chat({
|
||||
const hasImageGenStartMarker = messageData.parts?.some(
|
||||
(part: any) => part.type === "text" && part.text?.includes("<!-- IMAGE_GENERATION_START -->")
|
||||
);
|
||||
const hasImageGenEndMarker = messageData.parts?.some(
|
||||
(part: any) => part.type === "text" && part.text?.includes("<!-- IMAGE_GENERATION_END -->")
|
||||
);
|
||||
const hasLegacyGeneratingMarker = messageData.parts?.some(
|
||||
(part: any) => part.type === "text" && part.text?.includes("<!-- GENERATING_IMAGE -->")
|
||||
);
|
||||
@ -318,11 +321,22 @@ export function Chat({
|
||||
if (part.type !== "text" || !part.text) return false;
|
||||
const cleanedText = part.text
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_START -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_END -->\n*/g, "")
|
||||
.replace(/\n*<!-- GENERATING_IMAGE -->\n*/g, "")
|
||||
.trim();
|
||||
return cleanedText.length === 0;
|
||||
});
|
||||
|
||||
if (hasImageGenEndMarker && isMarkerOnlyMessage) {
|
||||
console.log("[Chat] 🖼️ Image generation ended, hide placeholder");
|
||||
if (imagePlaceholderTimerRef.current) {
|
||||
clearTimeout(imagePlaceholderTimerRef.current);
|
||||
imagePlaceholderTimerRef.current = null;
|
||||
}
|
||||
setIsGeneratingImageFlag(false);
|
||||
return; // 不渲染标记消息
|
||||
}
|
||||
|
||||
if (hasImageGenStartMarker && isMarkerOnlyMessage) {
|
||||
console.log("[Chat] 🖼️ Image generation started, show placeholder");
|
||||
if (imagePlaceholderTimerRef.current) {
|
||||
|
||||
@ -177,11 +177,19 @@ function PureMessages({
|
||||
lastUserMessage?.parts?.some((p: any) => p.type === "file" || p.type === "image");
|
||||
|
||||
// 🆕 检查是否有"图片正在生成"的标记(表示对话消息已发送,正在等待图片生成)
|
||||
const isGeneratingImage = messages.some(msg =>
|
||||
const lastUserIndexForMarkers = messages.findLastIndex(m => m.role === "user");
|
||||
const messagesAfterLastUser = lastUserIndexForMarkers >= 0 ? messages.slice(lastUserIndexForMarkers + 1) : messages;
|
||||
const hasGenerationStart = messagesAfterLastUser.some(msg =>
|
||||
msg.role === "assistant" && msg.parts?.some(
|
||||
(part: any) => part.type === "text" && part.text?.includes("<!-- IMAGE_GENERATION_START -->")
|
||||
)
|
||||
);
|
||||
const hasGenerationEnd = messagesAfterLastUser.some(msg =>
|
||||
msg.role === "assistant" && msg.parts?.some(
|
||||
(part: any) => part.type === "text" && part.text?.includes("<!-- IMAGE_GENERATION_END -->")
|
||||
)
|
||||
);
|
||||
const isGeneratingImage = hasGenerationStart && !hasGenerationEnd;
|
||||
|
||||
// 对于图片模型,检查是否有图片内容(包含 ![thumb| 或 ![thumb||| 标记)
|
||||
// 🆕 在智能自动模式下,需要检查最后一条用户消息之后的所有 assistant 消息
|
||||
@ -322,6 +330,7 @@ function PureMessages({
|
||||
const cleanedText = part.text
|
||||
.replace(/\n*<!-- GENERATING_IMAGE -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_START -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_END -->\n*/g, "")
|
||||
.trim();
|
||||
return cleanedText.length > 0;
|
||||
})
|
||||
@ -423,6 +432,7 @@ function PureMessages({
|
||||
text: part.text
|
||||
.replace(/\n*<!-- GENERATING_IMAGE -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_START -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_END -->\n*/g, "")
|
||||
.trim()
|
||||
};
|
||||
}
|
||||
@ -494,6 +504,7 @@ function PureMessages({
|
||||
const cleanedText = part.text
|
||||
.replace(/\n*<!-- GENERATING_IMAGE -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_START -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_END -->\n*/g, "")
|
||||
.trim();
|
||||
return cleanedText.length > 0;
|
||||
})
|
||||
@ -535,17 +546,25 @@ function PureMessages({
|
||||
const userMessageHasImage = lastUserMsg?.parts?.some((p: any) => p.type === "file" || p.type === "image");
|
||||
|
||||
// 🆕 检查是否收到了 GENERATING_IMAGE 标记(表示对话已发送,开始生图)
|
||||
const hasGeneratingMarker = messages.some(msg =>
|
||||
const lastUserIndexForMarkers = messages.findLastIndex(m => m.role === "user");
|
||||
const messagesAfterLastUser = lastUserIndexForMarkers >= 0 ? messages.slice(lastUserIndexForMarkers + 1) : messages;
|
||||
const hasGeneratingMarker = messagesAfterLastUser.some(msg =>
|
||||
msg.role === "assistant" && msg.parts?.some(
|
||||
(part: any) => part.type === "text" && part.text?.includes("<!-- IMAGE_GENERATION_START -->")
|
||||
)
|
||||
);
|
||||
const hasGenerationEnd = messagesAfterLastUser.some(msg =>
|
||||
msg.role === "assistant" && msg.parts?.some(
|
||||
(part: any) => part.type === "text" && part.text?.includes("<!-- IMAGE_GENERATION_END -->")
|
||||
)
|
||||
);
|
||||
const hasAssistantTextReplyWithContent = messages.some(msg =>
|
||||
msg.role === "assistant" && msg.parts?.some((part: any) => {
|
||||
if (part.type !== "text" || !part.text) return false;
|
||||
const cleanedText = part.text
|
||||
.replace(/\n*<!-- GENERATING_IMAGE -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_START -->\n*/g, "")
|
||||
.replace(/\n*<!-- IMAGE_GENERATION_END -->\n*/g, "")
|
||||
.trim();
|
||||
return cleanedText.length > 0;
|
||||
})
|
||||
@ -560,7 +579,7 @@ function PureMessages({
|
||||
if (isAutoMode && userMessageHasImage) {
|
||||
// 🆕 只用 isGeneratingImageFlag 来控制是否显示生图占位图
|
||||
// isGeneratingImageFlag 由 chat.tsx 控制:延迟 3 秒后设置为 true,收到图片后设置为 false
|
||||
if (autoImagePlaceholderReady && isGeneratingImageFlag && hasAssistantTextReplyWithContent) {
|
||||
if (autoImagePlaceholderReady && isGeneratingImageFlag && hasAssistantTextReplyWithContent && !hasGenerationEnd) {
|
||||
return <ImageGenerationLoading imageCount={imageCount} maxImages={6} providerType="alibaba-image" modelName="智能处理" />;
|
||||
}
|
||||
// 否则显示思考动画(在等待 AI 分析或延迟期间)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user