d7c6fab933
两个实测发现的问题: 1. 审批条偶发不弹、任务卡「运行中」:exec 的 await 事件走实时 core NATS(无回放), UI 的 EventSource 晚连一拍就漏掉 → 永远不弹审批。改为轮询持久化的任务状态 (GET /tasks/:id,每 1.5s),status==waiting 即弹审批条,不再依赖易抢跑的 exec 事件; exec 事件仅用于丰富摘要。新增 api.taskStatus + RunState.lifecycle/detail + App.tsx 轮询 + 终态停轮询;BottomDrawer 审批条触发改以 lifecycle==waiting 为准。 2. Studio 左侧调色板没有「人工审批」节点(只加了 NODE_KINDS,漏了 NODE_ORDER), 且其配色与「输入」同为 amber 难以区分:补进 NODE_ORDER(分支与并行之间), 配色改 orange(橙)与输入的 amber(琥珀金)区分。 验证:桌面端 live 重启确认审批条可靠弹出 + 批准/拒绝两条路径正常;左侧节点列表与 画布配色区分清晰。tsc 干净 + vitest 36 过。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
171 lines
5.6 KiB
TypeScript
171 lines
5.6 KiB
TypeScript
// 节点类型目录 —— Studio 画布的"类型化节点"定义(决定面板、配色、检查器字段)。
|
|
// 与后端 DSL / Eino 图节点对齐:输入 / 检索(RAG) / Agent / 工具 / 记忆 / 分支 / 并行 / 汇聚 / 渲染 / 输出。
|
|
|
|
export type FieldType = "text" | "textarea" | "number" | "select" | "checkbox";
|
|
|
|
export interface Field {
|
|
key: string;
|
|
label: string;
|
|
type: FieldType;
|
|
options?: string[];
|
|
placeholder?: string;
|
|
required?: boolean;
|
|
}
|
|
|
|
export interface NodeKind {
|
|
kind: string;
|
|
label: string;
|
|
accent: string; // 左色条 / 标签配色(tailwind 类)
|
|
badge: string;
|
|
desc: string;
|
|
fields: Field[];
|
|
defaults: Record<string, unknown>;
|
|
}
|
|
|
|
export const NODE_KINDS: Record<string, NodeKind> = {
|
|
input: {
|
|
kind: "input",
|
|
label: "输入",
|
|
accent: "border-l-amber-500",
|
|
badge: "bg-amber-100 text-amber-700",
|
|
desc: "用户提问 / 文件 / 变量",
|
|
fields: [
|
|
{ key: "text", label: "用户输入", type: "textarea", placeholder: "要发送给模型的内容…", required: true },
|
|
{ key: "source", label: "来源", type: "select", options: ["用户输入", "本地文件", "变量"] },
|
|
],
|
|
defaults: { text: "", source: "用户输入" },
|
|
},
|
|
retriever: {
|
|
kind: "retriever",
|
|
label: "检索 (RAG)",
|
|
accent: "border-l-sky-500",
|
|
badge: "bg-sky-100 text-sky-700",
|
|
desc: "绑定知识库做混合检索",
|
|
fields: [
|
|
{ key: "kb", label: "知识库", type: "select", options: ["(未建库)", "合同库", "判例库"], required: true },
|
|
{ key: "topK", label: "TopK", type: "number" },
|
|
{ key: "rerank", label: "重排 Rerank", type: "checkbox" },
|
|
],
|
|
defaults: { kb: "(未建库)", topK: 5, rerank: true },
|
|
},
|
|
agent: {
|
|
kind: "agent",
|
|
label: "Agent / LLM",
|
|
accent: "border-l-violet-500",
|
|
badge: "bg-violet-100 text-violet-700",
|
|
desc: "调模型生成",
|
|
fields: [
|
|
{ key: "model", label: "模型", type: "select", options: ["占位 Pool", "ollama:qwen", "vllm:custom"], required: true },
|
|
{ key: "system", label: "系统提示词", type: "textarea", placeholder: "你是…" },
|
|
{ key: "temperature", label: "温度", type: "number" },
|
|
{ key: "autonomous", label: "自主工具 (ReAct)", type: "checkbox" },
|
|
],
|
|
defaults: { model: "占位 Pool", system: "", temperature: 0.7, autonomous: false },
|
|
},
|
|
tool: {
|
|
kind: "tool",
|
|
label: "工具",
|
|
accent: "border-l-blue-500",
|
|
badge: "bg-blue-100 text-blue-700",
|
|
desc: "绑定 MCP 工具",
|
|
fields: [
|
|
{
|
|
key: "tool",
|
|
label: "工具",
|
|
type: "select",
|
|
options: ["wiki_search", "parse_document", "render_doc", "memory_get", "external_api"],
|
|
required: true,
|
|
},
|
|
{ key: "args", label: "参数(JSON)", type: "textarea", placeholder: '{"q":"..."}' },
|
|
],
|
|
defaults: { tool: "wiki_search", args: "{}" },
|
|
},
|
|
memory: {
|
|
kind: "memory",
|
|
label: "记忆",
|
|
accent: "border-l-emerald-500",
|
|
badge: "bg-emerald-100 text-emerald-700",
|
|
desc: "注入画像 / 历史 / 语义",
|
|
fields: [
|
|
{ key: "profile", label: "注入画像", type: "checkbox" },
|
|
{ key: "history", label: "注入历史", type: "checkbox" },
|
|
{ key: "semantic", label: "语义召回", type: "checkbox" },
|
|
],
|
|
defaults: { profile: true, history: true, semantic: false },
|
|
},
|
|
branch: {
|
|
kind: "branch",
|
|
label: "分支",
|
|
accent: "border-l-rose-500",
|
|
badge: "bg-rose-100 text-rose-700",
|
|
desc: "条件路由",
|
|
fields: [{ key: "condition", label: "条件", type: "text", placeholder: "score > 0.8" }],
|
|
defaults: { condition: "" },
|
|
},
|
|
approval: {
|
|
kind: "approval",
|
|
label: "人工审批",
|
|
accent: "border-l-orange-500",
|
|
badge: "bg-orange-100 text-orange-700",
|
|
desc: "HITL:暂停等人工批准",
|
|
fields: [
|
|
{ key: "title", label: "审批标题", type: "text", placeholder: "如:高危操作审批" },
|
|
{ key: "prompt", label: "审批说明", type: "textarea", placeholder: "向审批人说明待执行的操作…" },
|
|
],
|
|
defaults: { title: "人工审批", prompt: "请审批是否继续执行后续步骤" },
|
|
},
|
|
map: {
|
|
kind: "map",
|
|
label: "并行 / Map",
|
|
accent: "border-l-fuchsia-500",
|
|
badge: "bg-fuchsia-100 text-fuchsia-700",
|
|
desc: "fan-out(逐章节)",
|
|
fields: [{ key: "splitBy", label: "拆分依据", type: "text", placeholder: "sections" }],
|
|
defaults: { splitBy: "sections" },
|
|
},
|
|
aggregate: {
|
|
kind: "aggregate",
|
|
label: "汇聚",
|
|
accent: "border-l-fuchsia-700",
|
|
badge: "bg-fuchsia-100 text-fuchsia-800",
|
|
desc: "reduce / 合并",
|
|
fields: [{ key: "strategy", label: "合并策略", type: "select", options: ["拼接", "去重合并", "摘要"] }],
|
|
defaults: { strategy: "拼接" },
|
|
},
|
|
render: {
|
|
kind: "render",
|
|
label: "渲染",
|
|
accent: "border-l-cyan-600",
|
|
badge: "bg-cyan-100 text-cyan-700",
|
|
desc: "UniOffice 出 docx/pdf",
|
|
fields: [
|
|
{ key: "format", label: "格式", type: "select", options: ["docx", "pdf"] },
|
|
{ key: "template", label: "模板", type: "text", placeholder: "默认" },
|
|
],
|
|
defaults: { format: "docx", template: "默认" },
|
|
},
|
|
output: {
|
|
kind: "output",
|
|
label: "输出",
|
|
accent: "border-l-gray-500",
|
|
badge: "bg-gray-100 text-gray-700",
|
|
desc: "展示 / 导出 / 落盘",
|
|
fields: [{ key: "target", label: "目标", type: "select", options: ["屏幕", "本地文件"] }],
|
|
defaults: { target: "屏幕" },
|
|
},
|
|
};
|
|
|
|
export const NODE_ORDER = [
|
|
"input",
|
|
"retriever",
|
|
"agent",
|
|
"tool",
|
|
"memory",
|
|
"branch",
|
|
"approval",
|
|
"map",
|
|
"aggregate",
|
|
"render",
|
|
"output",
|
|
];
|