"use client"; import { useMemo } from "react"; import { ArrowRight, Copy, RotateCcw, Sparkles } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Textarea } from "@/components/ui/textarea"; import { cn } from "@/lib/utils"; import { defaultAgentWorkflowUiConfig } from "@/lib/agents/workflow-ui-config"; import { buildAgentWorkflowComposerContext, getAgentWorkflowProgress, } from "@/lib/agents/workflow-composer"; import type { AgentWorkflowComposerSelection, AgentWorkflowStepKey, AgentWorkflowUiConfig, } from "@/types/agent"; export interface AgentWorkflowInputs { primarySku: string; secondarySku: string; brand: string; store: string; } interface AgentWorkflowPanelProps { config?: AgentWorkflowUiConfig | null; inputs: AgentWorkflowInputs; onInputsChange: (next: AgentWorkflowInputs) => void; selections: Partial>; onSelectionsChange: (next: Partial>) => void; onReset?: () => void; embedded?: boolean; onNextStep?: () => void; } function buildSelectionSummary(params: { config: AgentWorkflowUiConfig; selections: Partial>; }) { const result: AgentWorkflowComposerSelection[] = []; for (const step of params.config.steps) { const selectedOptionKey = params.selections[step.key]; if (!selectedOptionKey) continue; const option = step.options.find((item) => item.key === selectedOptionKey && item.enabled); if (!option) continue; result.push({ stepKey: step.key, stepTitle: step.title, optionKey: option.key, optionName: option.buttonName, promptFragment: option.promptFragment || "", }); } return result; } export function AgentWorkflowPanel({ config, inputs, onInputsChange, selections, onSelectionsChange, onReset, embedded = false, onNextStep, }: AgentWorkflowPanelProps) { const brandOptions = useMemo( () => config?.inputOptions?.brands || defaultAgentWorkflowUiConfig.inputOptions?.brands || [], [config?.inputOptions?.brands] ); const storeOptions = useMemo( () => config?.inputOptions?.stores || defaultAgentWorkflowUiConfig.inputOptions?.stores || [], [config?.inputOptions?.stores] ); const resolvedConfig = useMemo(() => { const source = config?.steps?.length ? config : defaultAgentWorkflowUiConfig; return { steps: [...source.steps] .filter((step) => step.enabled) .map((step) => ({ ...step, options: [...step.options] .filter((option) => option.enabled) .sort((left, right) => left.sortOrder - right.sortOrder), })) .sort((left, right) => left.sortOrder - right.sortOrder), }; }, [config]); const summary = useMemo( () => buildSelectionSummary({ config: resolvedConfig, selections }), [resolvedConfig, selections] ); const composerContext = useMemo( () => buildAgentWorkflowComposerContext({ config: resolvedConfig, inputs, selections, }), [inputs, resolvedConfig, selections] ); const progress = useMemo( () => getAgentWorkflowProgress({ config: resolvedConfig, selections }), [resolvedConfig, selections] ); const handleCopyPrompt = async () => { if (!composerContext?.promptText) { toast.error("当前还没有可复制的 Prompt"); return; } try { await navigator.clipboard.writeText(composerContext.promptText); toast.success("Prompt 已复制"); } catch { toast.error("复制失败,请手动复制"); } }; return (
智能体工作流
左侧完成步骤选择,右侧执行与追调。
已完成 {progress.completed}/{progress.total} 个环节
基础输入
onInputsChange({ ...inputs, primarySku: event.target.value }) } placeholder="主 SKU" /> onInputsChange({ ...inputs, secondarySku: event.target.value }) } placeholder="搭配 SKU" /> onInputsChange({ ...inputs, brand: event.target.value }) } placeholder="品牌" list="agent-workflow-brand-options" /> {brandOptions.map((item) => ( onInputsChange({ ...inputs, store: event.target.value }) } placeholder="店铺" list="agent-workflow-store-options" /> {storeOptions.map((item) => (
{(brandOptions.length > 0 || storeOptions.length > 0) && (
品牌和店铺支持输入关键字匹配下拉选择,候选列表由系统设置配置。
)}
{resolvedConfig.steps.map((step) => (
{step.title}
{step.description ? (
{step.description}
) : null}
{step.options.map((option) => { const selected = selections[step.key] === option.key; return ( ); })}
))}
当前工作流摘要
{inputs.primarySku || inputs.secondarySku || inputs.brand || inputs.store ? (
{inputs.primarySku ? ( 主SKU:{inputs.primarySku} ) : null} {inputs.secondarySku ? ( 搭配SKU:{inputs.secondarySku} ) : null} {inputs.brand ? ( 品牌:{inputs.brand} ) : null} {inputs.store ? ( 店铺:{inputs.store} ) : null}
) : null} {summary.length > 0 ? (
{summary.map((item) => (
{item.stepTitle} {item.optionName}
))}
) : (
还没有选择工作流方案,先从上面的卡片开始。
)}
Prompt 预览
右侧发送时,会把下面这份工作流设定作为隐藏上下文一起注入。