Backend (Mini-Backend 1-2h): - Migration 016: ai_prompts.graph_data JSONB column - workflow_executor: graph_data parameter support (backward-compatible) - prompt_executor: execute_workflow_prompt uses graph_data Frontend (Main effort 25-35h): - WorkflowCanvas: React Flow wrapper component - 5 Custom Nodes: Start, End, Analysis, Logic, Join - 4 Config Panels: QuestionAugmentation, LogicExpression, Fallback, Join - workflowValidation: Structural + logical validation - workflowSerializer: Canvas ↔ JSONB conversion - WorkflowEditorPage: Main orchestration (420 LOC) - Route: /workflow-editor/:id - CSS: workflowEditor.css (300 LOC) Architecture: - Option B: ai_prompts.type='workflow' (not separate table) - panels/ subdirectory for clean separation - WorkflowCanvas reusable component - User GUI identical (Workflows = Prompts) - Backward-compatible (type='pipeline' unchanged) Version: v0.9m → v0.9n (Phase 5 complete) Module: workflow 0.5.0 → 0.6.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
71 lines
2.6 KiB
JavaScript
71 lines
2.6 KiB
JavaScript
/**
|
||
* FallbackConfig - Fallback-Strategie Konfiguration
|
||
*
|
||
* Props:
|
||
* - node: React Flow Node object
|
||
* - edges: Array of React Flow edges
|
||
* - onChange: (nodeId, updates) => void
|
||
*/
|
||
export function FallbackConfig({ node, edges, onChange }) {
|
||
const fallbackStrategy = node.data.fallback_strategy || 'conservative_skip'
|
||
const fallbackEdge = node.data.fallback_edge || null
|
||
|
||
// Outgoing Edges von diesem Node
|
||
const outgoingEdges = edges.filter(e => e.source === node.id)
|
||
|
||
const handleStrategyChange = (e) => {
|
||
const strategy = e.target.value
|
||
onChange(node.id, { fallback_strategy: strategy })
|
||
|
||
// Reset fallback_edge wenn strategy geändert wird
|
||
if (strategy === 'conservative_skip' || strategy === 'document_only') {
|
||
onChange(node.id, { fallback_edge: null })
|
||
}
|
||
}
|
||
|
||
const handleEdgeChange = (e) => {
|
||
onChange(node.id, { fallback_edge: e.target.value || null })
|
||
}
|
||
|
||
return (
|
||
<div className="config-section">
|
||
<h3>Fallback-Strategie</h3>
|
||
|
||
<label>Strategie bei unklaren Signalen</label>
|
||
<select value={fallbackStrategy} onChange={handleStrategyChange}>
|
||
<option value="conservative_skip">Konservativ überspringen</option>
|
||
<option value="default_path">Standardpfad ausführen</option>
|
||
<option value="uncertainty_path">Unsicherheits-Pfad</option>
|
||
<option value="document_only">Nur dokumentieren</option>
|
||
</select>
|
||
|
||
<div className="help-text">
|
||
{fallbackStrategy === 'conservative_skip' && 'Bei Unklarheit: Pfad nicht routen.'}
|
||
{fallbackStrategy === 'default_path' && 'Bei Unklarheit: Definierter Standardpfad wird ausgeführt.'}
|
||
{fallbackStrategy === 'uncertainty_path' && 'Bei Unklarheit: Expliziter Klärungspfad.'}
|
||
{fallbackStrategy === 'document_only' && 'Unklarheit dokumentieren, aber kein Routing.'}
|
||
</div>
|
||
|
||
{(fallbackStrategy === 'default_path' || fallbackStrategy === 'uncertainty_path') && (
|
||
<>
|
||
<label style={{ marginTop: '16px' }}>Ziel-Edge</label>
|
||
<select value={fallbackEdge || ''} onChange={handleEdgeChange}>
|
||
<option value="">-- Kante wählen --</option>
|
||
{outgoingEdges.map(e => (
|
||
<option key={e.id} value={e.id}>
|
||
{e.data?.label || `Edge → ${e.target}`}
|
||
</option>
|
||
))}
|
||
</select>
|
||
|
||
{outgoingEdges.length === 0 && (
|
||
<div className="help-text" style={{ color: 'var(--danger)' }}>
|
||
⚠️ Keine ausgehenden Kanten gefunden. Bitte verbinden Sie diesen Node zuerst.
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|