@flowdrop/flowdrop 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/enhanced-client.js +5 -1
- package/dist/components/PipelineStatus.svelte +17 -1
- package/dist/components/PipelineStatus.svelte.d.ts +2 -0
- package/dist/components/WorkflowEditor.svelte +26 -0
- package/dist/components/playground/ChatPanel.svelte +33 -235
- package/dist/components/playground/ExecutionList.svelte +2 -6
- package/dist/components/playground/MessageBubble.svelte +61 -4
- package/dist/components/playground/PipelinePanel.svelte +17 -7
- package/dist/components/playground/PipelinePanel.svelte.d.ts +2 -1
- package/dist/components/playground/Playground.svelte +122 -72
- package/dist/components/playground/PlaygroundStudio.svelte +404 -0
- package/dist/components/playground/PlaygroundStudio.svelte.d.ts +30 -0
- package/dist/editor/index.d.ts +1 -1
- package/dist/editor/index.js +1 -1
- package/dist/playground/index.d.ts +7 -3
- package/dist/playground/index.js +14 -5
- package/dist/playground/mount.d.ts +7 -0
- package/dist/playground/mount.js +78 -81
- package/dist/services/nodeExecutionService.js +4 -2
- package/dist/services/playgroundService.d.ts +11 -4
- package/dist/services/playgroundService.js +22 -12
- package/dist/stores/playgroundStore.svelte.d.ts +22 -21
- package/dist/stores/playgroundStore.svelte.js +79 -58
- package/dist/types/playground.d.ts +3 -5
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module stores/playgroundStore
|
|
8
8
|
*/
|
|
9
|
-
import { isChatInputNode
|
|
9
|
+
import { isChatInputNode } from '../types/playground.js';
|
|
10
10
|
import { logger } from '../utils/logger.js';
|
|
11
11
|
// =========================================================================
|
|
12
12
|
// Core State
|
|
@@ -23,10 +23,6 @@ let _sessions = $state([]);
|
|
|
23
23
|
* Messages in the current session
|
|
24
24
|
*/
|
|
25
25
|
let _messages = $state([]);
|
|
26
|
-
/**
|
|
27
|
-
* Whether an execution is currently running
|
|
28
|
-
*/
|
|
29
|
-
let _isExecuting = $state(false);
|
|
30
26
|
/**
|
|
31
27
|
* Whether we are currently loading data
|
|
32
28
|
*/
|
|
@@ -42,13 +38,19 @@ let _currentWorkflow = $state(null);
|
|
|
42
38
|
/**
|
|
43
39
|
* Last polling timestamp for incremental message fetching
|
|
44
40
|
*/
|
|
45
|
-
let
|
|
41
|
+
let _lastPollSequenceNumber = $state(null);
|
|
46
42
|
/** Execution ID explicitly pinned by the user (null = follow latest) */
|
|
47
43
|
let _pinnedExecutionId = $state(null);
|
|
44
|
+
/** Incremented on every message batch that should trigger a pipeline re-fetch */
|
|
45
|
+
let _pipelineRefreshTrigger = $state(0);
|
|
48
46
|
/** Latest execution ID derived from current session's executions list */
|
|
49
47
|
const _latestExecutionId = $derived(_currentSession?.executions?.at(-1)?.id ?? null);
|
|
50
48
|
/** Active execution: pinned if set, otherwise latest */
|
|
51
49
|
const _activeExecutionId = $derived(_pinnedExecutionId ?? _latestExecutionId);
|
|
50
|
+
// Derived from server status — never manually set.
|
|
51
|
+
// Exception: updateSessionStatus('running') in handleSendMessage is an
|
|
52
|
+
// acknowledged optimistic write, overwritten by the next server response.
|
|
53
|
+
const _isExecuting = $derived(_currentSession?.status === 'running');
|
|
52
54
|
// =========================================================================
|
|
53
55
|
// Getter Functions (for reactive access in components)
|
|
54
56
|
// =========================================================================
|
|
@@ -95,10 +97,10 @@ export function getCurrentWorkflow() {
|
|
|
95
97
|
return _currentWorkflow;
|
|
96
98
|
}
|
|
97
99
|
/**
|
|
98
|
-
* Get the last poll
|
|
100
|
+
* Get the last poll sequence number cursor
|
|
99
101
|
*/
|
|
100
|
-
export function
|
|
101
|
-
return
|
|
102
|
+
export function getLastPollSequenceNumber() {
|
|
103
|
+
return _lastPollSequenceNumber;
|
|
102
104
|
}
|
|
103
105
|
// =========================================================================
|
|
104
106
|
// Derived Getters
|
|
@@ -109,6 +111,14 @@ export function getLastPollTimestamp() {
|
|
|
109
111
|
export function getSessionStatus() {
|
|
110
112
|
return _currentSession?.status ?? 'idle';
|
|
111
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Whether the user can currently send a message.
|
|
116
|
+
* False when executing, when awaiting input, or when no session exists.
|
|
117
|
+
*/
|
|
118
|
+
export function getCanSendMessage() {
|
|
119
|
+
const status = _currentSession?.status ?? 'idle';
|
|
120
|
+
return _currentSession !== null && !_isExecuting && status !== 'awaiting_input';
|
|
121
|
+
}
|
|
112
122
|
/**
|
|
113
123
|
* Get message count
|
|
114
124
|
*/
|
|
@@ -219,6 +229,14 @@ export function getLatestExecutionId() {
|
|
|
219
229
|
export function getActiveExecutionId() {
|
|
220
230
|
return _activeExecutionId;
|
|
221
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Counter that increments whenever new messages arrive and the pipeline display
|
|
234
|
+
* should re-fetch — i.e. when following latest or pinned to the latest execution.
|
|
235
|
+
* Pass to PipelinePanel's refreshTrigger prop.
|
|
236
|
+
*/
|
|
237
|
+
export function getPipelineRefreshTrigger() {
|
|
238
|
+
return _pipelineRefreshTrigger;
|
|
239
|
+
}
|
|
222
240
|
// =========================================================================
|
|
223
241
|
// Helper Functions
|
|
224
242
|
// =========================================================================
|
|
@@ -277,6 +295,8 @@ function syncExecutionsFromMessages(messages) {
|
|
|
277
295
|
..._currentSession,
|
|
278
296
|
executions: [...(_currentSession.executions ?? []), ...newExecutions]
|
|
279
297
|
};
|
|
298
|
+
// Clear any manual pin so the panel automatically follows the new run.
|
|
299
|
+
_pinnedExecutionId = null;
|
|
280
300
|
}
|
|
281
301
|
}
|
|
282
302
|
// =========================================================================
|
|
@@ -323,11 +343,14 @@ export const playgroundActions = {
|
|
|
323
343
|
// Update the latest execution status when the session reaches a terminal state.
|
|
324
344
|
// Only the last execution can be running at any time (sessions are single-pipeline),
|
|
325
345
|
// so we only need to check and update the tail entry.
|
|
326
|
-
|
|
346
|
+
// 'idle' means the run finished normally (server returns 'idle' post-completion,
|
|
347
|
+
// not 'completed'), so map it to 'completed' for the execution entry.
|
|
348
|
+
const terminalExecutionStatus = status === 'failed' ? 'failed' : status === 'completed' || status === 'idle' ? 'completed' : null;
|
|
349
|
+
if (terminalExecutionStatus && _currentSession?.executions?.length) {
|
|
327
350
|
const execs = [..._currentSession.executions];
|
|
328
351
|
const last = execs[execs.length - 1];
|
|
329
352
|
if (last.status === 'running') {
|
|
330
|
-
execs[execs.length - 1] = { ...last, status };
|
|
353
|
+
execs[execs.length - 1] = { ...last, status: terminalExecutionStatus };
|
|
331
354
|
_currentSession = { ..._currentSession, executions: execs };
|
|
332
355
|
}
|
|
333
356
|
}
|
|
@@ -402,27 +425,27 @@ export const playgroundActions = {
|
|
|
402
425
|
addMessages: (newMessages) => {
|
|
403
426
|
if (newMessages.length === 0)
|
|
404
427
|
return;
|
|
405
|
-
// Deduplicate
|
|
428
|
+
// Deduplicate against existing messages AND within the incoming batch itself.
|
|
429
|
+
// The latter matters when the backend returns the same page twice (e.g. broken
|
|
430
|
+
// offset pagination), which would otherwise create duplicate IDs in _messages
|
|
431
|
+
// and trigger Svelte's each_key_duplicate error.
|
|
406
432
|
const existingIds = new Set(_messages.map((m) => m.id));
|
|
407
|
-
const
|
|
408
|
-
|
|
433
|
+
const seenInBatch = new Set();
|
|
434
|
+
const uniqueNewMessages = newMessages.filter((m) => {
|
|
435
|
+
if (existingIds.has(m.id) || seenInBatch.has(m.id))
|
|
436
|
+
return false;
|
|
437
|
+
seenInBatch.add(m.id);
|
|
438
|
+
return true;
|
|
439
|
+
});
|
|
409
440
|
_messages = sortMessagesChronologically([..._messages, ...uniqueNewMessages]);
|
|
410
|
-
syncExecutionsFromMessages(
|
|
441
|
+
syncExecutionsFromMessages(uniqueNewMessages);
|
|
411
442
|
},
|
|
412
443
|
/**
|
|
413
444
|
* Clear all messages
|
|
414
445
|
*/
|
|
415
446
|
clearMessages: () => {
|
|
416
447
|
_messages = [];
|
|
417
|
-
|
|
418
|
-
},
|
|
419
|
-
/**
|
|
420
|
-
* Set the executing state
|
|
421
|
-
*
|
|
422
|
-
* @param executing - Whether execution is in progress
|
|
423
|
-
*/
|
|
424
|
-
setExecuting: (executing) => {
|
|
425
|
-
_isExecuting = executing;
|
|
448
|
+
_lastPollSequenceNumber = null;
|
|
426
449
|
},
|
|
427
450
|
/**
|
|
428
451
|
* Set the loading state
|
|
@@ -445,8 +468,8 @@ export const playgroundActions = {
|
|
|
445
468
|
*
|
|
446
469
|
* @param timestamp - ISO 8601 timestamp
|
|
447
470
|
*/
|
|
448
|
-
|
|
449
|
-
|
|
471
|
+
updateLastPollSequenceNumber: (seq) => {
|
|
472
|
+
_lastPollSequenceNumber = seq;
|
|
450
473
|
},
|
|
451
474
|
/**
|
|
452
475
|
* Reset all playground state
|
|
@@ -455,11 +478,11 @@ export const playgroundActions = {
|
|
|
455
478
|
_currentSession = null;
|
|
456
479
|
_sessions = [];
|
|
457
480
|
_messages = [];
|
|
458
|
-
_isExecuting = false;
|
|
459
481
|
_isLoading = false;
|
|
460
482
|
_error = null;
|
|
461
483
|
_currentWorkflow = null;
|
|
462
|
-
|
|
484
|
+
_lastPollSequenceNumber = null;
|
|
485
|
+
_pipelineRefreshTrigger = 0;
|
|
463
486
|
},
|
|
464
487
|
/**
|
|
465
488
|
* Switch to a different session
|
|
@@ -472,7 +495,7 @@ export const playgroundActions = {
|
|
|
472
495
|
if (session) {
|
|
473
496
|
_currentSession = session;
|
|
474
497
|
_messages = [];
|
|
475
|
-
|
|
498
|
+
_lastPollSequenceNumber = null;
|
|
476
499
|
}
|
|
477
500
|
},
|
|
478
501
|
pinExecution(executionId) {
|
|
@@ -480,28 +503,25 @@ export const playgroundActions = {
|
|
|
480
503
|
}
|
|
481
504
|
};
|
|
482
505
|
// =========================================================================
|
|
483
|
-
//
|
|
506
|
+
// Server Response Application
|
|
484
507
|
// =========================================================================
|
|
485
508
|
/**
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (
|
|
496
|
-
|
|
509
|
+
* Apply a server response to the store. All message and status updates from
|
|
510
|
+
* the server flow through here — polling callback, manual fetches, interrupt
|
|
511
|
+
* resolution. Nothing updates messages or session status except this function.
|
|
512
|
+
*/
|
|
513
|
+
export function applyServerResponse(response) {
|
|
514
|
+
if (response.data && response.data.length > 0) {
|
|
515
|
+
playgroundActions.addMessages(response.data);
|
|
516
|
+
// Refresh pipeline when following latest or pinned to the latest execution.
|
|
517
|
+
// Skip only when the user is viewing a historical run.
|
|
518
|
+
if (_pinnedExecutionId === null || _pinnedExecutionId === _latestExecutionId) {
|
|
519
|
+
_pipelineRefreshTrigger++;
|
|
497
520
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
};
|
|
521
|
+
}
|
|
522
|
+
if (response.sessionStatus) {
|
|
523
|
+
playgroundActions.updateSessionStatus(response.sessionStatus);
|
|
524
|
+
}
|
|
505
525
|
}
|
|
506
526
|
// =========================================================================
|
|
507
527
|
// Utilities
|
|
@@ -532,14 +552,17 @@ export function getMessagesSnapshot() {
|
|
|
532
552
|
return _messages;
|
|
533
553
|
}
|
|
534
554
|
/**
|
|
535
|
-
* Get the latest message
|
|
555
|
+
* Get the sequence number of the latest message, used to seed incremental polling.
|
|
536
556
|
*
|
|
537
|
-
* @returns
|
|
557
|
+
* @returns Sequence number of the last message, or null
|
|
538
558
|
*/
|
|
539
|
-
export function
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
559
|
+
export function getLatestSequenceNumber() {
|
|
560
|
+
for (let i = _messages.length - 1; i >= 0; i--) {
|
|
561
|
+
if (_messages[i].sequenceNumber !== undefined) {
|
|
562
|
+
return _messages[i].sequenceNumber;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return null;
|
|
543
566
|
}
|
|
544
567
|
/**
|
|
545
568
|
* Subscribe to session status changes using $effect.root.
|
|
@@ -568,17 +591,15 @@ export function subscribeToSessionStatus(callback) {
|
|
|
568
591
|
* has stopped but new messages may exist on the server.
|
|
569
592
|
*
|
|
570
593
|
* @param fetchMessages - Async function to fetch messages from the API
|
|
571
|
-
* @param isTerminalStatus - Optional override for terminal status check
|
|
572
594
|
* @returns Promise that resolves when messages are refreshed
|
|
573
595
|
*/
|
|
574
|
-
export async function refreshSessionMessages(fetchMessages
|
|
596
|
+
export async function refreshSessionMessages(fetchMessages) {
|
|
575
597
|
const session = _currentSession;
|
|
576
598
|
if (!session)
|
|
577
599
|
return;
|
|
578
600
|
try {
|
|
579
601
|
const response = await fetchMessages(session.id);
|
|
580
|
-
|
|
581
|
-
callback(response);
|
|
602
|
+
applyServerResponse(response);
|
|
582
603
|
}
|
|
583
604
|
catch (err) {
|
|
584
605
|
logger.error('[playgroundStore] Failed to refresh messages:', err);
|
|
@@ -110,6 +110,8 @@ export interface PlaygroundMessageMetadata {
|
|
|
110
110
|
outputs?: Record<string, unknown>;
|
|
111
111
|
/** User's display name for user-role messages (from backend) */
|
|
112
112
|
userName?: string;
|
|
113
|
+
/** Subsystem that produced this message (e.g. 'pipeline', 'job', 'queue', 'cron') */
|
|
114
|
+
source?: string;
|
|
113
115
|
/** Allow additional properties */
|
|
114
116
|
[key: string]: unknown;
|
|
115
117
|
}
|
|
@@ -146,11 +148,7 @@ export interface PlaygroundMessage {
|
|
|
146
148
|
timestamp: string;
|
|
147
149
|
/** Message status */
|
|
148
150
|
status?: PlaygroundMessageStatus;
|
|
149
|
-
/**
|
|
150
|
-
* Sequence number for ordering messages
|
|
151
|
-
* - User messages: incrementing numbers (1, 2, 3, ...)
|
|
152
|
-
* - Assistant/system responses: 0 (sorted after parent via parentMessageId)
|
|
153
|
-
*/
|
|
151
|
+
/** Incrementing sequence number for chronological ordering. All message roles receive unique incrementing numbers (1, 2, 3, ...). Primary sort key. */
|
|
154
152
|
sequenceNumber?: number;
|
|
155
153
|
/** Parent message ID (for assistant responses linked to user messages) */
|
|
156
154
|
parentMessageId?: string;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "A drop-in visual workflow editor for any web application. You own the backend. You own the data. You own the orchestration.",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"private": false,
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.10.0",
|
|
7
7
|
"author": "Shibin Das (D34dMan)",
|
|
8
8
|
"bugs": {
|
|
9
9
|
"url": "https://github.com/flowdrop-io/flowdrop/issues"
|