Universal CSV Importer #70
|
|
@ -7,8 +7,8 @@ Semantic Versioning: MAJOR.MINOR.PATCH
|
||||||
- PATCH: Bugfix, kleine Änderung, Refactor
|
- PATCH: Bugfix, kleine Änderung, Refactor
|
||||||
"""
|
"""
|
||||||
|
|
||||||
APP_VERSION = "0.9n"
|
APP_VERSION = "0.9o"
|
||||||
BUILD_DATE = "2026-04-05"
|
BUILD_DATE = "2026-04-09"
|
||||||
DB_SCHEMA_VERSION = "20260406e" # Migration 041
|
DB_SCHEMA_VERSION = "20260406e" # Migration 041
|
||||||
|
|
||||||
MODULE_VERSIONS = {
|
MODULE_VERSIONS = {
|
||||||
|
|
|
||||||
139
frontend/src/components/workflow/panels/WorkflowExecutePanel.jsx
Normal file
139
frontend/src/components/workflow/panels/WorkflowExecutePanel.jsx
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { api } from '../../../utils/api'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WorkflowExecutePanel - Execution Controls für Workflow Editor
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Execute Button mit Loading State
|
||||||
|
* - Error Handling
|
||||||
|
* - Success State
|
||||||
|
* - Debug Mode Toggle
|
||||||
|
*
|
||||||
|
* Part 2: Frontend Execute Integration
|
||||||
|
*/
|
||||||
|
export function WorkflowExecutePanel({
|
||||||
|
currentPrompt,
|
||||||
|
onExecutionComplete,
|
||||||
|
disabled = false
|
||||||
|
}) {
|
||||||
|
const [executing, setExecuting] = useState(false)
|
||||||
|
const [error, setError] = useState(null)
|
||||||
|
const [debugMode, setDebugMode] = useState(true)
|
||||||
|
|
||||||
|
const handleExecute = async () => {
|
||||||
|
if (!currentPrompt || !currentPrompt.slug) {
|
||||||
|
setError('Workflow muss zuerst gespeichert werden (benötigt slug)')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setExecuting(true)
|
||||||
|
setError(null)
|
||||||
|
|
||||||
|
console.log('🚀 Executing workflow:', currentPrompt.slug)
|
||||||
|
|
||||||
|
const result = await api.executeWorkflow(
|
||||||
|
currentPrompt.slug,
|
||||||
|
null, // variables (später erweiterbar)
|
||||||
|
debugMode,
|
||||||
|
false // save (nicht in ai_insights speichern)
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log('✅ Workflow execution completed:', result)
|
||||||
|
|
||||||
|
// Success - kurze Bestätigung
|
||||||
|
if (result.status === 'completed') {
|
||||||
|
// Callback mit result
|
||||||
|
if (onExecutionComplete) {
|
||||||
|
onExecutionComplete(result)
|
||||||
|
}
|
||||||
|
} else if (result.status === 'failed') {
|
||||||
|
setError(result.error || 'Workflow-Ausführung fehlgeschlagen')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('❌ Workflow execution error:', e)
|
||||||
|
setError(e.message)
|
||||||
|
} finally {
|
||||||
|
setExecuting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<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>
|
||||||
|
)
|
||||||
|
}
|
||||||
303
frontend/src/components/workflow/panels/WorkflowResultViewer.jsx
Normal file
303
frontend/src/components/workflow/panels/WorkflowResultViewer.jsx
Normal file
|
|
@ -0,0 +1,303 @@
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WorkflowResultViewer - Zeigt Execution-Ergebnisse eines Workflows
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Aggregated Result (Final Output)
|
||||||
|
* - Node States (wenn Debug Mode aktiv)
|
||||||
|
* - Collapsible Sections
|
||||||
|
* - Copy to Clipboard
|
||||||
|
*
|
||||||
|
* Part 2: Frontend Execute Integration
|
||||||
|
*/
|
||||||
|
export function WorkflowResultViewer({ result, onClose }) {
|
||||||
|
const [expandedNodes, setExpandedNodes] = useState({})
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleNode = (nodeId) => {
|
||||||
|
setExpandedNodes((prev) => ({ ...prev, [nodeId]: !prev[nodeId] }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyToClipboard = (text) => {
|
||||||
|
navigator.clipboard.writeText(text)
|
||||||
|
alert('In Zwischenablage kopiert')
|
||||||
|
}
|
||||||
|
|
||||||
|
const aggregated = result.aggregated_result || {}
|
||||||
|
const nodeStates = result.node_states || []
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
width: '600px',
|
||||||
|
background: 'var(--surface)',
|
||||||
|
borderLeft: '1px solid var(--border)',
|
||||||
|
boxShadow: '-2px 0 8px rgba(0,0,0,0.1)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
zIndex: 1000
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '16px',
|
||||||
|
borderBottom: '1px solid var(--border)',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
background: 'var(--surface2)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h2 style={{ margin: 0, fontSize: '18px' }}>
|
||||||
|
Workflow-Ergebnis
|
||||||
|
{result.status === 'completed' && (
|
||||||
|
<span style={{ color: 'var(--accent)', marginLeft: '8px' }}>✓</span>
|
||||||
|
)}
|
||||||
|
{result.status === 'failed' && (
|
||||||
|
<span style={{ color: 'var(--danger)', marginLeft: '8px' }}>✗</span>
|
||||||
|
)}
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
style={{
|
||||||
|
background: 'none',
|
||||||
|
border: 'none',
|
||||||
|
fontSize: '24px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: 'var(--text3)',
|
||||||
|
padding: '4px 8px',
|
||||||
|
lineHeight: 1
|
||||||
|
}}
|
||||||
|
title="Schließen"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
overflowY: 'auto',
|
||||||
|
padding: '16px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Execution Info */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginBottom: '16px',
|
||||||
|
padding: '12px',
|
||||||
|
background: 'var(--bg)',
|
||||||
|
borderRadius: '8px',
|
||||||
|
fontSize: '12px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div><strong>Execution ID:</strong> {result.execution_id}</div>
|
||||||
|
<div><strong>Status:</strong> {result.status}</div>
|
||||||
|
{aggregated.total_nodes && (
|
||||||
|
<div><strong>Nodes:</strong> {aggregated.executed_nodes}/{aggregated.total_nodes}</div>
|
||||||
|
)}
|
||||||
|
{aggregated.failed_nodes > 0 && (
|
||||||
|
<div style={{ color: 'var(--danger)' }}>
|
||||||
|
<strong>Failed Nodes:</strong> {aggregated.failed_nodes}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Final Output */}
|
||||||
|
<div style={{ marginBottom: '16px' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: '8px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3 style={{ margin: 0, fontSize: '14px', fontWeight: 600 }}>
|
||||||
|
Final Output
|
||||||
|
</h3>
|
||||||
|
{aggregated.analysis_core && (
|
||||||
|
<button
|
||||||
|
className="btn-secondary"
|
||||||
|
style={{ fontSize: '11px', padding: '4px 8px' }}
|
||||||
|
onClick={() => copyToClipboard(aggregated.analysis_core)}
|
||||||
|
>
|
||||||
|
📋 Copy
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '12px',
|
||||||
|
background: 'var(--bg)',
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
fontSize: '13px',
|
||||||
|
lineHeight: 1.6,
|
||||||
|
maxHeight: '400px',
|
||||||
|
overflowY: 'auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{aggregated.analysis_core || aggregated.combined_analysis || '(Kein Output)'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* All Signals */}
|
||||||
|
{aggregated.all_signals && aggregated.all_signals.length > 0 && (
|
||||||
|
<div style={{ marginBottom: '16px' }}>
|
||||||
|
<h3 style={{ margin: '0 0 8px 0', fontSize: '14px', fontWeight: 600 }}>
|
||||||
|
All Signals ({aggregated.all_signals.length})
|
||||||
|
</h3>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '12px',
|
||||||
|
background: 'var(--bg)',
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
fontSize: '12px',
|
||||||
|
maxHeight: '200px',
|
||||||
|
overflowY: 'auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{aggregated.all_signals.map((signal, idx) => (
|
||||||
|
<div key={idx} style={{ marginBottom: '8px' }}>
|
||||||
|
• {signal}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Node States (Debug Info) */}
|
||||||
|
{nodeStates.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<h3 style={{ margin: '0 0 8px 0', fontSize: '14px', fontWeight: 600 }}>
|
||||||
|
Node States (Debug)
|
||||||
|
</h3>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||||
|
{nodeStates.map((node) => (
|
||||||
|
<div
|
||||||
|
key={node.node_id}
|
||||||
|
style={{
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
borderRadius: '8px',
|
||||||
|
overflow: 'hidden'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Node Header */}
|
||||||
|
<div
|
||||||
|
onClick={() => toggleNode(node.node_id)}
|
||||||
|
style={{
|
||||||
|
padding: '10px 12px',
|
||||||
|
background: 'var(--surface2)',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 500
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{node.node_type === 'start' && '🚀'}
|
||||||
|
{node.node_type === 'analysis' && '🤖'}
|
||||||
|
{node.node_type === 'logic' && '⚡'}
|
||||||
|
{node.node_type === 'join' && '🔀'}
|
||||||
|
{node.node_type === 'end' && '🏁'}
|
||||||
|
{' '}
|
||||||
|
{node.node_label || node.node_id}
|
||||||
|
{node.status === 'skipped' && (
|
||||||
|
<span style={{ color: 'var(--text3)', marginLeft: '8px' }}>
|
||||||
|
(skipped)
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: '16px' }}>
|
||||||
|
{expandedNodes[node.node_id] ? '▼' : '▶'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Node Details */}
|
||||||
|
{expandedNodes[node.node_id] && (
|
||||||
|
<div style={{ padding: '12px', fontSize: '12px' }}>
|
||||||
|
{node.output && (
|
||||||
|
<div style={{ marginBottom: '8px' }}>
|
||||||
|
<strong>Output:</strong>
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
marginTop: '4px',
|
||||||
|
padding: '8px',
|
||||||
|
background: 'var(--bg)',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '11px',
|
||||||
|
overflowX: 'auto',
|
||||||
|
maxHeight: '200px',
|
||||||
|
overflowY: 'auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{typeof node.output === 'string'
|
||||||
|
? node.output
|
||||||
|
: JSON.stringify(node.output, null, 2)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{node.error && (
|
||||||
|
<div style={{ color: 'var(--danger)', marginBottom: '8px' }}>
|
||||||
|
<strong>Error:</strong> {node.error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{node.metadata && (
|
||||||
|
<div style={{ marginTop: '8px' }}>
|
||||||
|
<strong>Metadata:</strong>
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
marginTop: '4px',
|
||||||
|
padding: '8px',
|
||||||
|
background: 'var(--bg)',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '11px',
|
||||||
|
overflowX: 'auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{JSON.stringify(node.metadata, null, 2)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Error (if failed) */}
|
||||||
|
{result.error && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: '16px',
|
||||||
|
padding: '12px',
|
||||||
|
background: 'var(--danger)',
|
||||||
|
color: 'white',
|
||||||
|
borderRadius: '8px',
|
||||||
|
fontSize: '13px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<strong>Error:</strong> {result.error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,8 @@ import { QuestionAugmentationPanel } from '../components/workflow/panels/Questio
|
||||||
import { LogicExpressionEditor } from '../components/workflow/panels/LogicExpressionEditor'
|
import { LogicExpressionEditor } from '../components/workflow/panels/LogicExpressionEditor'
|
||||||
import { FallbackConfig } from '../components/workflow/panels/FallbackConfig'
|
import { FallbackConfig } from '../components/workflow/panels/FallbackConfig'
|
||||||
import { JoinConfig } from '../components/workflow/panels/JoinConfig'
|
import { JoinConfig } from '../components/workflow/panels/JoinConfig'
|
||||||
|
import { WorkflowExecutePanel } from '../components/workflow/panels/WorkflowExecutePanel'
|
||||||
|
import { WorkflowResultViewer } from '../components/workflow/panels/WorkflowResultViewer'
|
||||||
import '../styles/workflowEditor.css'
|
import '../styles/workflowEditor.css'
|
||||||
|
|
||||||
// Node-Type Mapping
|
// Node-Type Mapping
|
||||||
|
|
@ -44,6 +46,7 @@ export default function WorkflowEditorPage() {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [availablePrompts, setAvailablePrompts] = useState([])
|
const [availablePrompts, setAvailablePrompts] = useState([])
|
||||||
|
const [executionResult, setExecutionResult] = useState(null)
|
||||||
|
|
||||||
// Load available basis prompts for Analysis nodes
|
// Load available basis prompts for Analysis nodes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -156,7 +159,7 @@ export default function WorkflowEditorPage() {
|
||||||
graph_data
|
graph_data
|
||||||
})
|
})
|
||||||
console.log('✅ Workflow created:', result)
|
console.log('✅ Workflow created:', result)
|
||||||
setCurrentPrompt({ id: result.id, name: workflowName })
|
setCurrentPrompt({ id: result.id, slug: result.slug, name: workflowName })
|
||||||
alert('Workflow erstellt!')
|
alert('Workflow erstellt!')
|
||||||
console.log('🚀 Navigating to:', `/workflow-editor/${result.id}`)
|
console.log('🚀 Navigating to:', `/workflow-editor/${result.id}`)
|
||||||
navigate(`/workflow-editor/${result.id}`)
|
navigate(`/workflow-editor/${result.id}`)
|
||||||
|
|
@ -249,6 +252,11 @@ export default function WorkflowEditorPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleExecutionComplete = (result) => {
|
||||||
|
console.log('🎯 Execution completed:', result)
|
||||||
|
setExecutionResult(result)
|
||||||
|
}
|
||||||
|
|
||||||
// ── Render ────────────────────────────────────────────────────────────────
|
// ── Render ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -273,6 +281,14 @@ export default function WorkflowEditorPage() {
|
||||||
<button className="btn-secondary" onClick={handleValidate}>
|
<button className="btn-secondary" onClick={handleValidate}>
|
||||||
Validieren {validationErrors.length > 0 ? `(${validationErrors.length} ⚠️)` : ''}
|
Validieren {validationErrors.length > 0 ? `(${validationErrors.length} ⚠️)` : ''}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* Execute Panel */}
|
||||||
|
<WorkflowExecutePanel
|
||||||
|
currentPrompt={currentPrompt}
|
||||||
|
onExecutionComplete={handleExecutionComplete}
|
||||||
|
disabled={validationErrors.length > 0}
|
||||||
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="btn-primary"
|
className="btn-primary"
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
|
|
@ -498,6 +514,14 @@ export default function WorkflowEditorPage() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Execution Result Viewer */}
|
||||||
|
{executionResult && (
|
||||||
|
<WorkflowResultViewer
|
||||||
|
result={executionResult}
|
||||||
|
onClose={() => setExecutionResult(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -392,6 +392,16 @@ export const api = {
|
||||||
if (timeframes) body.timeframes = timeframes
|
if (timeframes) body.timeframes = timeframes
|
||||||
return req('/prompts/execute?' + params, json(body))
|
return req('/prompts/execute?' + params, json(body))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Workflow Execution (Part 2: Frontend Execute Integration)
|
||||||
|
executeWorkflow: (slug, variables=null, debug=true, save=false) => {
|
||||||
|
const params = new URLSearchParams({ prompt_slug: slug })
|
||||||
|
if (debug) params.append('debug', 'true')
|
||||||
|
if (save) params.append('save', 'true')
|
||||||
|
const body = variables ? { variables } : {}
|
||||||
|
return req('/prompts/execute?' + params, json(body))
|
||||||
|
},
|
||||||
|
|
||||||
createUnifiedPrompt: (d) => req('/prompts/unified', json(d)),
|
createUnifiedPrompt: (d) => req('/prompts/unified', json(d)),
|
||||||
updateUnifiedPrompt: (id,d) => req(`/prompts/unified/${id}`, jput(d)),
|
updateUnifiedPrompt: (id,d) => req(`/prompts/unified/${id}`, jput(d)),
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user