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:
|
* Features:
|
||||||
* - Execute Button mit Loading State
|
* - Execute Button mit Loading State
|
||||||
|
* - SSE Streaming (kein Timeout bei langen Workflows)
|
||||||
|
* - Real-time Progress
|
||||||
* - Error Handling
|
* - Error Handling
|
||||||
* - Success State
|
|
||||||
* - Debug Mode Toggle
|
* - Debug Mode Toggle
|
||||||
*
|
|
||||||
* Part 2: Frontend Execute Integration
|
|
||||||
*/
|
*/
|
||||||
export function WorkflowExecutePanel({
|
export function WorkflowExecutePanel({
|
||||||
currentPrompt,
|
currentPrompt,
|
||||||
|
|
@ -20,6 +19,7 @@ export function WorkflowExecutePanel({
|
||||||
const [executing, setExecuting] = useState(false)
|
const [executing, setExecuting] = useState(false)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [debugMode, setDebugMode] = useState(true)
|
const [debugMode, setDebugMode] = useState(true)
|
||||||
|
const [progress, setProgress] = useState(null)
|
||||||
|
|
||||||
const handleExecute = async () => {
|
const handleExecute = async () => {
|
||||||
if (!currentPrompt || !currentPrompt.slug) {
|
if (!currentPrompt || !currentPrompt.slug) {
|
||||||
|
|
@ -30,14 +30,28 @@ export function WorkflowExecutePanel({
|
||||||
try {
|
try {
|
||||||
setExecuting(true)
|
setExecuting(true)
|
||||||
setError(null)
|
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,
|
currentPrompt.slug,
|
||||||
null, // variables (später erweiterbar)
|
null, // modules
|
||||||
|
null, // timeframes
|
||||||
debugMode,
|
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)
|
console.log('✅ Workflow execution completed:', result)
|
||||||
|
|
@ -57,81 +71,107 @@ export function WorkflowExecutePanel({
|
||||||
setError(e.message)
|
setError(e.message)
|
||||||
} finally {
|
} finally {
|
||||||
setExecuting(false)
|
setExecuting(false)
|
||||||
|
setProgress(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||||
{/* Debug Mode Toggle */}
|
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||||||
<label
|
{/* Debug Mode Toggle */}
|
||||||
style={{
|
<label
|
||||||
display: 'flex',
|
style={{
|
||||||
alignItems: 'center',
|
display: 'flex',
|
||||||
gap: '4px',
|
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',
|
fontSize: '12px',
|
||||||
color: 'var(--text2)',
|
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',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
|
||||||
gap: '8px'
|
gap: '8px'
|
||||||
}}
|
}}>
|
||||||
>
|
<span className="spinner" style={{ width: 12, height: 12 }} />
|
||||||
{executing ? (
|
<span>
|
||||||
<>
|
{progress.total > 0
|
||||||
<span className="spinner" style={{ width: 14, height: 14 }} />
|
? `${progress.current}/${progress.total} Nodes`
|
||||||
Executing...
|
: 'Läuft...'
|
||||||
</>
|
}
|
||||||
) : (
|
{progress.label && ` • ${progress.label}`}
|
||||||
<>
|
</span>
|
||||||
▶️ 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>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user