/** * Workflow Serialization Utilities * * Konvertiert zwischen React Flow (Canvas) und Backend-Format (JSONB). */ /** * Serialisiert React Flow Graph zu Backend-kompatiblem Format * * @param {Array} nodes - React Flow nodes * @param {Array} edges - React Flow edges * @param {Object} metadata - Zusätzliche Metadaten * @returns {Object} JSONB-kompatibles Objekt für ai_prompts.graph_data */ export function serializeToWorkflowGraph(nodes, edges, metadata = {}) { const workflowNodes = nodes.map(node => ({ id: node.id, type: node.type, label: node.data.label || node.type, position: { x: node.position.x, y: node.position.y }, // Type-spezifische Felder ...(node.type === 'analysis' && { prompt_slug: node.data.prompt_slug || null, inline_template: node.data.inline_template || null, // Part 3: Inline Prompts prompt_name: node.data.prompt_name || null, question_augmentations: node.data.questions || [], // Backend erwartet question_augmentations fallback_strategy: node.data.fallback_strategy || 'conservative_skip' }), ...(node.type === 'logic' && { condition: node.data.condition || null, fallback_strategy: node.data.fallback_strategy || 'conservative_skip' }), ...(node.type === 'join' && { join_strategy: node.data.join_strategy || 'wait_all', skip_handling: node.data.skip_handling || 'ignore_skipped', min_paths: node.data.min_paths || 2 }), ...(node.type === 'end' && { output_mode: node.data.output_mode || 'auto', template: node.data.template || null }) })) const workflowEdges = edges.map(edge => ({ id: edge.id, source: edge.source, target: edge.target, label: edge.data?.label || null, sourceHandle: edge.sourceHandle || null, targetHandle: edge.targetHandle || null })) return { nodes: workflowNodes, edges: workflowEdges, metadata: { created_at: metadata.created_at || new Date().toISOString(), updated_at: new Date().toISOString(), version: metadata.version || '1.0' } } } /** * Deserialisiert Backend-Format zu React Flow Graph * * @param {Object} jsonbData - ai_prompts.graph_data (JSONB) * @returns {Object} { nodes, edges, metadata } */ export function deserializeFromWorkflowGraph(jsonbData) { if (!jsonbData || !jsonbData.nodes || !jsonbData.edges) { throw new Error('Invalid workflow graph data') } const reactFlowNodes = jsonbData.nodes.map(node => ({ id: node.id, type: node.type, position: { x: node.position.x, y: node.position.y }, data: { label: node.label, ...(node.type === 'analysis' && { prompt_slug: node.prompt_slug || node.prompt_id || null, // Fallback für alte Workflows mit prompt_id inline_template: node.inline_template || null, // Part 3: Inline Prompts prompt_name: node.prompt_name || null, // Falls vom Backend mitgeliefert questions: node.question_augmentations || node.questions || [], // Backend sendet question_augmentations fallback_strategy: node.fallback_strategy || 'conservative_skip' }), ...(node.type === 'logic' && { condition: node.condition || null, fallback_strategy: node.fallback_strategy || 'conservative_skip' }), ...(node.type === 'join' && { join_strategy: node.join_strategy || 'wait_all', skip_handling: node.skip_handling || 'ignore_skipped', min_paths: node.min_paths || 2 }), ...(node.type === 'end' && { output_mode: node.output_mode || 'auto', template: node.template || null }) } })) const reactFlowEdges = jsonbData.edges.map(edge => ({ id: edge.id, source: edge.source, target: edge.target, sourceHandle: edge.sourceHandle || null, targetHandle: edge.targetHandle || null, data: { label: edge.label || null }, type: 'default', animated: false })) return { nodes: reactFlowNodes, edges: reactFlowEdges, metadata: jsonbData.metadata || {} } }