fix: keep canvas annotation operations idempotent
This commit is contained in:
parent
909f1c506c
commit
c0550988e0
@ -786,6 +786,13 @@ function applyAddAnnotationOperation(params: {
|
|||||||
const existingAnnotations = Array.isArray(metadata.annotations)
|
const existingAnnotations = Array.isArray(metadata.annotations)
|
||||||
? metadata.annotations.filter((item) => item && typeof item === "object")
|
? metadata.annotations.filter((item) => item && typeof item === "object")
|
||||||
: [];
|
: [];
|
||||||
|
const hasExistingOperationAnnotation = existingAnnotations.some(
|
||||||
|
(item) =>
|
||||||
|
(item as Record<string, unknown>).canvasAgentOperationId ===
|
||||||
|
params.plan.operationId
|
||||||
|
);
|
||||||
|
if (hasExistingOperationAnnotation) return;
|
||||||
|
|
||||||
const nextAnnotations = annotations.map((annotation, index) => ({
|
const nextAnnotations = annotations.map((annotation, index) => ({
|
||||||
...annotation,
|
...annotation,
|
||||||
id:
|
id:
|
||||||
|
|||||||
@ -688,6 +688,70 @@ test.describe("canvas agent operation mutation contract", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("adds annotation metadata to media nodes without duplicating retries", () => {
|
||||||
|
const target = buildImageNode("image-annotated");
|
||||||
|
const plan: CanvasOperationPlan = {
|
||||||
|
version: 1,
|
||||||
|
operationId: "op-add-annotation",
|
||||||
|
operation: "add_annotation",
|
||||||
|
projectId,
|
||||||
|
targetNodeIds: ["image-annotated"],
|
||||||
|
textContent: "背景换浅灰,主体保持不变",
|
||||||
|
annotations: [
|
||||||
|
{
|
||||||
|
type: "box",
|
||||||
|
text: "处理这块背景",
|
||||||
|
bounds: { x: 0.12, y: 0.2, width: 0.5, height: 0.32 },
|
||||||
|
color: "#f59e0b",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
metadata: {
|
||||||
|
messageId: "assistant-message-annotation",
|
||||||
|
chatId: "chat-1",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const first = applyCanvasOperationPlanToCanvasData({
|
||||||
|
canvasData: getCanvasData([target]),
|
||||||
|
plan,
|
||||||
|
});
|
||||||
|
const annotatedNode = getResultNodes(first.canvasData).find(
|
||||||
|
(node) => node.id === "image-annotated"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(first.createdNodeIds).toEqual([]);
|
||||||
|
expect(first.updatedNodeIds).toEqual(["image-annotated"]);
|
||||||
|
expect(annotatedNode).toMatchObject({
|
||||||
|
data: {
|
||||||
|
hasAnnotations: true,
|
||||||
|
metadata: {
|
||||||
|
annotations: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "op-add-annotation:annotation-image-annotated-1",
|
||||||
|
sourceNodeId: "image-annotated",
|
||||||
|
type: "box",
|
||||||
|
text: "处理这块背景",
|
||||||
|
bounds: { x: 0.12, y: 0.2, width: 0.5, height: 0.32 },
|
||||||
|
createdByAgent: true,
|
||||||
|
canvasAgentOperationId: "op-add-annotation",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const retry = applyCanvasOperationPlanToCanvasData({
|
||||||
|
canvasData: first.canvasData,
|
||||||
|
plan,
|
||||||
|
});
|
||||||
|
const retryNode = getResultNodes(retry.canvasData).find(
|
||||||
|
(node) => node.id === "image-annotated"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(retry.updatedNodeIds).toEqual([]);
|
||||||
|
expect(retryNode.data.metadata.annotations).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
test("keeps batch media writes idempotent by operation id", () => {
|
test("keeps batch media writes idempotent by operation id", () => {
|
||||||
const anchor = buildImageNode("image-anchor");
|
const anchor = buildImageNode("image-anchor");
|
||||||
const plan = {
|
const plan = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user