import { defaultAgentWorkflowUiConfig, flattenVisibleAgentWorkflowSteps, getAgentWorkflowSelectedOptionPath, getEnabledAgentWorkflowStepTree, isAgentWorkflowStepSelectionComplete, } from "@/lib/agents/workflow-ui-config"; import type { AgentWorkflowComposerContext, AgentWorkflowComposerSelection, AgentWorkflowMaterialSelections, AgentWorkflowReferenceImage, AgentWorkflowSelectedBrandModel, AgentWorkflowStepKey, AgentWorkflowUiConfig, } from "@/types/agent"; export interface AgentWorkflowInputsValue { primarySku: string; secondarySku: string; brand: string; store: string; priceBand: string; } function buildGoodsInfoPromptLine(params: { label: string; result?: AgentWorkflowMaterialSelections["primary"]["result"] | null; }) { const goodsInfo = params.result?.goodsInfo; if (!goodsInfo) { return ""; } const parts = [ goodsInfo.productName ? `商品名称:${goodsInfo.productName}` : "", goodsInfo.sizeInfo ? `尺寸信息:${goodsInfo.sizeInfo}` : "", goodsInfo.materialInfo ? `材质信息:${goodsInfo.materialInfo}` : "", goodsInfo.designer ? `设计师:${goodsInfo.designer}` : "", goodsInfo.buyer ? `采购负责人:${goodsInfo.buyer}` : "", goodsInfo.boardType ? `板材类型:${goodsInfo.boardType}` : "", ].filter(Boolean); if (parts.length === 0) { return ""; } return `${params.label}:${parts.join(";")}`; } function buildGoodsInfoSizeConstraintLine(params: { label: string; result?: AgentWorkflowMaterialSelections["primary"]["result"] | null; }) { const goodsInfo = params.result?.goodsInfo; const sizeInfo = String(goodsInfo?.sizeInfo || "").trim(); if (!sizeInfo) { return ""; } return `${params.label}尺寸比例约束:尺寸信息为 ${sizeInfo}。生成场景时必须依据该尺寸信息判断产品在空间中的真实体量、长宽高关系和与周边家具/人物/通道的比例,不得随意放大、缩小或改变产品结构比例。`; } function buildGoodsInfoSummaryLine(params: { label: string; result?: AgentWorkflowMaterialSelections["primary"]["result"] | null; }) { const goodsInfo = params.result?.goodsInfo; if (!goodsInfo) { return ""; } const parts = [ goodsInfo.productName || "", goodsInfo.sizeInfo || "", goodsInfo.materialInfo || "", ].filter(Boolean); if (parts.length === 0) { return ""; } return `${params.label}:${parts.join(" / ")}`; } function normalizeWorkflowText(value: string) { return String(value || "") .replace(/\s+/g, " ") .trim(); } function buildGroupedSelectionLines(params: { selections: AgentWorkflowComposerSelection[]; valueKey: "optionName" | "promptFragment"; separator: string; leafOnly?: boolean; dedupeAcrossSteps?: boolean; }) { const grouped = new Map< string, { stepTitle: string; values: string[]; valueSet: Set; } >(); const globalValueSet = new Set(); const sourceSelections = params.leafOnly ? params.selections.filter((item, index, items) => { const nextItem = items[index + 1]; return !nextItem || nextItem.stepKey !== item.stepKey; }) : params.selections; sourceSelections.forEach((item) => { const rawValue = params.valueKey === "promptFragment" ? item.promptFragment || item.optionName : item.optionName; const normalizedValue = normalizeWorkflowText(rawValue || ""); if (!normalizedValue) { return; } const groupKey = item.stepKey; const currentGroup = grouped.get(groupKey) || { stepTitle: item.stepTitle, values: [], valueSet: new Set(), }; const dedupeKey = normalizedValue.toLowerCase(); if (params.dedupeAcrossSteps && globalValueSet.has(dedupeKey)) { return; } if (!currentGroup.valueSet.has(dedupeKey)) { currentGroup.valueSet.add(dedupeKey); if (params.dedupeAcrossSteps) { globalValueSet.add(dedupeKey); } currentGroup.values.push(normalizedValue); } grouped.set(groupKey, currentGroup); }); return Array.from(grouped.values()) .filter((group) => group.values.length > 0) .map((group) => `${group.stepTitle}:${group.values.join(params.separator)}`); } export function buildAgentWorkflowComposerContext(params: { config?: AgentWorkflowUiConfig | null; inputs: AgentWorkflowInputsValue; materials?: AgentWorkflowMaterialSelections | null; selectedBrandModel?: AgentWorkflowSelectedBrandModel | null; selections: Partial>; }): AgentWorkflowComposerContext | null { const config = params.config || defaultAgentWorkflowUiConfig; const steps = flattenVisibleAgentWorkflowSteps( getEnabledAgentWorkflowStepTree(Array.isArray(config.steps) ? config.steps : []), params.selections ) .map(({ step }) => step) .filter((step) => Array.isArray(step.options) && step.options.length > 0); const selections = steps .map((step) => { const selectedOptions = getAgentWorkflowSelectedOptionPath({ step, selectionValue: params.selections[step.key], }); if (selectedOptions.length === 0) return []; return selectedOptions.map((option) => ({ stepKey: step.key, stepTitle: step.title, optionKey: option.key, optionName: option.buttonName, promptFragment: option.promptFragment || option.buttonName, })); }) .flat() as AgentWorkflowComposerContext["selections"]; const referenceImages = getSelectedWorkflowReferenceImages({ inputs: params.inputs, materials: params.materials, selectedBrandModel: params.selectedBrandModel, }); const summaryParts: string[] = []; if (params.inputs.primarySku.trim()) { summaryParts.push(`主SKU:${params.inputs.primarySku.trim()}`); } if (params.inputs.secondarySku.trim()) { summaryParts.push(`搭配SKU:${params.inputs.secondarySku.trim()}`); } if (params.inputs.brand.trim()) { summaryParts.push(`品牌:${params.inputs.brand.trim()}`); } if (params.inputs.store.trim()) { summaryParts.push(`店铺:${params.inputs.store.trim()}`); } if (params.inputs.priceBand.trim()) { summaryParts.push(`价格带:${params.inputs.priceBand.trim()}`); } const primaryGoodsInfoSummary = buildGoodsInfoSummaryLine({ label: "主SKU商品信息", result: params.materials?.primary?.result || null, }); if (primaryGoodsInfoSummary) { summaryParts.push(primaryGoodsInfoSummary); } const secondaryGoodsInfoSummary = buildGoodsInfoSummaryLine({ label: "搭配SKU商品信息", result: params.materials?.secondary?.result || null, }); if (secondaryGoodsInfoSummary) { summaryParts.push(secondaryGoodsInfoSummary); } if (referenceImages.some((item) => item.source === "primary")) { summaryParts.push( `主SPU图:${referenceImages.filter((item) => item.source === "primary").length}张` ); } if (referenceImages.some((item) => item.source === "secondary")) { summaryParts.push( `搭配SKU图:${referenceImages.filter((item) => item.source === "secondary").length}张` ); } if (referenceImages.some((item) => item.source === "brandModel")) { summaryParts.push( `品牌模特图:${referenceImages.filter((item) => item.source === "brandModel").length}张` ); } summaryParts.push( ...buildGroupedSelectionLines({ selections, valueKey: "optionName", separator: " / ", }) ); if (summaryParts.length === 0) { return null; } const promptLines: string[] = []; if (params.inputs.primarySku.trim()) { promptLines.push(`主SKU:${params.inputs.primarySku.trim()}`); } if (params.inputs.secondarySku.trim()) { promptLines.push(`搭配SKU:${params.inputs.secondarySku.trim()}`); } if (params.inputs.brand.trim()) { promptLines.push(`品牌:${params.inputs.brand.trim()}`); } if (params.inputs.store.trim()) { promptLines.push(`店铺:${params.inputs.store.trim()}`); } if (params.inputs.priceBand.trim()) { promptLines.push(`价格带:${params.inputs.priceBand.trim()}`); } const primaryGoodsInfoPrompt = buildGoodsInfoPromptLine({ label: "主SKU商品信息", result: params.materials?.primary?.result || null, }); if (primaryGoodsInfoPrompt) { promptLines.push(primaryGoodsInfoPrompt); } const primaryGoodsInfoSizeConstraint = buildGoodsInfoSizeConstraintLine({ label: "主SKU", result: params.materials?.primary?.result || null, }); if (primaryGoodsInfoSizeConstraint) { promptLines.push(primaryGoodsInfoSizeConstraint); } const secondaryGoodsInfoPrompt = buildGoodsInfoPromptLine({ label: "搭配SKU商品信息", result: params.materials?.secondary?.result || null, }); if (secondaryGoodsInfoPrompt) { promptLines.push(secondaryGoodsInfoPrompt); } const secondaryGoodsInfoSizeConstraint = buildGoodsInfoSizeConstraintLine({ label: "搭配SKU", result: params.materials?.secondary?.result || null, }); if (secondaryGoodsInfoSizeConstraint) { promptLines.push(secondaryGoodsInfoSizeConstraint); } if (referenceImages.some((item) => item.source === "primary")) { const labels = referenceImages .filter((item) => item.source === "primary") .map((item) => item.label) .filter(Boolean) .slice(0, 8); promptLines.push( `主SPU参考图:已选${referenceImages.filter((item) => item.source === "primary").length}张${ labels.length > 0 ? `(${labels.join("、")})` : "" }` ); } if (referenceImages.some((item) => item.source === "secondary")) { const labels = referenceImages .filter((item) => item.source === "secondary") .map((item) => item.label) .filter(Boolean) .slice(0, 8); promptLines.push( `搭配SKU参考图:已选${referenceImages.filter((item) => item.source === "secondary").length}张${ labels.length > 0 ? `(${labels.join("、")})` : "" }` ); } if (referenceImages.some((item) => item.source === "brandModel")) { const labels = referenceImages .filter((item) => item.source === "brandModel") .map((item) => item.label) .filter(Boolean) .slice(0, 8); promptLines.push( `品牌模特图:已选${referenceImages.filter((item) => item.source === "brandModel").length}张${ labels.length > 0 ? `(${labels.join("、")})` : "" }` ); } promptLines.push( ...buildGroupedSelectionLines({ selections, valueKey: "promptFragment", separator: ";", dedupeAcrossSteps: true, }) ); return { primarySku: params.inputs.primarySku.trim() || undefined, secondarySku: params.inputs.secondarySku.trim() || undefined, brand: params.inputs.brand.trim() || undefined, store: params.inputs.store.trim() || undefined, priceBand: params.inputs.priceBand.trim() || undefined, selections, referenceImages, summaryText: summaryParts.join(" / "), promptText: promptLines.join("\n"), }; } export function getSelectedWorkflowReferenceImages(params: { inputs: AgentWorkflowInputsValue; materials?: AgentWorkflowMaterialSelections | null; selectedBrandModel?: AgentWorkflowSelectedBrandModel | null; }): AgentWorkflowReferenceImage[] { const materials = params.materials; if (!materials) return []; const collect = ( source: "primary" | "secondary", sourceLabel: string, fallbackSku: string ) => { const state = materials[source]; const result = state?.result; if (!result || !Array.isArray(result.images) || result.images.length === 0) { return [] as AgentWorkflowReferenceImage[]; } const selectedIds = new Set( (state.selectedImageIds || []).map((item) => String(item || "").trim()).filter(Boolean) ); if (selectedIds.size === 0) { return [] as AgentWorkflowReferenceImage[]; } return result.images .filter((item) => selectedIds.has(item.id)) .map((item) => ({ id: item.id, source, sourceLabel, sku: result.matchedSku || fallbackSku || undefined, productModel: result.productModel || null, label: item.label, name: item.fileName || item.label, url: item.imageUrl, assetId: item.assetId || null, mimeType: item.mimeType || null, })); }; return [ ...collect("primary", "主SPU", params.inputs.primarySku.trim()), ...collect("secondary", "搭配SKU", params.inputs.secondarySku.trim()), ...(params.selectedBrandModel?.imageUrl ? [ { id: params.selectedBrandModel.id, source: "brandModel" as const, sourceLabel: "品牌模特", label: params.selectedBrandModel.displayName || params.selectedBrandModel.brandName, name: params.selectedBrandModel.displayName || params.selectedBrandModel.brandName, url: params.selectedBrandModel.imageUrl, assetId: params.selectedBrandModel.assetId || null, mimeType: params.selectedBrandModel.mimeType || null, }, ] : []), ]; } export function getAgentWorkflowProgress(params: { config?: AgentWorkflowUiConfig | null; selections: Partial>; }) { const config = params.config || defaultAgentWorkflowUiConfig; const enabledSteps = flattenVisibleAgentWorkflowSteps( getEnabledAgentWorkflowStepTree(config.steps || []), params.selections ) .map(({ step }) => step) .filter((step) => Array.isArray(step.options) && step.options.length > 0); const completedSteps = enabledSteps.filter((step) => { return isAgentWorkflowStepSelectionComplete({ step, selectionValue: params.selections[step.key], }); }); return { total: enabledSteps.length, completed: completedSteps.length, isComplete: enabledSteps.length > 0 && completedSteps.length === enabledSteps.length, }; }