fix: route explicit canvas slot creation
This commit is contained in:
parent
06834699be
commit
cf879c63b6
@ -4064,6 +4064,55 @@ export async function POST(request: Request) {
|
||||
const canvasExplicitOperationPlan = resolveCanvasExplicitOperationPlan();
|
||||
if (canvasExplicitOperationPlan) {
|
||||
const { operationPlan } = canvasExplicitOperationPlan;
|
||||
if (
|
||||
operationPlan.operation === "create_agent_slot" &&
|
||||
!canvasAgentFeatures.slotEnabled
|
||||
) {
|
||||
const canvasSlotDisabledStream = createUIMessageStream({
|
||||
execute: async ({ writer }) => {
|
||||
const messageId = generateUUID();
|
||||
const slotDisabledReply = "当前画布 Agent Slot 能力暂未开启。";
|
||||
writeAgentProgressChunk(
|
||||
writer,
|
||||
{
|
||||
stage: "completed",
|
||||
currentStep: "finalize",
|
||||
title: "画布槽位未创建",
|
||||
message: slotDisabledReply,
|
||||
toolName: "canvasOperation",
|
||||
capability: operationPlan.operation,
|
||||
},
|
||||
"canvas-explicit:slot-disabled",
|
||||
);
|
||||
writeAssistantTextChunk(
|
||||
writer,
|
||||
slotDisabledReply,
|
||||
"canvas-explicit:slot-disabled-reply",
|
||||
messageId,
|
||||
);
|
||||
await saveMessages({
|
||||
messages: [
|
||||
{
|
||||
id: messageId,
|
||||
chatId: id,
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: slotDisabledReply }],
|
||||
attachments: [],
|
||||
createdAt: new Date(),
|
||||
videoStorageStatus: null,
|
||||
temporaryVideoUrl: null,
|
||||
permanentVideoUrl: null,
|
||||
archivedAt: null,
|
||||
videoMetadata: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return createSseResponse(canvasSlotDisabledStream);
|
||||
}
|
||||
|
||||
const canvasExplicitStream = createUIMessageStream({
|
||||
execute: async ({ writer }) => {
|
||||
const messageId = generateUUID();
|
||||
|
||||
@ -2,8 +2,16 @@ import type {
|
||||
CanvasAgentContext,
|
||||
CanvasOperationPlan,
|
||||
} from "@/types/canvas/agent-protocol";
|
||||
import {
|
||||
buildCanvasAgentSlotCreationPlan,
|
||||
resolveCanvasAgentSlotCreationIntent,
|
||||
} from "@/lib/canvas/agent-model-planner";
|
||||
|
||||
type ExplicitOperationKind = "create_text_node" | "rename_node" | "update_note";
|
||||
type ExplicitOperationKind =
|
||||
| "create_agent_slot"
|
||||
| "create_text_node"
|
||||
| "rename_node"
|
||||
| "update_note";
|
||||
|
||||
export type CanvasAgentExplicitOperationPlan = {
|
||||
operationPlan: CanvasOperationPlan;
|
||||
@ -190,6 +198,49 @@ function buildCreateTextOperation(params: {
|
||||
};
|
||||
}
|
||||
|
||||
function getSlotTypeLabel(slotType: "image" | "video" | "ui-screen") {
|
||||
if (slotType === "video") return "视频";
|
||||
if (slotType === "ui-screen") return "UI";
|
||||
return "图片";
|
||||
}
|
||||
|
||||
function buildCreateAgentSlotOperation(params: {
|
||||
context: CanvasAgentContext;
|
||||
projectId: string;
|
||||
messageId: string;
|
||||
chatId: string;
|
||||
userText: string;
|
||||
}): CanvasAgentExplicitOperationPlan | null {
|
||||
const intent = resolveCanvasAgentSlotCreationIntent({
|
||||
userText: params.userText,
|
||||
});
|
||||
if (!intent.shouldCreate || intent.combinedWithMediaExecution) return null;
|
||||
|
||||
const operationPlan = buildCanvasAgentSlotCreationPlan({
|
||||
...params,
|
||||
slotType: intent.slotType,
|
||||
slotLabel: intent.label,
|
||||
aspectRatio: intent.aspectRatio,
|
||||
});
|
||||
if (!operationPlan) return null;
|
||||
|
||||
const slotTypeLabel = getSlotTypeLabel(intent.slotType);
|
||||
return {
|
||||
operationPlan: {
|
||||
...operationPlan,
|
||||
metadata: {
|
||||
...(operationPlan.metadata || {}),
|
||||
planner: "deterministic",
|
||||
},
|
||||
},
|
||||
reply: `已在当前画布创建${slotTypeLabel}槽位。`,
|
||||
progressTitle: "正在创建画布槽位",
|
||||
progressMessage: `我会在当前视口中心创建一个空${slotTypeLabel}槽位。`,
|
||||
completedTitle: "画布槽位已创建",
|
||||
failedTitle: "画布槽位创建失败",
|
||||
};
|
||||
}
|
||||
|
||||
function buildRenameOperation(params: {
|
||||
context: CanvasAgentContext;
|
||||
projectId: string;
|
||||
@ -300,6 +351,7 @@ export function buildCanvasAgentExplicitOperationPlan(params: {
|
||||
}
|
||||
|
||||
return (
|
||||
buildCreateAgentSlotOperation(commonParams) ||
|
||||
buildCreateTextOperation(commonParams) ||
|
||||
buildRenameOperation(commonParams) ||
|
||||
buildUpdateNoteOperation(commonParams)
|
||||
|
||||
@ -944,6 +944,40 @@ test.describe("canvas chat pure helpers", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("builds deterministic agent slot creation plans from explicit canvas prompts", () => {
|
||||
const context: CanvasAgentContext = {
|
||||
...plannerContext,
|
||||
viewport: { x: 0, y: 0, zoom: 1, width: 1000, height: 800 },
|
||||
focusPolicy: "auto",
|
||||
};
|
||||
const outcome = buildCanvasAgentExplicitOperationPlan({
|
||||
context,
|
||||
projectId: plannerContext.projectId || "",
|
||||
messageId: "message-1",
|
||||
chatId: "chat-1",
|
||||
userText: "新建一个 9:16 视频槽位",
|
||||
});
|
||||
|
||||
expect(outcome?.operationPlan).toMatchObject({
|
||||
operation: "create_agent_slot",
|
||||
targetPosition: { x: 320, y: 290 },
|
||||
slot: {
|
||||
slotType: "video",
|
||||
label: "视频槽位",
|
||||
aspectRatio: "9:16",
|
||||
fillPolicy: "replace-empty",
|
||||
acceptedSources: ["text", "image", "selection", "annotation"],
|
||||
},
|
||||
preserveOriginal: true,
|
||||
metadata: {
|
||||
messageId: "message-1",
|
||||
chatId: "chat-1",
|
||||
planner: "deterministic",
|
||||
},
|
||||
});
|
||||
expect(outcome?.reply).toContain("视频槽位");
|
||||
});
|
||||
|
||||
test("builds deterministic node rename plans without treating titles as image edits", () => {
|
||||
const outcome = buildCanvasAgentExplicitOperationPlan({
|
||||
context: plannerContext,
|
||||
@ -1006,6 +1040,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", () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user