return prompt analysis without generating images
This commit is contained in:
parent
7ff6af2065
commit
1e3e81ed0b
@ -1662,6 +1662,56 @@ function buildAutoModeFallbackMessage(params: {
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function sanitizePromptOnlyUserIntent(text: string): string {
|
||||
return String(text || "")
|
||||
.replace(/先?分析(?:一下)?(?:这张)?(?:图片|图像|这张图)?/g, "")
|
||||
.replace(/帮我|给我|先给我|只给我|仅给我/g, "")
|
||||
.replace(/优化(?:一下)?提示词|提示词优化|提示词|prompt/gi, "")
|
||||
.replace(/不要生图|先别生图|不需要生图|不要生成|别生成|不直接生成/g, "")
|
||||
.replace(/[,。;:,.!!??]/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function buildPromptOnlyOptimizationSeed(params: {
|
||||
text: string;
|
||||
imageSummary?: string;
|
||||
}): string {
|
||||
const cleanedIntent = sanitizePromptOnlyUserIntent(params.text);
|
||||
const summary = String(params.imageSummary || "").replace(/\s+/g, " ").trim();
|
||||
|
||||
if (cleanedIntent && summary) {
|
||||
return `${cleanedIntent}。参考图关键特征:${summary}`;
|
||||
}
|
||||
if (summary) {
|
||||
return `基于参考图生成一段可直接复现核心主体、场景、风格与光线特征的中文提示词。参考图关键特征:${summary}`;
|
||||
}
|
||||
if (cleanedIntent) {
|
||||
return cleanedIntent;
|
||||
}
|
||||
return "基于当前参考图整理一段可直接使用的中文提示词。";
|
||||
}
|
||||
|
||||
function buildPromptOnlyReply(params: {
|
||||
userText: string;
|
||||
imageSummary?: string;
|
||||
optimizedPrompt: string;
|
||||
}): string {
|
||||
const demand = sanitizePromptOnlyUserIntent(params.userText) || "你希望我先分析这张图并整理可直接使用的提示词";
|
||||
const summary = String(params.imageSummary || "").replace(/\s+/g, " ").trim();
|
||||
|
||||
return [
|
||||
"【需求理解】",
|
||||
`- ${demand}`,
|
||||
"【图片分析】",
|
||||
summary ? `- ${summary}` : "- 我已基于当前图片梳理主体、场景、风格与光线特征。",
|
||||
"【优化后提示词】",
|
||||
`- ${params.optimizedPrompt.replace(/\s+/g, " ").trim()}`,
|
||||
"【使用建议】",
|
||||
"- 本轮仅输出优化后的提示词,不直接执行生图。",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function hasStructuredAgentSections(message?: string): boolean {
|
||||
if (!message) return false;
|
||||
return /(?:###\s*需求理解|【\s*需求理解\s*】|###\s*图片分析|【\s*图片分析\s*】|###\s*执行计划|【\s*执行计划\s*】|###\s*优化后提示词|【\s*优化后提示词\s*】)/.test(
|
||||
@ -4042,6 +4092,133 @@ export async function POST(request: Request) {
|
||||
const streamId = generateUUID();
|
||||
await createStreamId({ streamId, chatId: id });
|
||||
|
||||
const wantsPromptOnlyFromCurrentImage =
|
||||
shouldRunAgentConversation &&
|
||||
!smartAutoNeedsClarification &&
|
||||
requestResolvedMediaInput.currentMessageImageUrls.length > 0 &&
|
||||
(
|
||||
await import("@/lib/ai/smart-analyzer")
|
||||
).isExplicitPromptOptimizationRequest(requestTextContent);
|
||||
|
||||
if (wantsPromptOnlyFromCurrentImage) {
|
||||
let promptOnlyImageSummary = "";
|
||||
try {
|
||||
const { summarizeImageForExecutionReport } = await import("@/lib/ai/smart-analyzer");
|
||||
const summaryTimeoutMs = getBudgetedTimeout({
|
||||
startedAt: requestStartedAt,
|
||||
preferredMs: SMART_AUTO_IMAGE_SUMMARY_TIMEOUT_MS,
|
||||
minimumMs: 6_000,
|
||||
});
|
||||
if (summaryTimeoutMs) {
|
||||
const summaryAssetIds = Array.from(
|
||||
new Set([
|
||||
...extractMessageImageAssetIds(message.parts as any[]),
|
||||
...((contextPayload?.selectedAssetIds || []).filter(Boolean) as string[]),
|
||||
]),
|
||||
);
|
||||
const summary = await withOperationTimeout(
|
||||
summarizeImageForExecutionReport({
|
||||
text: requestTextContent,
|
||||
imageUrls: requestResolvedMediaInput.currentMessageImageUrls.slice(0, 4),
|
||||
context: typeof autoContext === "string" ? autoContext : undefined,
|
||||
preferredModelId: preferredAgentModelId,
|
||||
assetIds: summaryAssetIds,
|
||||
}),
|
||||
"promptOnlyImageSummary",
|
||||
summaryTimeoutMs,
|
||||
);
|
||||
if (summary?.trim()) {
|
||||
promptOnlyImageSummary = summary.trim();
|
||||
}
|
||||
}
|
||||
} catch (promptOnlySummaryError) {
|
||||
console.warn("[Chat API] Prompt-only image summary failed:", promptOnlySummaryError);
|
||||
}
|
||||
|
||||
let promptOnlyOptimizedPrompt = smartAutoOptimizedPrompt?.trim() || "";
|
||||
if (!promptOnlyOptimizedPrompt) {
|
||||
try {
|
||||
const { optimizePromptWithLLM, optimizePrompt } = await import("@/lib/ai/prompt-optimizer");
|
||||
const promptSeed = buildPromptOnlyOptimizationSeed({
|
||||
text: requestTextContent,
|
||||
imageSummary: promptOnlyImageSummary,
|
||||
});
|
||||
const optimizedResult = await optimizePromptWithLLM({
|
||||
originalPrompt: promptSeed,
|
||||
intent: "image_gen",
|
||||
historyMessages: smartAutoHistoryMessagesFromDb,
|
||||
context: typeof autoContext === "string" ? autoContext : undefined,
|
||||
}).catch(() =>
|
||||
optimizePrompt({
|
||||
originalPrompt: promptSeed,
|
||||
intent: "image_gen",
|
||||
}),
|
||||
);
|
||||
promptOnlyOptimizedPrompt = String(optimizedResult?.optimizedPrompt || "").trim();
|
||||
} catch (promptOnlyOptimizeError) {
|
||||
console.warn("[Chat API] Prompt-only optimization failed:", promptOnlyOptimizeError);
|
||||
}
|
||||
}
|
||||
|
||||
if (!promptOnlyOptimizedPrompt) {
|
||||
promptOnlyOptimizedPrompt = buildPromptOnlyOptimizationSeed({
|
||||
text: requestTextContent,
|
||||
imageSummary: promptOnlyImageSummary,
|
||||
});
|
||||
}
|
||||
|
||||
const promptOnlyMessage = buildPromptOnlyReply({
|
||||
userText: requestTextContent,
|
||||
imageSummary: promptOnlyImageSummary,
|
||||
optimizedPrompt: promptOnlyOptimizedPrompt,
|
||||
});
|
||||
|
||||
const promptOnlyStream = createUIMessageStream({
|
||||
execute: async ({ writer }) => {
|
||||
const messageId = generateUUID();
|
||||
writeAssistantTextChunk(writer, promptOnlyMessage, "prompt-only:reply", messageId);
|
||||
|
||||
await saveMessages({
|
||||
messages: [
|
||||
{
|
||||
id: messageId,
|
||||
chatId: id,
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: promptOnlyMessage }],
|
||||
attachments: [],
|
||||
createdAt: new Date(),
|
||||
videoStorageStatus: null,
|
||||
temporaryVideoUrl: null,
|
||||
permanentVideoUrl: null,
|
||||
archivedAt: null,
|
||||
videoMetadata: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
try {
|
||||
await upsertConversationWorkingState({
|
||||
userId: session.user.id,
|
||||
chatId: id,
|
||||
projectId: contextPayload?.projectId || null,
|
||||
currentIntent: "vision",
|
||||
lastMessageId: messageId,
|
||||
lastTurnSummary: {
|
||||
text: requestTextContent.slice(0, 500),
|
||||
resolutionMode: "prompt_only",
|
||||
confidence: 90,
|
||||
optimizedPrompt: promptOnlyOptimizedPrompt.slice(0, 1000),
|
||||
},
|
||||
});
|
||||
} catch (promptOnlyMemoryError) {
|
||||
console.warn("[Chat API] Failed to persist prompt-only response state:", promptOnlyMemoryError);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return createSseResponse(promptOnlyStream);
|
||||
}
|
||||
|
||||
if (shouldRunAgentConversation && smartAutoNeedsClarification) {
|
||||
const hasCurrentImageInput = message.parts.some((part: any) =>
|
||||
part?.type === "file" || part?.type === "image" || part?.type === "image_url"
|
||||
|
||||
@ -46,6 +46,26 @@ function isLikelyAdvisoryOrPlanningRequest(text: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
export function isExplicitPromptOptimizationRequest(text: string): boolean {
|
||||
const normalizedText = normalizeCompactText(text);
|
||||
if (!normalizedText) return false;
|
||||
|
||||
const asksPrompt =
|
||||
/提示词|prompt|文生图提示|出图提示|描述词|咒语|关键词/.test(normalizedText);
|
||||
if (!asksPrompt) return false;
|
||||
|
||||
const asksAnalysis =
|
||||
/分析|看图|看看|识别|拆解|提炼|整理|归纳|总结|描述|写出|给我|输出|优化|改写/.test(
|
||||
normalizedText,
|
||||
);
|
||||
const blocksExecution =
|
||||
/不要生图|先别生图|不需要生图|不要生成|别生成|仅给|只给|先给我|先出提示词|先分析/.test(
|
||||
normalizedText,
|
||||
);
|
||||
|
||||
return asksAnalysis || blocksExecution;
|
||||
}
|
||||
|
||||
function isExplicitVideoGenerationRequest(text: string): boolean {
|
||||
const normalizedText = normalizeCompactText(text);
|
||||
const hasStrongVideoMarkers =
|
||||
@ -112,6 +132,8 @@ function applyDirectExecutionPolicy(
|
||||
hasAnyRequestText && isLikelyAdvisoryOrPlanningRequest(params.text);
|
||||
const likelyVisionRequest = hasAnyRequestText && isLikelyVisionRequest(params.text);
|
||||
const likelyGeneralChatTurn = hasAnyRequestText && isLikelyGeneralChatTurn(params.text);
|
||||
const promptOptimizationOnlyRequest =
|
||||
hasAnyRequestText && isExplicitPromptOptimizationRequest(params.text);
|
||||
|
||||
if (explicitVideoGenerationRequest) {
|
||||
return {
|
||||
@ -130,6 +152,19 @@ function applyDirectExecutionPolicy(
|
||||
}
|
||||
|
||||
if (!hasUsableImage) {
|
||||
if (promptOptimizationOnlyRequest) {
|
||||
return {
|
||||
...result,
|
||||
intent: "chat",
|
||||
subIntent: undefined,
|
||||
confidence: Math.max(result.confidence, 0.82),
|
||||
needsClarification: false,
|
||||
clarifyingQuestions: [],
|
||||
conversationalReply:
|
||||
result.conversationalReply || "收到,我先按你的要求整理提示词,不直接执行生成。",
|
||||
};
|
||||
}
|
||||
|
||||
if (!explicitImageGenerationRequest && !explicitVideoGenerationRequest) {
|
||||
if (likelyAdvisoryRequest || likelyGeneralChatTurn || result.intent === "chat") {
|
||||
return {
|
||||
@ -174,6 +209,19 @@ function applyDirectExecutionPolicy(
|
||||
const keepClarification = Boolean(result.needsClarification);
|
||||
const explicitEditRequest = hasAnyRequestText && isLikelyImageEditRequest(params.text);
|
||||
|
||||
if (promptOptimizationOnlyRequest) {
|
||||
return {
|
||||
...baseResult,
|
||||
intent: "vision",
|
||||
subIntent: undefined,
|
||||
confidence: Math.max(baseResult.confidence, 0.88),
|
||||
needsClarification: false,
|
||||
clarifyingQuestions: [],
|
||||
conversationalReply:
|
||||
baseResult.conversationalReply || "收到,我先分析这张图并整理可直接使用的优化提示词,不会直接生图。",
|
||||
};
|
||||
}
|
||||
|
||||
if (!hasAnyRequestText) {
|
||||
return {
|
||||
...baseResult,
|
||||
@ -2102,6 +2150,14 @@ export function fallbackKeywordAnalysis(
|
||||
|
||||
// === 图片编辑场景(需要有图片) ===
|
||||
if (hasImage) {
|
||||
if (isExplicitPromptOptimizationRequest(text)) {
|
||||
return {
|
||||
intent: "vision",
|
||||
confidence: 0.92,
|
||||
conversationalReply: "收到,我先分析这张图并给你整理优化后的提示词,不直接生图。",
|
||||
};
|
||||
}
|
||||
|
||||
// 视频生成(图生视频/首尾帧等)
|
||||
if (isExplicitVideoGenerationRequest(text)) {
|
||||
return {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user