fix: route explicit canvas annotations

This commit is contained in:
134 2026-06-23 18:28:53 +08:00
parent cf879c63b6
commit e0dc5a3d77
2 changed files with 94 additions and 1 deletions

View File

@ -8,6 +8,7 @@ import {
} from "@/lib/canvas/agent-model-planner";
type ExplicitOperationKind =
| "add_annotation"
| "create_agent_slot"
| "create_text_node"
| "rename_node"
@ -332,6 +333,59 @@ function buildUpdateNoteOperation(params: {
};
}
function buildAddAnnotationOperation(params: {
context: CanvasAgentContext;
projectId: string;
messageId: string;
chatId: string;
userText: string;
requestedNodeIds?: string[];
}): CanvasAgentExplicitOperationPlan | null {
const text = params.userText.trim();
if (!/批注|标注|箭头|框选/i.test(text)) return null;
if (/按.*(?:批注|标注|箭头|框选).*(?:修改|编辑|改图|改主图|生成|出图|修图)/i.test(text)) {
return null;
}
if (hasMediaMutationIntent(text)) return null;
const annotationText = extractContentFromPatterns(
text,
[
/(?:添加批注|加批注|写批注|批注为|批注成|批注|添加标注|加标注|写标注|标注为|标注成|标注|箭头说明|框选说明)\s*[::]?\s*([\s\S]+)$/i,
/[::]\s*([\s\S]+)$/i,
],
1000
);
if (!annotationText) return null;
const targetNodeIds = getTargetNodeIds({
context: params.context,
requestedNodeIds: params.requestedNodeIds,
allowedTypes: ["image", "video"],
}).slice(0, 10);
if (targetNodeIds.length === 0) return null;
return {
operationPlan: {
...buildPlanBase({ ...params, operation: "add_annotation" }),
targetNodeIds,
textContent: annotationText,
annotations: [
{
type: "text-note",
text: annotationText,
color: "#f59e0b",
},
],
},
reply: "已给选中图片添加批注。",
progressTitle: "正在添加画布批注",
progressMessage: "我会把批注保存到选中的图片或视频节点。",
completedTitle: "画布批注已添加",
failedTitle: "画布批注添加失败",
};
}
export function buildCanvasAgentExplicitOperationPlan(params: {
context: CanvasAgentContext;
projectId: string;
@ -354,6 +408,7 @@ export function buildCanvasAgentExplicitOperationPlan(params: {
buildCreateAgentSlotOperation(commonParams) ||
buildCreateTextOperation(commonParams) ||
buildRenameOperation(commonParams) ||
buildUpdateNoteOperation(commonParams)
buildUpdateNoteOperation(commonParams) ||
buildAddAnnotationOperation(commonParams)
);
}

View File

@ -1020,6 +1020,34 @@ test.describe("canvas chat pure helpers", () => {
});
});
test("builds deterministic annotation additions for selected media nodes only", () => {
const outcome = buildCanvasAgentExplicitOperationPlan({
context: plannerContext,
projectId: plannerContext.projectId || "",
messageId: "message-1",
chatId: "chat-1",
userText: "给选中图片加批注:这里需要重点保留产品轮廓",
});
expect(outcome?.operationPlan).toMatchObject({
operation: "add_annotation",
targetNodeIds: ["image-1"],
textContent: "这里需要重点保留产品轮廓",
annotations: [
{
type: "text-note",
text: "这里需要重点保留产品轮廓",
color: "#f59e0b",
},
],
metadata: {
messageId: "message-1",
chatId: "chat-1",
planner: "deterministic",
},
});
});
test("does not route media generation or note-driven image edits as explicit canvas operations", () => {
expect(
buildCanvasAgentExplicitOperationPlan({
@ -1050,6 +1078,16 @@ test.describe("canvas chat pure helpers", () => {
userText: "新建一个视频槽位并生成片头",
})
).toBeNull();
expect(
buildCanvasAgentExplicitOperationPlan({
context: plannerContext,
projectId: plannerContext.projectId || "",
messageId: "message-1",
chatId: "chat-1",
userText: "按这张图上的批注修改",
})
).toBeNull();
});
test("normalizes model-planned annotations to selected media nodes", () => {