fix: Use SSE streaming in WorkflowExecutePanel to prevent 504 timeout
ROOT CAUSE: - WorkflowExecutePanel used api.executeWorkflow (synchronous POST) - Long workflows (10+ nodes, 60-90s) → 504 Gateway Timeout - Reverse proxy kills request after 60s SOLUTION: - Switch to api.executeUnifiedPromptStream (SSE) - Real-time progress updates during execution - No timeout (streaming connection stays alive) - Progress display: X/Y Nodes • Current node label Changes: - Replace executeWorkflow with executeUnifiedPromptStream - Add progress callback for node_complete events - Add progress state + UI display - Flexbox column layout for progress bar Fixes: 504 Gateway Timeout bei langen Workflows im Workflow Designer
This commit is contained in:
parent
680269e971
commit
b1d596e0ab
|
|
@ -6,11 +6,10 @@ import { api } from '../../../utils/api'
|
|||
*
|
||||
* Features:
|
||||
* - Execute Button mit Loading State
|
||||
* - SSE Streaming (kein Timeout bei langen Workflows)
|
||||
* - Real-time Progress
|
||||
* - Error Handling
|
||||
* - Success State
|
||||
* - Debug Mode Toggle
|
||||
*
|
||||
* Part 2: Frontend Execute Integration
|
||||
*/
|
||||
export function WorkflowExecutePanel({
|
||||
currentPrompt,
|
||||
|
|
@ -20,6 +19,7 @@ export function WorkflowExecutePanel({
|
|||
const [executing, setExecuting] = useState(false)
|
||||
const [error, setError] = useState(null)
|
||||
const [debugMode, setDebugMode] = useState(true)
|
||||
const [progress, setProgress] = useState(null)
|
||||
|
||||
const handleExecute = async () => {
|
||||
if (!currentPrompt || !currentPrompt.slug) {
|
||||
|
|
@ -30,14 +30,28 @@ export function WorkflowExecutePanel({
|
|||
try {
|
||||
setExecuting(true)
|
||||
setError(null)
|
||||
setProgress({ current: 0, total: 0, label: 'Starte...' })
|
||||
|
||||
console.log('🚀 Executing workflow:', currentPrompt.slug)
|
||||
console.log('🚀 Executing workflow via SSE:', currentPrompt.slug)
|
||||
|
||||
const result = await api.executeWorkflow(
|
||||
const result = await api.executeUnifiedPromptStream(
|
||||
currentPrompt.slug,
|
||||
null, // variables (später erweiterbar)
|
||||
null, // modules
|
||||
null, // timeframes
|
||||
debugMode,
|
||||
false // save (nicht in ai_insights speichern)
|
||||
false, // save (nicht in ai_insights speichern)
|
||||
(event) => {
|
||||
// Progress callback für real-time updates
|
||||
if (event.type === 'execution_started') {
|
||||
setProgress({ current: 0, total: 0, label: 'Gestartet...' })
|
||||
} else if (event.type === 'node_complete') {
|
||||
setProgress({
|
||||
current: event.completed_nodes || 0,
|
||||
total: event.total_nodes || 0,
|
||||
label: event.node_label || 'Processing...'
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
console.log('✅ Workflow execution completed:', result)
|
||||
|
|
@ -57,81 +71,107 @@ export function WorkflowExecutePanel({
|
|||
setError(e.message)
|
||||
} finally {
|
||||
setExecuting(false)
|
||||
setProgress(null)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||||
{/* Debug Mode Toggle */}
|
||||
<label
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||||
{/* Debug Mode Toggle */}
|
||||
<label
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
fontSize: '12px',
|
||||
color: 'var(--text2)',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
title="Debug-Modus zeigt detaillierte Node-States im Ergebnis"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={debugMode}
|
||||
onChange={(e) => setDebugMode(e.target.checked)}
|
||||
disabled={executing}
|
||||
/>
|
||||
Debug
|
||||
</label>
|
||||
|
||||
{/* Execute Button */}
|
||||
<button
|
||||
className="btn-primary"
|
||||
onClick={handleExecute}
|
||||
disabled={disabled || executing || !currentPrompt}
|
||||
title={
|
||||
!currentPrompt
|
||||
? 'Workflow muss zuerst gespeichert werden'
|
||||
: disabled
|
||||
? 'Workflow hat Validierungsfehler'
|
||||
: 'Workflow ausführen'
|
||||
}
|
||||
style={{
|
||||
minWidth: '120px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '8px'
|
||||
}}
|
||||
>
|
||||
{executing ? (
|
||||
<>
|
||||
<span className="spinner" style={{ width: 14, height: 14 }} />
|
||||
Executing...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
▶️ Execute
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Error Display */}
|
||||
{error && (
|
||||
<div
|
||||
style={{
|
||||
padding: '6px 12px',
|
||||
background: 'var(--danger)',
|
||||
color: 'white',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px',
|
||||
maxWidth: '300px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}}
|
||||
title={error}
|
||||
>
|
||||
❌ {error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Progress Display */}
|
||||
{progress && (
|
||||
<div style={{
|
||||
padding: '8px 12px',
|
||||
background: 'var(--surface2)',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px',
|
||||
color: 'var(--text2)',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
title="Debug-Modus zeigt detaillierte Node-States im Ergebnis"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={debugMode}
|
||||
onChange={(e) => setDebugMode(e.target.checked)}
|
||||
disabled={executing}
|
||||
/>
|
||||
Debug
|
||||
</label>
|
||||
|
||||
{/* Execute Button */}
|
||||
<button
|
||||
className="btn-primary"
|
||||
onClick={handleExecute}
|
||||
disabled={disabled || executing || !currentPrompt}
|
||||
title={
|
||||
!currentPrompt
|
||||
? 'Workflow muss zuerst gespeichert werden'
|
||||
: disabled
|
||||
? 'Workflow hat Validierungsfehler'
|
||||
: 'Workflow ausführen'
|
||||
}
|
||||
style={{
|
||||
minWidth: '120px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '8px'
|
||||
}}
|
||||
>
|
||||
{executing ? (
|
||||
<>
|
||||
<span className="spinner" style={{ width: 14, height: 14 }} />
|
||||
Executing...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
▶️ Execute
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Error Display */}
|
||||
{error && (
|
||||
<div
|
||||
style={{
|
||||
padding: '6px 12px',
|
||||
background: 'var(--danger)',
|
||||
color: 'white',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px',
|
||||
maxWidth: '300px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}}
|
||||
title={error}
|
||||
>
|
||||
❌ {error}
|
||||
}}>
|
||||
<span className="spinner" style={{ width: 12, height: 12 }} />
|
||||
<span>
|
||||
{progress.total > 0
|
||||
? `${progress.current}/${progress.total} Nodes`
|
||||
: 'Läuft...'
|
||||
}
|
||||
{progress.label && ` • ${progress.label}`}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user