修复画布Agent误澄清与输入标签污染并优化chat限流
This commit is contained in:
parent
118a2efe9a
commit
f0ede6f0c9
@ -939,6 +939,11 @@ export async function POST(request: Request) {
|
||||
.join(" ");
|
||||
autoTextForFallback = textContent;
|
||||
const autoContextText = typeof autoContext === "string" ? autoContext.trim() : "";
|
||||
const hasSelectedImageHint = /用户已选中\s*\d+\s*张图片/.test(autoContextText);
|
||||
const hasExplicitTargetHint = /第[一二三四五六七八九十\d]+张|左|右|上|下|中间|选中/.test(textContent);
|
||||
const hasActionableEditIntent = /改|调整|优化|换|替换|去掉|抠|重绘|增强|修|处理|按照|按|保留|统一|色差|背景|清晰|风格/.test(
|
||||
textContent
|
||||
);
|
||||
|
||||
// @ts-ignore - 检查各种可能的图片类型
|
||||
let hasImage = lastUserMessage.parts.some(
|
||||
@ -1084,6 +1089,29 @@ export async function POST(request: Request) {
|
||||
smartAutoDetectedIntent = autoGraphResult.analysis.intent;
|
||||
smartAutoNeedsClarification = Boolean(autoGraphResult.analysis.needsClarification);
|
||||
smartAutoClarificationQuestion = autoGraphResult.analysis.clarifyingQuestions?.[0];
|
||||
|
||||
if (
|
||||
smartAutoNeedsClarification &&
|
||||
hasImage &&
|
||||
hasActionableEditIntent &&
|
||||
(hasSelectedImageHint || hasExplicitTargetHint)
|
||||
) {
|
||||
smartAutoNeedsClarification = false;
|
||||
smartAutoClarificationQuestion = undefined;
|
||||
if (!smartAutoOptimizedPrompt && textContent.trim()) {
|
||||
smartAutoOptimizedPrompt = textContent.trim();
|
||||
}
|
||||
if (
|
||||
!smartAutoFriendlyMessage ||
|
||||
/[??]$/.test(smartAutoFriendlyMessage.trim()) ||
|
||||
/哪一张|哪个|请先确认/.test(smartAutoFriendlyMessage)
|
||||
) {
|
||||
smartAutoFriendlyMessage = "收到,我将按你指定的目标图直接执行。";
|
||||
}
|
||||
console.log(
|
||||
"[Smart Auto Mode] ✅ Skip clarification due to explicit target/selection context"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (autoGraphResult.friendlyMessage) {
|
||||
@ -1091,6 +1119,19 @@ export async function POST(request: Request) {
|
||||
console.log("[Smart Auto Mode] 💬 Conversational reply:", smartAutoFriendlyMessage);
|
||||
}
|
||||
|
||||
if (
|
||||
!smartAutoNeedsClarification &&
|
||||
hasImage &&
|
||||
hasActionableEditIntent &&
|
||||
(hasSelectedImageHint || hasExplicitTargetHint) &&
|
||||
smartAutoFriendlyMessage &&
|
||||
(/[??]$/.test(smartAutoFriendlyMessage.trim()) ||
|
||||
/哪一张|哪个|请先确认/.test(smartAutoFriendlyMessage))
|
||||
) {
|
||||
smartAutoFriendlyMessage = "收到,我将按你指定的目标图直接执行。";
|
||||
console.log("[Smart Auto Mode] ✅ Replace clarification-like friendly reply");
|
||||
}
|
||||
|
||||
const autoIntent = autoGraphResult.analysis?.intent;
|
||||
const isImageTask =
|
||||
autoIntent === "image_gen" ||
|
||||
|
||||
@ -1260,7 +1260,29 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
});
|
||||
|
||||
// 添加文本部分(用户可见)
|
||||
let textContent = input.trim() || (effectiveAttachments.length > 0 ? "请处理这张图片" : "");
|
||||
let textContent = input
|
||||
.replace(/\u200b/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
|
||||
if (textContent && effectiveAttachments.length > 0) {
|
||||
let strippedText = textContent;
|
||||
for (const attachment of effectiveAttachments) {
|
||||
const fullName = (attachment.name || "").trim();
|
||||
const shortName = fullName.length > 10 ? `${fullName.substring(0, 8)}...` : fullName;
|
||||
for (const candidate of [fullName, shortName]) {
|
||||
if (!candidate) continue;
|
||||
if (strippedText.startsWith(candidate)) {
|
||||
strippedText = strippedText.slice(candidate.length).trimStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
textContent = strippedText || textContent;
|
||||
}
|
||||
|
||||
if (!textContent && effectiveAttachments.length > 0) {
|
||||
textContent = "请处理这张图片";
|
||||
}
|
||||
|
||||
// 🆕 隐藏提示词(不在 UI 中展示)
|
||||
const hiddenSegments: string[] = [];
|
||||
@ -1968,16 +1990,15 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
className="min-h-[40px] max-h-[120px] overflow-y-auto text-sm outline-none empty:before:content-[attr(data-placeholder)] empty:before:text-muted-foreground/50"
|
||||
data-placeholder="请输入你的设计需求,例如:绿色椅子 替换 Image"
|
||||
onInput={(e) => {
|
||||
// 从 contenteditable 中提取纯文本(排除图片标签的 data-url)
|
||||
// 从 contenteditable 中提取纯文本(排除附件标签文本)
|
||||
const target = e.target as HTMLDivElement;
|
||||
// 获取所有文本节点的内容
|
||||
let textContent = '';
|
||||
const walker = document.createTreeWalker(target, NodeFilter.SHOW_TEXT, null);
|
||||
let node;
|
||||
while (node = walker.nextNode()) {
|
||||
textContent += node.textContent;
|
||||
}
|
||||
setInput(textContent.trim());
|
||||
const clone = target.cloneNode(true) as HTMLDivElement;
|
||||
clone.querySelectorAll("[data-attachment-url]").forEach((el) => el.remove());
|
||||
const textContent = (clone.textContent || "")
|
||||
.replace(/\u200b/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
setInput(textContent);
|
||||
syncAttachmentsWithEditor(target);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
|
||||
@ -76,6 +76,9 @@ class RateLimiter {
|
||||
export const rateLimiters = {
|
||||
// 通用API限制:每分钟100次请求
|
||||
api: new RateLimiter(60000, 100),
|
||||
|
||||
// 聊天接口限制:每分钟60次请求(避免被高频轮询接口挤占额度)
|
||||
chat: new RateLimiter(60000, 60),
|
||||
|
||||
// 严格限制:每分钟10次请求(用于敏感操作)
|
||||
strict: new RateLimiter(60000, 10),
|
||||
@ -117,4 +120,3 @@ export function checkRateLimit(
|
||||
const identifier = getClientIdentifier(request);
|
||||
return limiter.check(identifier);
|
||||
}
|
||||
|
||||
|
||||
14
proxy.ts
14
proxy.ts
@ -28,7 +28,19 @@ export async function proxy(request: NextRequest) {
|
||||
|
||||
// 🛡️ 安全检查3:速率限制
|
||||
// 对API路径应用速率限制
|
||||
if (pathname.startsWith("/api/")) {
|
||||
if (pathname === "/api/chat") {
|
||||
const chatRateLimit = checkRateLimit(request, rateLimiters.chat);
|
||||
if (!chatRateLimit.allowed) {
|
||||
console.warn(`[Security] Chat rate limit exceeded for IP: ${clientIP}`, {
|
||||
pathname,
|
||||
resetTime: new Date(chatRateLimit.resetTime).toISOString(),
|
||||
});
|
||||
return NextResponse.json(
|
||||
{ error: "Too many chat requests", retryAfter: Math.ceil((chatRateLimit.resetTime - Date.now()) / 1000) },
|
||||
{ status: 429 }
|
||||
);
|
||||
}
|
||||
} else if (pathname.startsWith("/api/")) {
|
||||
const rateLimit = checkRateLimit(request, rateLimiters.api);
|
||||
if (!rateLimit.allowed) {
|
||||
console.warn(`[Security] Rate limit exceeded for IP: ${clientIP}`, {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user