From 7ebfcfd4eb6ea5a959f269e3d3fd2fc8f50f354d Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 19 Mar 2026 00:34:09 +0800 Subject: [PATCH] feat: refine agent workflow settings and studio ui --- .../agent-workflow-settings-client.tsx | 703 +++++++++++------- .../agents/AgentGenerationWorkspaceDarkV2.tsx | 89 --- components/agents/AgentPresetDialog.tsx | 112 +-- .../agents/AgentWorkflowPanelDarkV2.tsx | 38 +- lib/agents/workflow-composer.ts | 25 +- lib/agents/workflow-ui-config.ts | 274 ++++--- types/agent.ts | 1 + 7 files changed, 726 insertions(+), 516 deletions(-) diff --git a/app/(admin)/settings/agent-workflow/agent-workflow-settings-client.tsx b/app/(admin)/settings/agent-workflow/agent-workflow-settings-client.tsx index ab8a0ae7b..574a536c7 100644 --- a/app/(admin)/settings/agent-workflow/agent-workflow-settings-client.tsx +++ b/app/(admin)/settings/agent-workflow/agent-workflow-settings-client.tsx @@ -26,7 +26,13 @@ import { Video, } from "lucide-react"; import { + AGENT_WORKFLOW_MAX_TREE_DEPTH, + countAgentWorkflowOptions, + countAgentWorkflowSteps, defaultAgentWorkflowUiConfig, + findAgentWorkflowStepByKey, + flattenAgentWorkflowSteps, + normalizeAgentWorkflowStepOrdering, normalizeAgentWorkflowUiConfig, } from "@/lib/agents/workflow-ui-config"; import { @@ -78,17 +84,12 @@ const itemVariants = { visible: { opacity: 1, y: 0 } }; +const WORKFLOW_LEVEL_LABELS = ["一级模块", "二级模块", "三级模块"]; + function normalizeConfigOrdering(config: AgentWorkflowUiConfig): AgentWorkflowUiConfig { return { ...config, - steps: config.steps.map((step, stepIndex) => ({ - ...step, - sortOrder: stepIndex, - options: step.options.map((option, optionIndex) => ({ - ...option, - sortOrder: optionIndex, - })), - })), + steps: normalizeAgentWorkflowStepOrdering(config.steps), }; } @@ -140,20 +141,59 @@ function createWorkflowOptionDraft( } function createWorkflowStepDraft( + depth: number, stepIndex: number, existingStepKeys: string[] ): AgentWorkflowStepUiConfig { - const stepKey = ensureUniqueWorkflowKey(`step_${stepIndex + 1}`, existingStepKeys); + const levelLabel = WORKFLOW_LEVEL_LABELS[Math.max(0, Math.min(depth - 1, 2))] || "模块"; + const stepKey = ensureUniqueWorkflowKey(`step_${depth}_${stepIndex + 1}`, existingStepKeys); return { key: stepKey, - title: `新步骤${stepIndex + 1}`, + title: `新${levelLabel}${stepIndex + 1}`, description: "", enabled: true, sortOrder: stepIndex, options: [createWorkflowOptionDraft(stepKey, 0, [])], + children: [], }; } +function updateWorkflowStepTree( + steps: AgentWorkflowStepUiConfig[], + stepKey: string, + updater: (step: AgentWorkflowStepUiConfig) => AgentWorkflowStepUiConfig +): AgentWorkflowStepUiConfig[] { + return steps.map((step) => { + if (step.key === stepKey) { + return updater(step); + } + return { + ...step, + children: updateWorkflowStepTree(step.children || [], stepKey, updater), + }; + }); +} + +function removeWorkflowStepTree( + steps: AgentWorkflowStepUiConfig[], + stepKey: string +): AgentWorkflowStepUiConfig[] { + return steps + .filter((step) => step.key !== stepKey) + .map((step) => ({ + ...step, + children: removeWorkflowStepTree(step.children || [], stepKey), + })); +} + +function collectWorkflowStepKeys(steps: AgentWorkflowStepUiConfig[]): string[] { + return flattenAgentWorkflowSteps(steps).map(({ step }) => step.key); +} + +function collectDescendantStepKeys(step: AgentWorkflowStepUiConfig): string[] { + return [step.key, ...collectWorkflowStepKeys(step.children || [])]; +} + interface AgentWorkflowSettingsClientProps { backHref?: string; backLabel?: string; @@ -189,7 +229,8 @@ export function AgentWorkflowSettingsClient({ useEffect(() => { if (!data) return; - setConfig(normalizeAgentWorkflowUiConfig(data.agentWorkflowUiConfig)); + const normalizedUiConfig = normalizeAgentWorkflowUiConfig(data.agentWorkflowUiConfig); + setConfig(normalizedUiConfig); setMaterialApiConfig( normalizeAgentWorkflowMaterialApiConfig(data.agentWorkflowMaterialApiConfig) ); @@ -199,9 +240,8 @@ export function AgentWorkflowSettingsClient({ setBrandModelConfig( normalizeAgentWorkflowBrandModelConfig(data.agentWorkflowBrandModelConfig) ); - // Expand first step by default - if (data.agentWorkflowUiConfig?.steps?.[0]) { - setExpandedSteps(new Set([data.agentWorkflowUiConfig.steps[0].key])); + if (normalizedUiConfig.steps[0]) { + setExpandedSteps(new Set([normalizedUiConfig.steps[0].key])); } }, [data]); @@ -278,11 +318,15 @@ export function AgentWorkflowSettingsClient({ }; const enabledStepsCount = useMemo( - () => sortedSteps.filter((s) => s.enabled).length, + () => flattenAgentWorkflowSteps(sortedSteps).filter(({ step }) => step.enabled).length, [sortedSteps] ); const totalOptionsCount = useMemo( - () => sortedSteps.reduce((acc, s) => acc + s.options.filter((o) => o.enabled).length, 0), + () => + flattenAgentWorkflowSteps(sortedSteps).reduce( + (acc, { step }) => acc + step.options.filter((option) => option.enabled).length, + 0 + ), [sortedSteps] ); @@ -305,7 +349,7 @@ export function AgentWorkflowSettingsClient({ setConfig((prev) => normalizeConfigOrdering({ ...prev, - steps: prev.steps.map((step) => (step.key === stepKey ? updater(step) : step)), + steps: updateWorkflowStepTree(prev.steps, stepKey, updater), }) ); }; @@ -325,30 +369,59 @@ export function AgentWorkflowSettingsClient({ })); }; - const handleAddStep = () => { + const handleAddStep = (parentStepKey?: string) => { setConfig((prev) => { - const newStep = createWorkflowStepDraft(prev.steps.length, prev.steps.map((step) => step.key)); - // Auto expand new step - setExpandedSteps((prevExpanded) => new Set([...prevExpanded, newStep.key])); + const existingStepKeys = collectWorkflowStepKeys(prev.steps); + + if (!parentStepKey) { + const newStep = createWorkflowStepDraft(1, prev.steps.length, existingStepKeys); + setExpandedSteps((prevExpanded) => new Set([...prevExpanded, newStep.key])); + return normalizeConfigOrdering({ + ...prev, + steps: [...prev.steps, newStep], + }); + } + + const parentStep = findAgentWorkflowStepByKey(prev.steps, parentStepKey); + const parentDepth = flattenAgentWorkflowSteps(prev.steps).find( + ({ step }) => step.key === parentStepKey + )?.depth; + if (!parentStep || !parentDepth || parentDepth >= AGENT_WORKFLOW_MAX_TREE_DEPTH) { + return prev; + } + + const newChildStep = createWorkflowStepDraft( + parentDepth + 1, + (parentStep.children || []).length, + existingStepKeys + ); + setExpandedSteps( + (prevExpanded) => new Set([...prevExpanded, parentStepKey, newChildStep.key]) + ); return normalizeConfigOrdering({ ...prev, - steps: [...prev.steps, newStep], + steps: updateWorkflowStepTree(prev.steps, parentStepKey, (step) => ({ + ...step, + children: [...(step.children || []), newChildStep], + })), }); }); - // Switch to workflow tab setActiveTab("workflow"); }; const handleRemoveStep = (stepKey: string) => { + const targetStep = findAgentWorkflowStepByKey(config.steps, stepKey); setConfig((prev) => normalizeConfigOrdering({ ...prev, - steps: prev.steps.filter((step) => step.key !== stepKey), + steps: removeWorkflowStepTree(prev.steps, stepKey), }) ); setExpandedSteps((prev) => { const next = new Set(prev); - next.delete(stepKey); + for (const key of targetStep ? collectDescendantStepKeys(targetStep) : [stepKey]) { + next.delete(key); + } return next; }); }; @@ -428,6 +501,332 @@ export function AgentWorkflowSettingsClient({ } }; + const renderWorkflowStepCard = ( + step: AgentWorkflowStepUiConfig, + depth: number, + indexPath: number[] + ) => { + const stepLabel = indexPath.join("."); + const levelLabel = + WORKFLOW_LEVEL_LABELS[Math.max(0, Math.min(depth - 1, WORKFLOW_LEVEL_LABELS.length - 1))] || + `第 ${depth} 级模块`; + const childSteps = [...(step.children || [])].sort((left, right) => left.sortOrder - right.sortOrder); + const directEnabledOptionCount = step.options.filter((option) => option.enabled).length; + const childStepCount = countAgentWorkflowSteps(childSteps); + const canAddChild = depth < AGENT_WORKFLOW_MAX_TREE_DEPTH; + + return ( + 1 && "pl-4 sm:pl-6")} + > + toggleStepExpansion(step.key)} + > + 1 && "border-l-4 border-l-emerald-500/30" + )} + > + +
+
+ + + + +
+ {stepLabel} +
+
+ +
+
+ + updateStep(step.key, (currentStep) => ({ + ...currentStep, + title: e.target.value, + })) + } + className="h-8 min-w-[180px] max-w-[260px] border-0 bg-transparent px-0 text-base font-medium shadow-none focus-visible:ring-0" + placeholder="模块标题" + /> + {levelLabel} + + {step.key} + +
+
+ +
+ {directEnabledOptionCount} 选项 + {childStepCount} 子模块 + {canAddChild ? ( + + ) : ( + 已达三级 + )} + + updateStep(step.key, (currentStep) => ({ + ...currentStep, + enabled: checked, + })) + } + /> + +
+
+
+ + + +
+ +