ai-chatbot/lib/ai/canvas-agent-runtime/capability-access.ts

268 lines
9.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { isZcodeWorkspaceAuthorized } from "./authorization";
import { CANVAS_CAPABILITY_REGISTRY } from "@/lib/canvas/capabilities/registry";
import { DEFAULT_CANVAS_TOOLBAR_ITEMS } from "@/lib/canvas-toolbar-config";
import {
defaultVideoCanvasFunctionConfig,
type VideoCanvasFunctionConfig,
} from "@/lib/video-canvas/config";
export type AgentCapabilityAccess = {
id: string;
workspace: "canvas" | "video";
title: string;
authorized: boolean;
previouslyExposed: boolean;
execution: "server" | "interactive" | "backend_required";
entry: string;
note?: string;
};
// The full audit manifest is for the report/API. Repeating audit metadata and UI
// descriptions in model history can exhaust context before the first edit.
export function summarizeAgentCapabilities(items: AgentCapabilityAccess[]) {
return items.map(({ id, title, execution, entry, note, authorized }) => ({
id,
title,
authorized,
execution,
...(authorized && execution === "server" ? { entry } : {}),
...(execution === "backend_required" || !authorized ? { note } : {}),
}));
}
export const VIDEO_CANVAS_EXTRA_ACTIONS = [
["rename", "节点重命名"],
["move", "节点移动"],
["duplicate", "复制节点"],
["delete", "删除节点"],
["connect", "工作流连线"],
["disconnect", "移除连线"],
["note", "节点批注"],
["text", "编辑文本"],
["create-text", "添加文本节点"],
["arrange", "排列节点"],
["viewport", "画布缩放与定位"],
] as const;
export const VIDEO_CANVAS_INTERACTIVE_ACTIONS = [
["upload-image", "上传图片"],
["upload-video", "上传视频"],
["upload-audio", "上传音频"],
["download", "下载素材"],
["preview", "预览素材"],
["local-edit", "局部编辑"],
["annotate", "标注"],
["video-hd", "视频高清"],
["video-clip", "选择视频剪辑范围"],
["video-crop", "视频裁剪面板"],
["subtitle-erase", "去字幕面板"],
["audio-split", "音视频分离面板"],
["voice-split", "人声分离面板"],
["background-split", "背景音分离面板"],
["director", "导演台"],
["storyboard", "分镜工作台"],
["batch-storyboard", "批量生成分镜"],
["batch-video", "批量生成视频"],
["assets", "素材库"],
["roles", "角色库"],
["styles", "风格库"],
["effects", "特效库"],
["history", "生成历史"],
["workflows", "工作流模板"],
["save", "保存画布"],
["export", "导出画布"],
["import", "导入画布"],
["share", "分享设置"],
["publish", "发布设置"],
["skills", "视频技能"],
["help", "帮助与快捷键"],
["frame-first", "提取首帧"],
["frame-last", "提取尾帧"],
["frame-current", "提取指定时间帧"],
] as const;
export const IMAGE_CANVAS_OPERATIONS = [
"create_agent_slot",
"create_text_node",
"fill_slot",
"insert_beside",
"append_revision",
"replace_node_media",
"add_annotation",
"link_nodes",
"move_nodes",
"delete_nodes",
"rename_node",
"update_note",
"update_text",
] as const;
export function isExplicitDestructiveIntent(text: string) {
return (
/删除|移除|清空|覆盖|替换原图/.test(text) &&
!/(?:不要|不许|禁止|别|不需要).{0,12}(?:删除|移除|清空|覆盖)/.test(text)
);
}
const imageOperationTitles: Record<string, string> = {
create_agent_slot: "创建输出槽位",
create_text_node: "创建文本节点",
fill_slot: "填充输出槽位",
insert_beside: "在素材旁添加结果",
append_revision: "追加素材版本",
replace_node_media: "替换节点媒体",
add_annotation: "添加标注",
link_nodes: "节点连线",
move_nodes: "移动节点",
delete_nodes: "删除节点",
rename_node: "重命名节点",
update_note: "更新批注",
update_text: "更新文本",
};
export function getCanvasAgentAccessManifest(
videoConfig: VideoCanvasFunctionConfig = defaultVideoCanvasFunctionConfig,
): AgentCapabilityAccess[] {
return [
...CANVAS_CAPABILITY_REGISTRY.map(
(capability): AgentCapabilityAccess => ({
id: capability.id,
workspace: "canvas",
title: capability.title,
authorized: true,
previouslyExposed:
!["image.add_to_chat", "image.layout_replace", "image.download"].includes(
capability.id,
) &&
capability.agent.exposed &&
capability.executionMode !== "local" &&
!capability.id.includes("3d") &&
capability.risk !== "destructive",
execution:
capability.executionMode === "interactive" ||
capability.executionMode === "local" ||
[
"image.text_edit",
"image.local_edit",
"image.scale_adjust",
"image.crop",
"image.flip_rotate",
].includes(capability.id)
? "interactive"
: "server",
entry: capability.agent.toolName || capability.id.replaceAll(".", "_"),
note:
capability.risk === "destructive"
? "需要用户明确要求及明确目标;模型不能自行确认"
: capability.executionMode === "hybrid"
? "参数齐全时执行,否则打开真实交互面板"
: undefined,
}),
),
...DEFAULT_CANVAS_TOOLBAR_ITEMS.map(
(item): AgentCapabilityAccess => ({
id: `canvas.ui.${item.key}`,
workspace: "canvas",
title: `${item.group} · ${item.label}`,
authorized: true,
previouslyExposed: false,
execution: "interactive",
entry: "openCanvasFeature",
note: "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成",
}),
),
...IMAGE_CANVAS_OPERATIONS.map(
(operation): AgentCapabilityAccess => ({
id: `canvas.operation.${operation}`,
workspace: "canvas",
title: `画布操作 · ${imageOperationTitles[operation] || operation}`,
authorized: true,
previouslyExposed: false,
execution: "server",
entry: "editCanvasNodes",
}),
),
...Object.values(videoConfig.functions).map(
(definition): AgentCapabilityAccess => ({
id: `video.function.${definition.key}`,
workspace: "video",
title: definition.label,
authorized: true,
previouslyExposed: [
"story-script",
"video-analysis",
"image-to-prompt",
"text-to-video",
"first-frame-video",
"first-last-frame-video",
].includes(definition.key),
execution:
definition.key === "audio-to-video"
? "backend_required"
: ["local", "pick-object"].includes(definition.route)
? "interactive"
: "server",
entry: "runVideoCanvasFunction",
note:
definition.key === "audio-to-video"
? "当前页面未接真实音频驱动视频后端,已授权入口但不能伪称完成音频驱动"
: !definition.enabled
? "已授权;管理员关闭了底层功能,需启用对应服务后才能执行"
: undefined,
}),
),
...VIDEO_CANVAS_EXTRA_ACTIONS.map(
([id, title]): AgentCapabilityAccess => ({
id: `video.node.${id}`,
workspace: "video",
title,
authorized: true,
previouslyExposed: false,
execution: "server",
entry: "editVideoCanvas",
}),
),
...(
[
["extract-frame", "真实视频截帧"],
["trim", "真实视频剪辑导出"],
["crop", "真实视频裁剪"],
["extract-audio", "提取完整音轨"],
["mute", "生成无声视频"],
] as const
).map(
([id, title]): AgentCapabilityAccess => ({
id: `video.media.${id}`,
workspace: "video",
title,
authorized: true,
previouslyExposed: false,
execution: "server",
entry: "processVideoMedia",
note: "使用真实媒体处理并上传现有存储,不复用页面的模拟产物;单次视频不超过120秒",
}),
),
...VIDEO_CANVAS_INTERACTIVE_ACTIONS.map(
([id, title]): AgentCapabilityAccess => ({
id: `video.ui.${id}`,
workspace: "video",
title,
authorized: true,
previouslyExposed: false,
execution: ["video-hd", "subtitle-erase", "voice-split", "background-split"].includes(id)
? "backend_required"
: "interactive",
entry: "openVideoCanvasFeature",
note: [
"video-crop",
"video-hd",
"subtitle-erase",
"audio-split",
"voice-split",
"background-split",
"frame-first",
"frame-last",
"frame-current",
].includes(id)
? "页面当前为占位/模拟流程;只授权设置入口,不执行模拟产物或声称媒体已处理"
: undefined,
}),
),
].map(item => ({ ...item, authorized: isZcodeWorkspaceAuthorized(item.workspace),
...(!isZcodeWorkspaceAuthorized(item.workspace) ? { note: ["用户暂未授权视频画布能力", item.note].filter(Boolean).join(";") } : {}) }));
}