feat: 智能自动模式先对话再生图
修复内容: 1. 修复变量名 smartAutoHistoryImage -> smartAutoHistoryImages 2. 修复 model-router.ts 循环依赖问题 3. 智能自动模式显示占位图(当用户发送图片时) 4. 先发送对话消息(如:好的,我来帮你...),再生成图片 5. 让用户感受到是在和AI对话,而不是机器执行 流程: 用户发送请求 -> AI回复对话 -> 显示生图占位图 -> 显示生成结果 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6ccdd73c65
commit
6462fe2456
@ -418,7 +418,7 @@ export async function POST(request: Request) {
|
||||
}
|
||||
}
|
||||
|
||||
console.log("[Smart Auto Mode] Input:", { textContent: textContent.substring(0, 100), hasImage, imageCount: imageUrls.length, fromHistory: !!smartAutoHistoryImage });
|
||||
console.log("[Smart Auto Mode] Input:", { textContent: textContent.substring(0, 100), hasImage, imageCount: imageUrls.length, fromHistory: smartAutoHistoryImages.length > 0 });
|
||||
|
||||
// 2. 调用智能分析器
|
||||
const { analyzeIntent } = await import("@/lib/ai/smart-analyzer");
|
||||
@ -1019,10 +1019,12 @@ export async function POST(request: Request) {
|
||||
const imageParts = smartAutoHistoryImages.map((url) => ({
|
||||
type: "file" as const,
|
||||
url,
|
||||
mediaType: "image/png",
|
||||
mimeType: "image/png",
|
||||
}));
|
||||
return {
|
||||
...msg,
|
||||
// @ts-ignore - 类型兼容性问题,运行时正确
|
||||
parts: [...msg.parts, ...imageParts],
|
||||
};
|
||||
}
|
||||
@ -1030,6 +1032,36 @@ export async function POST(request: Request) {
|
||||
});
|
||||
}
|
||||
|
||||
// 🆕 如果是智能模式且有对话回复,先发送对话消息
|
||||
if (smartAutoFriendlyMessage) {
|
||||
const chatMessageId = generateUUID();
|
||||
console.log("[Chat API] 💬 Sending conversational reply first:", smartAutoFriendlyMessage);
|
||||
|
||||
// 写入对话消息到流
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
id: chatMessageId,
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: smartAutoFriendlyMessage }],
|
||||
}),
|
||||
});
|
||||
|
||||
// 保存对话消息到数据库
|
||||
await saveMessages({
|
||||
messages: [
|
||||
{
|
||||
id: chatMessageId,
|
||||
chatId: id,
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: smartAutoFriendlyMessage }],
|
||||
attachments: [],
|
||||
createdAt: new Date(),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// 转换消息(阿里生图支持 URL,会自动上传 base64)
|
||||
const modelMessages = await convertMessagesWithImages(messagesForAlibaba, "alibaba-image");
|
||||
|
||||
@ -1043,27 +1075,20 @@ export async function POST(request: Request) {
|
||||
const imageMarkdown = result.text || "图片生成失败";
|
||||
console.log("[Chat API] Alibaba generated, length:", imageMarkdown.length);
|
||||
|
||||
// 🆕 如果是智能模式,在图片前加上友好消息
|
||||
let finalMessage = imageMarkdown;
|
||||
if (smartAutoFriendlyMessage) {
|
||||
finalMessage = `${smartAutoFriendlyMessage}\n\n${imageMarkdown}`;
|
||||
console.log("[Chat API] Added friendly message:", smartAutoFriendlyMessage);
|
||||
}
|
||||
|
||||
// 生成消息 ID
|
||||
// 生成消息 ID(图片消息)
|
||||
const messageId = generateUUID();
|
||||
|
||||
// 写入消息到流
|
||||
// 写入图片消息到流
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
id: messageId,
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: finalMessage }],
|
||||
parts: [{ type: "text", text: imageMarkdown }],
|
||||
}),
|
||||
});
|
||||
|
||||
// 保存消息到数据库
|
||||
// 保存图片消息到数据库
|
||||
try {
|
||||
await saveMessages({
|
||||
messages: [
|
||||
@ -1071,7 +1096,7 @@ export async function POST(request: Request) {
|
||||
id: messageId,
|
||||
chatId: id,
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: finalMessage }],
|
||||
parts: [{ type: "text", text: imageMarkdown }],
|
||||
attachments: [],
|
||||
createdAt: new Date(),
|
||||
videoStorageStatus: null,
|
||||
@ -1175,6 +1200,36 @@ export async function POST(request: Request) {
|
||||
const volcanoStream = createUIMessageStream({
|
||||
execute: async ({ writer }) => {
|
||||
try {
|
||||
// 🆕 如果是智能模式且有对话回复,先发送对话消息
|
||||
if (smartAutoFriendlyMessage) {
|
||||
const chatMessageId = generateUUID();
|
||||
console.log("[Chat API] 💬 Volcano: Sending conversational reply first:", smartAutoFriendlyMessage);
|
||||
|
||||
// 写入对话消息到流
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
id: chatMessageId,
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: smartAutoFriendlyMessage }],
|
||||
}),
|
||||
});
|
||||
|
||||
// 保存对话消息到数据库
|
||||
await saveMessages({
|
||||
messages: [
|
||||
{
|
||||
id: chatMessageId,
|
||||
chatId: id,
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: smartAutoFriendlyMessage }],
|
||||
attachments: [],
|
||||
createdAt: new Date(),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// 转换消息,火山引擎支持 URL 和 base64
|
||||
const modelMessages = await convertMessagesWithImages(messagesForVolcano, "volcano");
|
||||
|
||||
@ -1187,10 +1242,10 @@ export async function POST(request: Request) {
|
||||
console.log("[Chat API] Volcano Vision generated, length:", imageMarkdown.length);
|
||||
console.log("[Chat API] Volcano Vision image markdown:", imageMarkdown.substring(0, 200));
|
||||
|
||||
// 生成一个统一的消息 ID
|
||||
// 生成一个统一的消息 ID(图片消息)
|
||||
const messageId = generateUUID();
|
||||
|
||||
// 写入消息到流 - 使用正确的 parts 格式
|
||||
// 写入图片消息到流
|
||||
writer.write({
|
||||
type: "data-appendMessage",
|
||||
data: JSON.stringify({
|
||||
@ -1200,7 +1255,7 @@ export async function POST(request: Request) {
|
||||
}),
|
||||
});
|
||||
|
||||
// 保存消息到数据库
|
||||
// 保存图片消息到数据库
|
||||
try {
|
||||
await saveMessages({
|
||||
messages: [
|
||||
|
||||
@ -167,21 +167,25 @@ function PureMessages({
|
||||
);
|
||||
|
||||
// 判断是否是图片生成模型(包括 Gemini、火山、阿里)
|
||||
// 注意:智能自动模式不在这里判断,因为后端还在分析意图,可能最终是对话而不是生图
|
||||
const isImageGenModel = isGeminiModel || isVolcanoVisionModel || isAlibabaImageModel;
|
||||
|
||||
// 🆕 智能自动模式:检查用户消息是否包含图片,如果有则视为图片编辑场景
|
||||
const isAutoModeWithImage = selectedModelId === "auto" && lastMessage?.role === "user" &&
|
||||
lastMessage.parts?.some((p: any) => p.type === "file" || p.type === "image");
|
||||
|
||||
// 根据模型类型判断是否有有效内容
|
||||
let hasContent = false;
|
||||
if (isQianwenVideoModel || isVideoModel) {
|
||||
hasContent = hasVideoContent ?? false; // 视频模型:只有真正的视频才算有内容
|
||||
} else if (isImageGenModel) {
|
||||
hasContent = hasImageContent ?? false; // 图片模型:只有图片才算有内容
|
||||
} else if (isImageGenModel || isAutoModeWithImage) {
|
||||
hasContent = hasImageContent ?? false; // 图片模型或智能自动+图片:只有图片才算有内容
|
||||
} else {
|
||||
hasContent = hasTextContent ?? false; // 其他模型:任何文本都算有内容
|
||||
}
|
||||
|
||||
// 根据模型类型设置延迟时间(视频模型 60 秒,图片模型 4 秒,其他 6 秒)
|
||||
const delayTime = (isQianwenVideoModel || isVideoModel) ? 60000 : isImageGenModel ? 4000 : 6000;
|
||||
// 🆕 智能自动模式如果有图片,也按图片模型处理
|
||||
const delayTime = (isQianwenVideoModel || isVideoModel) ? 60000 : (isImageGenModel || isAutoModeWithImage) ? 4000 : 6000;
|
||||
|
||||
// 如果是 submitted 状态,立即显示
|
||||
if (status === "submitted") {
|
||||
@ -314,12 +318,21 @@ function PureMessages({
|
||||
const showAlibabaImage = modelType === "alibaba-image" || (!modelType && isAlibabaImageModel);
|
||||
const showVideo = modelType === "volcano-video" || modelType === "qianwen-video" || (!modelType && (isQianwenVideoModel || isVideoModel));
|
||||
|
||||
// 🆕 检查智能自动模式是否有图片(需要显示生图占位图)
|
||||
const isAutoMode = selectedModelId === "auto";
|
||||
const userMessageHasImage = messages.length > 0 &&
|
||||
messages[messages.length - 1]?.role === "user" &&
|
||||
messages[messages.length - 1]?.parts?.some((p: any) => p.type === "file" || p.type === "image");
|
||||
const showAutoModeImage = isAutoMode && userMessageHasImage;
|
||||
|
||||
if (showVideo) return <QianwenVideoLoading />;
|
||||
if (showGemini) return <GeminiImageLoading imageCount={imageCount} />;
|
||||
if (showVolcano) return <VolcanoVisionLoading imageCount={imageCount} />;
|
||||
// 🆕 阿里生图模型使用通用组件,橙色主题,最多 6 张,传递自定义模型名称
|
||||
if (showAlibabaImage) return <ImageGenerationLoading imageCount={imageCount} maxImages={6} providerType="alibaba-image" modelName={currentModelDisplayName} />;
|
||||
// 智能自动模式会先显示通用思考动画,等后端确定模型后自然切换
|
||||
// 🆕 智能自动模式:如果用户发送了图片,显示智能处理占位图
|
||||
if (showAutoModeImage) return <ImageGenerationLoading imageCount={imageCount} maxImages={6} providerType="alibaba-image" modelName="智能处理" />;
|
||||
// 其他情况显示通用思考动画
|
||||
return <ThinkingMessage />;
|
||||
})()}
|
||||
|
||||
|
||||
@ -5,7 +5,8 @@
|
||||
* 核心逻辑:图片编辑场景优先使用 alibaba-image (灵魂4.5)
|
||||
*/
|
||||
|
||||
import { getEnabledAiModelsWithProvider } from "@/lib/db/queries";
|
||||
// 使用动态导入避免循环依赖
|
||||
// import { getEnabledAiModelsWithProvider } from "@/lib/db/queries";
|
||||
|
||||
// 路由结果类型
|
||||
export interface RouteResult {
|
||||
@ -107,6 +108,9 @@ export async function routeToModel(params: {
|
||||
|
||||
console.log("[Model Router] Routing:", { intent, subIntent, hasImage });
|
||||
|
||||
// 使用动态导入避免循环依赖
|
||||
const { getEnabledAiModelsWithProvider } = await import("@/lib/db/queries");
|
||||
|
||||
// 获取所有可用模型
|
||||
const allModels = await getEnabledAiModelsWithProvider();
|
||||
console.log("[Model Router] Available models:", allModels.length);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user