fix: let canvas control prompts bypass image point gate
This commit is contained in:
parent
3760f7ff44
commit
76229dac7b
@ -110,6 +110,7 @@ import {
|
||||
shouldDeferCanvasAgentViewOperation,
|
||||
} from "@/lib/canvas/agent-view-interaction-guard";
|
||||
import { getCanvasAgentOperationErrorMessage } from "@/lib/canvas/agent-operation-errors";
|
||||
import { shouldBypassCanvasImagePointsPrecheckForAgentControl } from "@/lib/canvas/agent-client-intent";
|
||||
import { getCanvasSkillIconComponent } from "@/components/canvas/canvas-skill-icons";
|
||||
import { ImageParamsBar, type ImageGenerationParams } from "@/components/image-params-bar";
|
||||
import { VideoParamsBar, type VideoGenerationParams } from "@/components/video-params-bar";
|
||||
@ -8301,6 +8302,19 @@ export function CanvasChatPanel({
|
||||
|
||||
const ensureImagePointsBeforeSend = useCallback(() => {
|
||||
if (isVideoModel || activeComposerMode === "agent") return true;
|
||||
if (
|
||||
shouldBypassCanvasImagePointsPrecheckForAgentControl({
|
||||
text: input,
|
||||
activeComposerMode,
|
||||
attachmentCount: attachments.length,
|
||||
hasSkillTag: Boolean(selectedSkillTag),
|
||||
hasWorkflowContext,
|
||||
isDetailGenerationMode,
|
||||
agentEnabled: effectiveAgentEnabled,
|
||||
})
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (membershipSummary?.isHighVip) return true;
|
||||
|
||||
const pointsEnabled = membershipSummary?.pointsSystemConfig?.enabled === true;
|
||||
@ -8339,20 +8353,39 @@ export function CanvasChatPanel({
|
||||
return false;
|
||||
}, [
|
||||
activeComposerMode,
|
||||
attachments.length,
|
||||
currentModel?.imageGenConfig,
|
||||
effectiveAgentEnabled,
|
||||
hasWorkflowContext,
|
||||
imageParams?.imageCount,
|
||||
imageParams?.imageSize,
|
||||
input,
|
||||
isDetailGenerationMode,
|
||||
isVideoModel,
|
||||
membershipSummary?.isHighVip,
|
||||
membershipSummary?.pointsSystemConfig?.blockOnInsufficient,
|
||||
membershipSummary?.pointsSystemConfig?.enabled,
|
||||
membershipSummary?.summary?.currentCredits,
|
||||
selectedSkillTag,
|
||||
]);
|
||||
|
||||
const estimatedPointsCost = useMemo(() => {
|
||||
if (membershipSummary?.isHighVip) return null;
|
||||
if (membershipSummary?.pointsSystemConfig?.enabled !== true) return null;
|
||||
if (activeComposerMode === "agent") return null;
|
||||
if (
|
||||
shouldBypassCanvasImagePointsPrecheckForAgentControl({
|
||||
text: input,
|
||||
activeComposerMode,
|
||||
attachmentCount: attachments.length,
|
||||
hasSkillTag: Boolean(selectedSkillTag),
|
||||
hasWorkflowContext,
|
||||
isDetailGenerationMode,
|
||||
agentEnabled: effectiveAgentEnabled,
|
||||
})
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isVideoModel) {
|
||||
const cost = getGenerationCreditCostFromImageGenConfig(
|
||||
@ -8388,12 +8421,18 @@ export function CanvasChatPanel({
|
||||
: null;
|
||||
}, [
|
||||
activeComposerMode,
|
||||
attachments.length,
|
||||
currentModel?.imageGenConfig,
|
||||
effectiveAgentEnabled,
|
||||
hasWorkflowContext,
|
||||
imageParams?.imageCount,
|
||||
imageParams?.imageSize,
|
||||
input,
|
||||
isDetailGenerationMode,
|
||||
isVideoModel,
|
||||
membershipSummary?.isHighVip,
|
||||
membershipSummary?.pointsSystemConfig?.enabled,
|
||||
selectedSkillTag,
|
||||
videoParams?.duration,
|
||||
videoParams?.resolution,
|
||||
]);
|
||||
|
||||
48
lib/canvas/agent-client-intent.ts
Normal file
48
lib/canvas/agent-client-intent.ts
Normal file
@ -0,0 +1,48 @@
|
||||
export function normalizeCanvasAgentClientIntentText(value: unknown) {
|
||||
return String(value || "")
|
||||
.replace(/\u200b/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
const CANVAS_AGENT_IMAGE_GENERATION_OR_EDIT_REGEX =
|
||||
/(生成|生图|出图|画一张|来一张|做一张|做图|做视频|生成视频|视频生成|换背景|背景换|白底|去背景|删除背景|抠图|高清|增强|超清|超分|修图|重绘|扩图|扩展画幅|局部重绘|风格化|海报|主图|logo)/i;
|
||||
|
||||
const CANVAS_AGENT_VIEW_CONTROL_REGEX =
|
||||
/(看全局|看全部|缩小看|适配全部|适配画布|聚焦选中|聚焦结果|聚焦刚生成|focus selection|focus latest|fit all|zoom in|zoom out|pan to)|(画布|视角|镜头|viewport|canvas).{0,12}(放大|缩小|缩放|聚焦|定位|居中|看全局|看全部|移动|平移|拖动|zoom|fit|focus|pan)|(放大|缩小|缩放|聚焦|定位|居中|zoom|fit|focus).{0,12}(画布|视角|镜头|全局|全部|选中|选择|刚生成|结果|节点|对象|图片|图)/i;
|
||||
|
||||
const CANVAS_AGENT_SLOT_MANAGEMENT_REGEX =
|
||||
/(新建|创建|添加|插入|放置|加一个|建一个).{0,10}(图片|视频|ui|画面|空)?(槽位|slot|空框|占位框|框)|(槽位|slot|空框|占位框).{0,10}(新建|创建|添加|插入|放置)/i;
|
||||
|
||||
const CANVAS_AGENT_STRUCTURAL_OPERATION_REGEX =
|
||||
/(删除|移除|移动|挪动|排列|对齐|重命名|改名|连线|连接|添加批注|批注|备注|标注|添加文字|文字节点|更新文字|修改文字).{0,14}(画布|节点|对象|元素|图片|图|文本|便签|选中|这些|它们|这个)|(画布|节点|对象|元素|图片|图|文本|便签|选中|这些|它们|这个).{0,14}(删除|移除|移动|挪动|排列|对齐|重命名|改名|连线|连接|添加批注|批注|备注|标注|添加文字|文字节点|更新文字|修改文字)/i;
|
||||
|
||||
export function hasCanvasAgentNonGenerativeControlIntent(value: unknown) {
|
||||
const text = normalizeCanvasAgentClientIntentText(value);
|
||||
if (!text) return false;
|
||||
if (CANVAS_AGENT_IMAGE_GENERATION_OR_EDIT_REGEX.test(text)) return false;
|
||||
return (
|
||||
CANVAS_AGENT_VIEW_CONTROL_REGEX.test(text) ||
|
||||
CANVAS_AGENT_SLOT_MANAGEMENT_REGEX.test(text) ||
|
||||
CANVAS_AGENT_STRUCTURAL_OPERATION_REGEX.test(text)
|
||||
);
|
||||
}
|
||||
|
||||
export function shouldBypassCanvasImagePointsPrecheckForAgentControl(params: {
|
||||
text: unknown;
|
||||
activeComposerMode?: string | null;
|
||||
attachmentCount?: number;
|
||||
hasSkillTag?: boolean;
|
||||
hasWorkflowContext?: boolean;
|
||||
isDetailGenerationMode?: boolean;
|
||||
agentEnabled?: boolean;
|
||||
}) {
|
||||
if (params.activeComposerMode === "agent") return true;
|
||||
if (params.agentEnabled === false) return false;
|
||||
if ((params.attachmentCount || 0) > 0) return false;
|
||||
if (params.hasSkillTag || params.hasWorkflowContext || params.isDetailGenerationMode) {
|
||||
return false;
|
||||
}
|
||||
return hasCanvasAgentNonGenerativeControlIntent(params.text);
|
||||
}
|
||||
@ -114,6 +114,10 @@ import {
|
||||
buildCanvasSaveRequestBody,
|
||||
mergeCanvasSaveRevisionOptions,
|
||||
} from "@/lib/canvas/canvas-save-request";
|
||||
import {
|
||||
hasCanvasAgentNonGenerativeControlIntent,
|
||||
shouldBypassCanvasImagePointsPrecheckForAgentControl,
|
||||
} from "@/lib/canvas/agent-client-intent";
|
||||
import {
|
||||
applyCanvasOperationPlanToCanvasData,
|
||||
CanvasAgentMutationError,
|
||||
@ -227,6 +231,61 @@ test.describe("canvas chat pure helpers", () => {
|
||||
expect(isCanvasAgentViewOperationClientEnabled("1")).toBe(true);
|
||||
});
|
||||
|
||||
test("bypasses image point precheck only for non-generative canvas agent controls", () => {
|
||||
expect(hasCanvasAgentNonGenerativeControlIntent("把画布放大一点")).toBe(true);
|
||||
expect(hasCanvasAgentNonGenerativeControlIntent("缩小看全局")).toBe(true);
|
||||
expect(hasCanvasAgentNonGenerativeControlIntent("新建一个图片槽位")).toBe(true);
|
||||
expect(hasCanvasAgentNonGenerativeControlIntent("删除选中的节点")).toBe(true);
|
||||
expect(hasCanvasAgentNonGenerativeControlIntent("把选中图片背景换白")).toBe(false);
|
||||
expect(hasCanvasAgentNonGenerativeControlIntent("生成一张产品主图")).toBe(false);
|
||||
expect(hasCanvasAgentNonGenerativeControlIntent("抠图去背景")).toBe(false);
|
||||
|
||||
expect(
|
||||
shouldBypassCanvasImagePointsPrecheckForAgentControl({
|
||||
text: "把画布放大一点",
|
||||
activeComposerMode: "image",
|
||||
agentEnabled: true,
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldBypassCanvasImagePointsPrecheckForAgentControl({
|
||||
text: "把画布放大一点",
|
||||
activeComposerMode: "image",
|
||||
attachmentCount: 1,
|
||||
agentEnabled: true,
|
||||
})
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldBypassCanvasImagePointsPrecheckForAgentControl({
|
||||
text: "把画布放大一点",
|
||||
activeComposerMode: "image",
|
||||
hasWorkflowContext: true,
|
||||
agentEnabled: true,
|
||||
})
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldBypassCanvasImagePointsPrecheckForAgentControl({
|
||||
text: "把画布放大一点",
|
||||
activeComposerMode: "image",
|
||||
agentEnabled: false,
|
||||
})
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldBypassCanvasImagePointsPrecheckForAgentControl({
|
||||
text: "生成一张产品主图",
|
||||
activeComposerMode: "image",
|
||||
agentEnabled: true,
|
||||
})
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldBypassCanvasImagePointsPrecheckForAgentControl({
|
||||
text: "任意 agent 模式文本",
|
||||
activeComposerMode: "agent",
|
||||
agentEnabled: true,
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test("rejects stale autosave bases without blocking fresh or missing revisions", () => {
|
||||
const currentUpdatedAt = new Date("2026-06-22T00:00:05.500Z");
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user