/** * 旧版数据格式适配层 * 将旧版前端数据格式转换为后端期望的协议格式 */ /** * 将旧版 nodes/edges 转换为后端执行器期望的格式 */ export function toExecutorPayload(data: { nodes: any[] edges: any[] settings?: any globalContext?: any }) { const { nodes, edges, settings, globalContext } = data // 转换节点 const buildParams = (node: any) => { if (node.params && typeof node.params === 'object') return node.params const meta = node.meta || node.data?.meta || {} const schema = meta.param_schema if (schema) { let names: string[] = [] if (Array.isArray(schema)) { names = schema.map((s: any) => s && s.name).filter(Boolean) } else if (schema && typeof schema === 'object') { names = Object.keys(schema) } const out: Record = {} names.forEach((k) => { if (node.data && Object.prototype.hasOwnProperty.call(node.data, k)) out[k] = node.data[k] else if (node.params && Object.prototype.hasOwnProperty.call(node.params, k)) out[k] = node.params[k] else out[k] = null }) return out } const source = (node.params && typeof node.params === 'object') ? node.params : (node.data && typeof node.data === 'object' ? node.data : {}) const reserved = new Set(['meta', 'label', 'position', 'updim', 'file_path', 'file', 'dirty']) const out: Record = {} Object.entries(source).forEach(([k, v]) => { if (!reserved.has(k)) out[k] = v }) return out } const nodesOut = nodes.map((n: any) => { // Support both React-view nodes and exported workflow nodes const className = n.class_name || n.data?.meta?.class_name || n.schemaName || (n.meta && n.meta.class_name) || n.type || 'UnknownNode' const nodeTypeRaw = n.data?.meta?.node_type || n.meta?.node_type || n.node_type || 'normal' const nodeType = nodeTypeRaw ? nodeTypeRaw.toString().toLowerCase() : 'normal' return { id: n.id, node_type: nodeType, class_name: className, params: buildParams(n), sub_workflow_nodes: n.sub_workflow_nodes || n.sub_workflow?.nodes || null, sub_workflow_edges: n.sub_workflow_edges || n.sub_workflow?.edges || null } }) // 转换边 const edgesOut = edges.map((e: any) => ({ source: e.source, source_port: e.source_port || e.sourceHandle?.replace(/^output-/, '') || 'output', target: e.target, target_port: e.target_port || e.targetHandle?.replace(/^input-/, '') || 'input', dimension_mode: e.dimension_mode || 'none' })) const payload: any = { nodes: nodesOut, edges: edgesOut } if (settings) payload.settings = settings if (globalContext) payload.global_context = globalContext return payload } /** * 将后端节点 meta 转换为前端期望的格式 */ export function transformNodeMeta(meta: any) { return { ...meta, display_name: meta.display_name || meta.name || 'Unknown', category: meta.category || 'Other', param_schema: normalizeParamSchema(meta.param_schema), inputs: normalizeInputs(meta.inputs), outputs: normalizeOutputs(meta.outputs) } } /** * 标准化参数模式 */ function normalizeParamSchema(raw: any): Array { if (Array.isArray(raw)) return raw if (raw && typeof raw === 'object') { return Object.entries(raw).map(([name, spec]: [string, any]) => ({ name, ...(spec || {}) })) } return [] } /** * 标准化输入端口 */ function normalizeInputs(raw: any): Array { if (Array.isArray(raw)) return raw if (raw && typeof raw === 'object') { return Object.entries(raw).map(([name, spec]: [string, any]) => ({ name, ...(spec || {}) })) } return [] } /** * 标准化输出端口 */ function normalizeOutputs(raw: any): Array { if (Array.isArray(raw)) return raw if (raw && typeof raw === 'object') { return Object.entries(raw).map(([name, spec]: [string, any]) => ({ name, ...(spec || {}) })) } return [] } /** * 获取类型信息 */ export function getTypeInfo(type: string = 'any') { const typeMap: Record = { 'any': { dim: 1, color: '#8b5cf6' }, 'number': { dim: 1, color: '#3b82f6' }, 'string': { dim: 1, color: '#10b981' }, 'array': { dim: 2, color: '#f59e0b' }, 'dataframe': { dim: 2, color: '#f59e0b' }, 'image': { dim: 2, color: '#ec4899' }, 'tensor': { dim: 3, color: '#ef4444' }, } return typeMap[type] || { dim: 1, color: '#6b7280' } }