From b80a7b9ccb46d73a5ea326dcc89a09f45718ca58 Mon Sep 17 00:00:00 2001 From: mlogclub Date: Tue, 30 Jun 2026 11:16:24 +0800 Subject: [PATCH] feat: add output schema handling and display in NodeConfigPanel; implement buildVariableSpecDisplay function --- .../_components/node-config-panel.tsx | 21 +++++++++++++++++++ .../_components/workflow-utils.test.mjs | 16 ++++++++++++++ .../_components/workflow-utils.ts | 16 ++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/web/app/dashboard/ai-workflows/_components/node-config-panel.tsx b/web/app/dashboard/ai-workflows/_components/node-config-panel.tsx index ff488a0..c1410e3 100644 --- a/web/app/dashboard/ai-workflows/_components/node-config-panel.tsx +++ b/web/app/dashboard/ai-workflows/_components/node-config-panel.tsx @@ -12,6 +12,7 @@ import { cn } from "@/lib/utils" import { VariableSelector } from "./variable-selector" import { + buildVariableSpecDisplay, createConditionBranchID, isRefValue, normalizeNodeConfig, @@ -73,6 +74,7 @@ export function NodeConfigPanel({ const inputsValues = node.data?.inputsValues ?? {} const inputSchema = nodeSpec?.inputSchema ?? [] + const outputSchema = nodeSpec?.outputSchema ?? [] const canDelete = node.type !== "start" && node.type !== "end" const config = normalizeNodeConfig(node.data?.config) const branches = config.branches ?? [] @@ -175,6 +177,25 @@ export function NodeConfigPanel({ onDelete={deleteBranch} /> ) : null} + + {outputSchema.length > 0 ? ( + +
+ {outputSchema.map((output) => { + const item = buildVariableSpecDisplay(output) + return ( +
+
{item.label}
+
{item.subtitle}
+ {item.description ? ( +
{item.description}
+ ) : null} +
+ ) + })} +
+
+ ) : null} ) diff --git a/web/app/dashboard/ai-workflows/_components/workflow-utils.test.mjs b/web/app/dashboard/ai-workflows/_components/workflow-utils.test.mjs index 68067ef..64486fc 100644 --- a/web/app/dashboard/ai-workflows/_components/workflow-utils.test.mjs +++ b/web/app/dashboard/ai-workflows/_components/workflow-utils.test.mjs @@ -244,6 +244,22 @@ describe("workflow variable display helpers", () => { description: "客户本轮发送的消息内容", }) }) + + it("builds variable spec display rows for readonly node outputs", async () => { + const { buildVariableSpecDisplay } = await loadModule() + + assert.deepEqual(plain(buildVariableSpecDisplay({ + name: "replyText", + label: "回复内容", + type: "string", + description: "发送给客户的最终回复文本", + })), { + key: "replyText", + label: "回复内容", + subtitle: "replyText · string", + description: "发送给客户的最终回复文本", + }) + }) }) describe("workflow definition mutations", () => { diff --git a/web/app/dashboard/ai-workflows/_components/workflow-utils.ts b/web/app/dashboard/ai-workflows/_components/workflow-utils.ts index d61547a..ed4bffc 100644 --- a/web/app/dashboard/ai-workflows/_components/workflow-utils.ts +++ b/web/app/dashboard/ai-workflows/_components/workflow-utils.ts @@ -72,6 +72,13 @@ export type WorkflowVariableOption = { description: string } +export type WorkflowVariableSpecDisplay = { + key: string + label: string + subtitle: string + description: string +} + export type WorkflowNodeSpec = AIWorkflowNodeSpec export type WorkflowDraftValidation = { @@ -110,6 +117,15 @@ export function buildVariableOption(variable: WorkflowVariableRef): WorkflowVari } } +export function buildVariableSpecDisplay(variable: WorkflowVariableSpec): WorkflowVariableSpecDisplay { + return { + key: variable.name, + label: variable.label || variable.name, + subtitle: [variable.name, variable.type].filter(Boolean).join(" · "), + description: variable.description || "", + } +} + export function getNodeTitle( node: AIWorkflowDefinition["nodes"][number] | undefined, specs: AIWorkflowNodeSpec[] = []