langflower 0.0.9 → 0.0.10
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/package.json +2 -2
- package/ui-dist/index.html +2 -2
- package/ui-dist/main-Z7CPFXFA.js +313 -0
- package/ui-dist/styles-2TMDAUD7.css +1 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-reducer.js +0 -12
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-types.d.ts +2 -7
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-types.js +1 -1
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-agent-loop.d.ts +0 -3
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-agent-loop.js +22 -36
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-llm-loop.d.ts +1 -7
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-llm-loop.js +12 -35
- package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.d.ts +3 -10
- package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.js +9 -14
- package/vendor/common-nodes/dist/ai/features/path-choice/run-reactive-path-choice-loop.d.ts +0 -3
- package/vendor/common-nodes/dist/ai/features/path-choice/run-reactive-path-choice-loop.js +2 -15
- package/vendor/common-nodes/dist/ai/features/sub-agent-protocol.d.ts +1 -0
- package/vendor/common-nodes/dist/ai/features/sub-agent-protocol.js +1 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-recovery-ui-schema.d.ts +1 -1
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-recovery-ui-schema.js +1 -1
- package/vendor/common-nodes/dist/ai/features/wait-for-subagent-result.d.ts +1 -0
- package/vendor/common-nodes/dist/ai/features/wait-for-subagent-result.js +1 -0
- package/vendor/common-nodes/dist/ai/nodes/critique/node.d.ts +3 -3
- package/vendor/common-nodes/dist/ai/nodes/critique/node.js +5 -18
- package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.d.ts +3 -3
- package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.js +3 -10
- package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.d.ts +3 -3
- package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.js +2 -9
- package/vendor/common-nodes/dist/ai/nodes/review/node.d.ts +3 -3
- package/vendor/common-nodes/dist/ai/nodes/review/node.js +5 -18
- package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.d.ts +5 -5
- package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.js +211 -96
- package/vendor/common-nodes/dist/catalog.js +2 -0
- package/vendor/common-nodes/dist/mcp/mcp-http/node.d.ts +4 -4
- package/vendor/common-nodes/dist/mcp/mcp-http/node.js +9 -10
- package/vendor/common-nodes/dist/mcp/mcp-stdio/node.d.ts +4 -4
- package/vendor/common-nodes/dist/mcp/mcp-stdio/node.js +9 -10
- package/vendor/common-nodes/dist/tools/collect-agent-tool-handles.d.ts +4 -4
- package/vendor/common-nodes/dist/tools/collect-agent-tool-handles.js +3 -11
- package/vendor/common-nodes/dist/tools/inventory-tool-round.d.ts +0 -10
- package/vendor/common-nodes/dist/tools/inventory-tool-round.js +0 -84
- package/vendor/common-nodes/dist/tools/tool-collection/node.d.ts +20 -0
- package/vendor/common-nodes/dist/tools/tool-collection/node.js +42 -0
- package/vendor/common-nodes/package.json +1 -5
- package/vendor/compiler/package.json +1 -1
- package/vendor/node-sdk/dist/node-factory/define-llm-node/default-llm-ports.d.ts +3 -7
- package/vendor/node-sdk/dist/node-factory/define-llm-node/default-llm-ports.js +4 -44
- package/vendor/node-sdk/dist/node-factory/define-llm-node/define-llm-node.d.ts +3 -5
- package/vendor/node-sdk/dist/node-factory/define-llm-node/define-llm-node.js +3 -5
- package/vendor/node-sdk/dist/node-factory/define-llm-node/llm-inventory-wire.d.ts +1 -0
- package/vendor/node-sdk/dist/node-factory/define-llm-node/llm-inventory-wire.js +1 -0
- package/vendor/node-sdk/dist/node-factory/define-mcp/mcp-handle.d.ts +3 -4
- package/vendor/node-sdk/dist/node-factory/define-mcp/mcp-handle.js +1 -1
- package/vendor/node-sdk/dist/node-factory/define-reactive-node/types.d.ts +2 -4
- package/vendor/node-sdk/dist/node-factory/define-reactive-node/ui-schema-inference.d.ts +1 -1
- package/vendor/node-sdk/package.json +1 -1
- package/vendor/runtime/dist/port-feed-override.d.ts +13 -0
- package/vendor/runtime/dist/port-feed-override.js +21 -0
- package/vendor/runtime/dist/port-signal-from-response.d.ts +15 -0
- package/vendor/runtime/dist/port-signal-from-response.js +48 -0
- package/vendor/runtime/dist/runtime-runner.d.ts +8 -3
- package/vendor/runtime/dist/runtime-runner.js +102 -81
- package/vendor/runtime/dist/runtime.d.ts +3 -2
- package/vendor/runtime/dist/runtime.js +1 -1
- package/vendor/runtime/dist/testing/workflows/workflow-events.d.ts +2 -0
- package/vendor/runtime/dist/testing/workflows/workflow-events.js +7 -5
- package/vendor/runtime/dist/types.d.ts +25 -8
- package/vendor/runtime/dist/types.js +3 -0
- package/vendor/runtime/package.json +1 -1
- package/vendor/server/dist/bridge/build-execution-context.js +3 -3
- package/vendor/server/dist/bridge/get-live-wired-tools.d.ts +3 -2
- package/vendor/server/dist/bridge/get-live-wired-tools.js +8 -5
- package/vendor/server/dist/bridge/wire-runner-handlers.js +2 -2
- package/vendor/server/dist/bridge/wire-workflow-handlers.js +23 -9
- package/vendor/server/dist/checkpoint/run-checkpoint-session.js +3 -3
- package/vendor/server/dist/session/reset-session-execution-feed.d.ts +6 -0
- package/vendor/server/dist/session/reset-session-execution-feed.js +8 -0
- package/vendor/server/skeleton/nodes/my-nodes/package.json +1 -1
- package/vendor/server/skeleton/skills/langflower-helper/SKILL.md +36 -22
- package/vendor/server/skeleton/skills/langflower-helper/architecture.md +7 -1
- package/vendor/server/skeleton/skills/langflower-helper/layout.md +3 -2
- package/vendor/server/skeleton/skills/langflower-workflow-writer/SKILL.md +12 -8
- package/vendor/server/skeleton/workflows/kb-create.json +15 -43
- package/vendor/server/skeleton/workflows/kb-navigate.json +8 -22
- package/vendor/server/skeleton/workflows/simple-coder.json +18 -46
- package/vendor/server/skeleton/workflows/starter.json +8 -22
- package/vendor/shared/dist/execution/derive-run-settle-outcome.js +2 -2
- package/vendor/shared/dist/execution/derive-run-settle-outcome.test.js +11 -8
- package/vendor/shared/dist/langflower-ws-waits.d.ts +3 -1
- package/vendor/shared/dist/langflower-ws-waits.js +2 -2
- package/ui-dist/main-2JJGCNTD.js +0 -313
- package/ui-dist/styles-EOXODOI7.css +0 -1
|
@@ -1,60 +1,24 @@
|
|
|
1
|
-
import { combineStatefulObservables,
|
|
1
|
+
import { combineStatefulObservables, serializeResponse, statefulObservable, } from '@rx-evo/stateful-observable';
|
|
2
2
|
import { BehaviorSubject, filter, merge, of, Subject, Subscription, tap, zip, } from 'rxjs';
|
|
3
3
|
import { checkpointPortIdForSlot, getBypassConnection, isBypassPort, } from './bypass-ports.js';
|
|
4
4
|
import { normalizePortErrorValue } from './normalize-port-error-value.js';
|
|
5
5
|
import { edgePortSlotKey, parseSlotKey, slotKey, } from './port-meta.js';
|
|
6
6
|
import { clusterHasChatEntry, collectClusterSlotKeys, } from './runtime-helpers.js';
|
|
7
|
+
const toResponseDto = (response) => {
|
|
8
|
+
if (response === null || response === undefined) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return serializeResponse(response);
|
|
12
|
+
};
|
|
7
13
|
const isConnectionInactive = (connection) => {
|
|
8
14
|
let inactive = true;
|
|
9
15
|
const sub = connection.raw$.subscribe((raw) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
inactive = isInactive(raw) || isSymbolStateSentinel(raw);
|
|
16
|
+
const dto = toResponseDto(raw);
|
|
17
|
+
inactive = dto === null || 'inactive' in dto;
|
|
13
18
|
});
|
|
14
19
|
sub.unsubscribe();
|
|
15
20
|
return inactive;
|
|
16
21
|
};
|
|
17
|
-
/**
|
|
18
|
-
* `@rx-evo` status sentinels are `{ state: Symbol(...) }` (loading / inactive).
|
|
19
|
-
* When the sentinel is created by a different module instance than the
|
|
20
|
-
* `isLoading` / `isInactive` guards (CJS/ESM dual-package hazard), identity
|
|
21
|
-
* checks fail and `isSuccess` may return true — telemetry then emits
|
|
22
|
-
* `state:'value'` with that object (JSON → `{}`), or `applyPortDefaults`
|
|
23
|
-
* skips seeding and LLM `combineInputs` stalls.
|
|
24
|
-
*/
|
|
25
|
-
const isSymbolStateSentinel = (signal) => {
|
|
26
|
-
if (typeof signal !== 'object' ||
|
|
27
|
-
signal === null ||
|
|
28
|
-
Array.isArray(signal)) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
const keys = Object.keys(signal);
|
|
32
|
-
if (keys.length !== 1 || keys[0] !== 'state') {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
const state = signal.state;
|
|
36
|
-
return typeof state === 'symbol';
|
|
37
|
-
};
|
|
38
|
-
const resolveSignalState = (response) => {
|
|
39
|
-
const signal = response;
|
|
40
|
-
if (isError(signal)) {
|
|
41
|
-
return {
|
|
42
|
-
state: 'error',
|
|
43
|
-
value: normalizePortErrorValue(signal.error),
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
if (isLoading(signal) || isSymbolStateSentinel(signal)) {
|
|
47
|
-
return { state: 'pending', value: undefined };
|
|
48
|
-
}
|
|
49
|
-
if (isSuccess(signal)) {
|
|
50
|
-
return { state: 'value', value: signal };
|
|
51
|
-
}
|
|
52
|
-
return null;
|
|
53
|
-
};
|
|
54
|
-
/** Drop errors and loading so edges only carry success values. */
|
|
55
|
-
const isEdgeForwardable = (response) => !isError(response) &&
|
|
56
|
-
!isLoading(response) &&
|
|
57
|
-
!isSymbolStateSentinel(response);
|
|
58
22
|
export class RuntimeRunner {
|
|
59
23
|
editor;
|
|
60
24
|
static EMPTY_EVENT_LOG = [];
|
|
@@ -63,6 +27,8 @@ export class RuntimeRunner {
|
|
|
63
27
|
logEvents;
|
|
64
28
|
eventLogBuffer = [];
|
|
65
29
|
activeRun;
|
|
30
|
+
pendingWireRunId;
|
|
31
|
+
pendingWire;
|
|
66
32
|
disposed = false;
|
|
67
33
|
status$ = this.statusSubject.asObservable();
|
|
68
34
|
/** Synchronous snapshot of {@link status$}. */
|
|
@@ -143,6 +109,7 @@ export class RuntimeRunner {
|
|
|
143
109
|
this.finishRun(resolvedRunId);
|
|
144
110
|
return resolvedRunId;
|
|
145
111
|
}
|
|
112
|
+
this.assertResumeOutputSnapshots(completedNodeIds, outputSnapshots);
|
|
146
113
|
return this.runScope(new Set(nodes.map((node) => node.nodeId)), new Set(edges.map((edge) => edge.edgeId)), options.initialPayload, resolvedRunId, {
|
|
147
114
|
completedNodeIds,
|
|
148
115
|
outputSnapshots,
|
|
@@ -169,6 +136,7 @@ export class RuntimeRunner {
|
|
|
169
136
|
return false;
|
|
170
137
|
}
|
|
171
138
|
const runId = this.runScope(cluster.nodeIds, cluster.edgeIds);
|
|
139
|
+
this.flushPendingWire();
|
|
172
140
|
const pushedRunId = this.pushIntoActiveRun(cfg);
|
|
173
141
|
if (pushedRunId === false) {
|
|
174
142
|
this.interrupt('cancel');
|
|
@@ -181,6 +149,7 @@ export class RuntimeRunner {
|
|
|
181
149
|
if (this.statusSubject.value !== 'running') {
|
|
182
150
|
return;
|
|
183
151
|
}
|
|
152
|
+
this.cancelPendingWire();
|
|
184
153
|
this.teardownRun();
|
|
185
154
|
this.setStatus('stopped');
|
|
186
155
|
}
|
|
@@ -206,6 +175,7 @@ export class RuntimeRunner {
|
|
|
206
175
|
return;
|
|
207
176
|
}
|
|
208
177
|
if (this.statusSubject.value === 'running') {
|
|
178
|
+
this.cancelPendingWire();
|
|
209
179
|
this.teardownRun();
|
|
210
180
|
}
|
|
211
181
|
this.disposed = true;
|
|
@@ -239,18 +209,26 @@ export class RuntimeRunner {
|
|
|
239
209
|
}
|
|
240
210
|
this.eventsSubject.next(event);
|
|
241
211
|
}
|
|
242
|
-
emitPortEvent(_runId, portDir, nodeId, portId, portIdx,
|
|
212
|
+
emitPortEvent(_runId, portDir, nodeId, portId, portIdx, raw, edgeIds, defaultFeed) {
|
|
213
|
+
const dto = toResponseDto(raw);
|
|
214
|
+
if (dto === null) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
const response = 'error' in dto
|
|
218
|
+
? { error: normalizePortErrorValue(dto.error) }
|
|
219
|
+
: dto;
|
|
220
|
+
const feed = defaultFeed ?? null;
|
|
243
221
|
const portIdStr = typeof portId === 'string' ? portId : String(portId);
|
|
244
222
|
this.emitRunnerEvent([
|
|
245
223
|
portDir,
|
|
246
224
|
nodeId,
|
|
247
225
|
portIdStr,
|
|
248
|
-
|
|
249
|
-
value,
|
|
226
|
+
response,
|
|
250
227
|
portIdx,
|
|
251
228
|
edgeIds,
|
|
252
|
-
feed
|
|
229
|
+
feed,
|
|
253
230
|
]);
|
|
231
|
+
return 'value' in response;
|
|
254
232
|
}
|
|
255
233
|
canPreparePushedInput(node, portId) {
|
|
256
234
|
const connection = node.inputs[portId];
|
|
@@ -268,6 +246,7 @@ export class RuntimeRunner {
|
|
|
268
246
|
edge.toPort[1] === 0);
|
|
269
247
|
}
|
|
270
248
|
pushIntoActiveRun(cfg) {
|
|
249
|
+
this.flushPendingWire();
|
|
271
250
|
const run = this.activeRun;
|
|
272
251
|
if (run === undefined || !run.scopeNodeIds.has(cfg.nodeId)) {
|
|
273
252
|
return false;
|
|
@@ -281,7 +260,7 @@ export class RuntimeRunner {
|
|
|
281
260
|
if (existing !== undefined) {
|
|
282
261
|
// Telemetry before deliver so HITL input-received precedes cascade
|
|
283
262
|
// (output-emitted / downstream input-received) in the event log.
|
|
284
|
-
this.emitPortEvent(run.runId, 'in', cfg.nodeId, cfg.portId, 0,
|
|
263
|
+
this.emitPortEvent(run.runId, 'in', cfg.nodeId, cfg.portId, 0, cfg.payload, []);
|
|
285
264
|
existing.source.next(cfg.payload);
|
|
286
265
|
return run.runId;
|
|
287
266
|
}
|
|
@@ -300,15 +279,15 @@ export class RuntimeRunner {
|
|
|
300
279
|
connection,
|
|
301
280
|
});
|
|
302
281
|
run.pushedInputSources.set(key, { source });
|
|
303
|
-
this.emitPortEvent(run.runId, 'in', cfg.nodeId, cfg.portId, 0,
|
|
282
|
+
this.emitPortEvent(run.runId, 'in', cfg.nodeId, cfg.portId, 0, cfg.payload, []);
|
|
304
283
|
source.next(cfg.payload);
|
|
305
284
|
return run.runId;
|
|
306
285
|
}
|
|
307
286
|
/**
|
|
308
287
|
* Returns `output` wrapped with a `tap` that emits `output-emitted`
|
|
309
|
-
* telemetry, then
|
|
310
|
-
* loading sentinels stay visible
|
|
311
|
-
*
|
|
288
|
+
* telemetry, then forward only success values on edges so errors and
|
|
289
|
+
* loading sentinels stay visible on the source node (loading must not
|
|
290
|
+
* appear as `{}` downstream).
|
|
312
291
|
* The wrapper is the value the downstream connects to, so the event fires
|
|
313
292
|
* *as part of* the dataflow delivery — there is no separate refCounted
|
|
314
293
|
* subscriber, which is what previously reordered events against downstream
|
|
@@ -322,12 +301,8 @@ export class RuntimeRunner {
|
|
|
322
301
|
return output
|
|
323
302
|
.pipe(tap({
|
|
324
303
|
next: (response) => {
|
|
325
|
-
const
|
|
326
|
-
if (
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
this.emitPortEvent(run.runId, 'out', nodeId, portId, 0, resolved.state, resolved.value, edgeIds, output.meta.feed);
|
|
330
|
-
if (resolved.state === 'value' &&
|
|
304
|
+
const emittedValue = this.emitPortEvent(run.runId, 'out', nodeId, portId, 0, response, edgeIds, output.meta.feed);
|
|
305
|
+
if (emittedValue &&
|
|
331
306
|
node !== false &&
|
|
332
307
|
node.stopsRun === true) {
|
|
333
308
|
const finishRunId = run.runId;
|
|
@@ -338,7 +313,10 @@ export class RuntimeRunner {
|
|
|
338
313
|
});
|
|
339
314
|
}
|
|
340
315
|
},
|
|
341
|
-
}), filter(
|
|
316
|
+
}), filter((response) => {
|
|
317
|
+
const dto = toResponseDto(response);
|
|
318
|
+
return dto !== null && 'value' in dto;
|
|
319
|
+
}))
|
|
342
320
|
.with({ meta: output.meta });
|
|
343
321
|
}
|
|
344
322
|
/**
|
|
@@ -353,11 +331,7 @@ export class RuntimeRunner {
|
|
|
353
331
|
return source
|
|
354
332
|
.pipe(tap({
|
|
355
333
|
next: (response) => {
|
|
356
|
-
|
|
357
|
-
if (resolved === null) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
this.emitPortEvent(run.runId, 'in', nodeId, portId, slotIndex, resolved.state, resolved.value, edgeIds);
|
|
334
|
+
this.emitPortEvent(run.runId, 'in', nodeId, portId, slotIndex, response, edgeIds);
|
|
361
335
|
},
|
|
362
336
|
}))
|
|
363
337
|
.with({ meta: source.meta });
|
|
@@ -373,25 +347,76 @@ export class RuntimeRunner {
|
|
|
373
347
|
this.setStatus('idle');
|
|
374
348
|
this.teardownRun();
|
|
375
349
|
}
|
|
350
|
+
assertResumeOutputSnapshots(completedNodeIds, outputSnapshots) {
|
|
351
|
+
const nodesById = new Map(this.editor.getNodes().map((node) => [node.nodeId, node]));
|
|
352
|
+
for (const edge of this.editor.getEdges()) {
|
|
353
|
+
if (!completedNodeIds.has(edge.fromNodeId) ||
|
|
354
|
+
completedNodeIds.has(edge.toNodeId)) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const fromNode = nodesById.get(edge.fromNodeId);
|
|
358
|
+
if (fromNode === undefined) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const [fromPortId, fromSlotIndex] = edge.fromPort;
|
|
362
|
+
const snapshotPortId = checkpointPortIdForSlot(fromNode, fromPortId, fromSlotIndex);
|
|
363
|
+
const snapshotValue = outputSnapshots
|
|
364
|
+
.get(edge.fromNodeId)
|
|
365
|
+
?.get(snapshotPortId);
|
|
366
|
+
if (snapshotValue === undefined) {
|
|
367
|
+
throw new Error(`Resume missing output snapshot for ${edge.fromNodeId}.${fromPortId}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
cancelPendingWire() {
|
|
372
|
+
this.pendingWire = undefined;
|
|
373
|
+
this.pendingWireRunId = undefined;
|
|
374
|
+
}
|
|
375
|
+
flushPendingWire() {
|
|
376
|
+
const wire = this.pendingWire;
|
|
377
|
+
if (wire === undefined) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
this.pendingWire = undefined;
|
|
381
|
+
this.pendingWireRunId = undefined;
|
|
382
|
+
wire();
|
|
383
|
+
}
|
|
376
384
|
runScope(scopeNodeIds, scopeEdgeIds, initialPayload, runId, resumeOverlay) {
|
|
377
385
|
this.assertNotDisposed();
|
|
378
386
|
if (this.statusSubject.value === 'running') {
|
|
379
387
|
return false;
|
|
380
388
|
}
|
|
381
389
|
const resolvedRunId = runId ?? crypto.randomUUID();
|
|
382
|
-
this.editor.setLocked(true);
|
|
383
|
-
try {
|
|
384
|
-
this.activeRun = this.wireScope(resolvedRunId, scopeEdgeIds, scopeNodeIds, initialPayload, resumeOverlay);
|
|
385
|
-
}
|
|
386
|
-
catch (error) {
|
|
387
|
-
this.editor.setLocked(false);
|
|
388
|
-
throw error;
|
|
389
|
-
}
|
|
390
390
|
this.setStatus('running');
|
|
391
|
+
this.editor.setLocked(true);
|
|
392
|
+
this.pendingWireRunId = resolvedRunId;
|
|
393
|
+
this.pendingWire = () => {
|
|
394
|
+
try {
|
|
395
|
+
this.activeRun = this.wireScope(resolvedRunId, scopeEdgeIds, scopeNodeIds, initialPayload, resumeOverlay);
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
this.editor.setLocked(false);
|
|
399
|
+
this.setStatus('idle');
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
queueMicrotask(() => {
|
|
404
|
+
if (this.pendingWireRunId !== resolvedRunId) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
this.flushPendingWire();
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
// Status/lock already restored in pendingWire.
|
|
412
|
+
}
|
|
413
|
+
});
|
|
391
414
|
return resolvedRunId;
|
|
392
415
|
}
|
|
393
416
|
teardownRun() {
|
|
417
|
+
this.cancelPendingWire();
|
|
394
418
|
if (this.activeRun === undefined) {
|
|
419
|
+
this.editor.setLocked(false);
|
|
395
420
|
return;
|
|
396
421
|
}
|
|
397
422
|
for (const wired of this.activeRun.wiredSlots) {
|
|
@@ -429,7 +454,7 @@ export class RuntimeRunner {
|
|
|
429
454
|
connection.connect(of(seed.value).pipe(tap({
|
|
430
455
|
next: (value) => {
|
|
431
456
|
if (typeof seed.portId !== 'symbol') {
|
|
432
|
-
this.emitPortEvent(runId, 'in', nodeId, seed.portId, seed.slotIndex,
|
|
457
|
+
this.emitPortEvent(runId, 'in', nodeId, seed.portId, seed.slotIndex, value, []);
|
|
433
458
|
}
|
|
434
459
|
},
|
|
435
460
|
})));
|
|
@@ -467,7 +492,7 @@ export class RuntimeRunner {
|
|
|
467
492
|
}
|
|
468
493
|
connection.connect(of(defaultValue).pipe(tap({
|
|
469
494
|
next: (value) => {
|
|
470
|
-
this.emitPortEvent(runId, 'in', nodeId, portId, 0,
|
|
495
|
+
this.emitPortEvent(runId, 'in', nodeId, portId, 0, value, []);
|
|
471
496
|
},
|
|
472
497
|
})));
|
|
473
498
|
wiredSlotKeys.add(key);
|
|
@@ -701,11 +726,7 @@ export class RuntimeRunner {
|
|
|
701
726
|
}).with({ meta });
|
|
702
727
|
group.connection.connect(combined.pipe(tap({
|
|
703
728
|
next: (response) => {
|
|
704
|
-
|
|
705
|
-
if (resolved === null) {
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
this.emitPortEvent(run.runId, 'in', group.nodeId, group.portId, 0, resolved.state, resolved.value, group.edges.map((entry) => entry.edge.edgeId));
|
|
729
|
+
this.emitPortEvent(run.runId, 'in', group.nodeId, group.portId, 0, response, group.edges.map((entry) => entry.edge.edgeId));
|
|
709
730
|
},
|
|
710
731
|
})));
|
|
711
732
|
wiredSlots.push({
|
|
@@ -6,9 +6,10 @@ export { RuntimeEditor } from './runtime-editor.js';
|
|
|
6
6
|
export { graphHasCycle } from './runtime-helpers.js';
|
|
7
7
|
export type { GraphCluster } from './runtime-helpers.js';
|
|
8
8
|
export { RuntimeRunner } from './runtime-runner.js';
|
|
9
|
-
export type { MetaFromStatefulObservable, NodeId, EdgeId, RunId, PortMeta, RuntimeEdge, RuntimeEditorApi, RuntimeFeedPortMeta, RuntimeFeedRole, PortTelemetry, RuntimeDoneTelemetry, RuntimeNode,
|
|
10
|
-
export { isPortTelemetry, isRuntimeDone } from './types.js';
|
|
9
|
+
export type { MetaFromStatefulObservable, NodeId, EdgeId, RunId, PortMeta, RuntimeEdge, RuntimeEditorApi, RuntimeFeedPortMeta, RuntimeFeedRole, PortTelemetry, RuntimeDoneTelemetry, RuntimeNode, RuntimeResumeOptions, RuntimeRunnerApi, RuntimeRunnerEvent, RuntimeRunnerStatus, RuntimeSeedPortValue, RuntimeWireType, SwapNodeResult, } from './types.js';
|
|
10
|
+
export { isPortErrorTelemetry, isPortPendingTelemetry, isPortTelemetry, isPortValueTelemetry, isRuntimeDone, } from './types.js';
|
|
11
11
|
export type { RuntimeOptions };
|
|
12
|
+
export type { ResponseDto } from '@rx-evo/stateful-observable';
|
|
12
13
|
export declare class RuntimeFacade {
|
|
13
14
|
readonly editor: RuntimeEditor;
|
|
14
15
|
readonly runner: RuntimeRunner;
|
|
@@ -4,7 +4,7 @@ export { bypassOutputPortId, parseBypassOutputPortId } from './bypass-ports.js';
|
|
|
4
4
|
export { RuntimeEditor } from './runtime-editor.js';
|
|
5
5
|
export { graphHasCycle } from './runtime-helpers.js';
|
|
6
6
|
export { RuntimeRunner } from './runtime-runner.js';
|
|
7
|
-
export { isPortTelemetry, isRuntimeDone } from './types.js';
|
|
7
|
+
export { isPortErrorTelemetry, isPortPendingTelemetry, isPortTelemetry, isPortValueTelemetry, isRuntimeDone, } from './types.js';
|
|
8
8
|
export class RuntimeFacade {
|
|
9
9
|
editor;
|
|
10
10
|
runner;
|
|
@@ -20,3 +20,5 @@ export declare function runAndCollectEvents(runtime: RuntimeRunner | RuntimeHarn
|
|
|
20
20
|
export declare function noDoneWithin(runtime: RuntimeRunner | RuntimeHarness, ms: number, runId?: RunId): Promise<boolean>;
|
|
21
21
|
export declare const edgeIdsFromPortEvent: (event: PortTelemetry) => readonly EdgeId[];
|
|
22
22
|
export declare const portDirLabel: (event: PortTelemetry) => "input-received" | "output-emitted";
|
|
23
|
+
export declare const dtoHas: (dtos: readonly unknown[], key: "pending" | "value" | "error" | "inactive") => boolean;
|
|
24
|
+
export declare const dtoIndex: (dtos: readonly unknown[], key: "pending" | "value" | "error" | "inactive") => number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { filter, firstValueFrom } from 'rxjs';
|
|
2
|
-
import {
|
|
2
|
+
import { isPortValueTelemetry, isRuntimeDone, } from '../../types.js';
|
|
3
3
|
import { RuntimeEditor } from '../../runtime-editor.js';
|
|
4
4
|
import { RuntimeRunner } from '../../runtime-runner.js';
|
|
5
5
|
export function createRuntimeHarness(options) {
|
|
@@ -18,13 +18,13 @@ export function wireEdge(editor, edge) {
|
|
|
18
18
|
throw new Error(`Failed to wire ${edge.fromNodeId}.${edge.fromPort[0]} → ${edge.toNodeId}.${edge.toPort[0]}`);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
const isOutputValue = (event) =>
|
|
21
|
+
const isOutputValue = (event) => isPortValueTelemetry(event) && event[0] === 'out';
|
|
22
22
|
export function outputValues(events, nodeId, portId, _runId) {
|
|
23
23
|
return events
|
|
24
24
|
.filter((event) => isOutputValue(event) &&
|
|
25
25
|
event[1] === nodeId &&
|
|
26
26
|
event[2] === portId)
|
|
27
|
-
.map((event) => event[
|
|
27
|
+
.map((event) => event[3].value);
|
|
28
28
|
}
|
|
29
29
|
export async function waitForOutput(runtime, nodeId, portId, _runId) {
|
|
30
30
|
return waitForOutputMatching(runtime, nodeId, portId, () => true, _runId);
|
|
@@ -34,7 +34,7 @@ async function waitForOutputMatching(runtime, nodeId, portId, predicate, _runId)
|
|
|
34
34
|
return firstValueFrom(runner.events$.pipe(filter((event) => isOutputValue(event) &&
|
|
35
35
|
event[1] === nodeId &&
|
|
36
36
|
event[2] === portId &&
|
|
37
|
-
predicate(event[
|
|
37
|
+
predicate(event[3].value))));
|
|
38
38
|
}
|
|
39
39
|
export async function runAndCollectEvents(runtime, startRun, settleMs = 50) {
|
|
40
40
|
const events = [];
|
|
@@ -61,5 +61,7 @@ export async function noDoneWithin(runtime, ms, runId) {
|
|
|
61
61
|
]);
|
|
62
62
|
return result === 'timeout';
|
|
63
63
|
}
|
|
64
|
-
export const edgeIdsFromPortEvent = (event) => event[
|
|
64
|
+
export const edgeIdsFromPortEvent = (event) => event[5];
|
|
65
65
|
export const portDirLabel = (event) => event[0] === 'out' ? 'output-emitted' : 'input-received';
|
|
66
|
+
export const dtoHas = (dtos, key) => dtos.some((dto) => typeof dto === 'object' && dto !== null && key in dto);
|
|
67
|
+
export const dtoIndex = (dtos, key) => dtos.findIndex((dto) => typeof dto === 'object' && dto !== null && key in dto);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { StatefulConnection, StatefulObservable } from '@rx-evo/stateful-observable';
|
|
1
|
+
import type { ResponseDto, StatefulConnection, StatefulObservable } from '@rx-evo/stateful-observable';
|
|
2
2
|
import type { Observable } from 'rxjs';
|
|
3
3
|
import type { GraphCluster } from './runtime-helpers.js';
|
|
4
4
|
export type RuntimeWireType = string & {
|
|
@@ -83,7 +83,7 @@ export type MetaFromStatefulObservable<T extends StatefulObservable<unknown, unk
|
|
|
83
83
|
*
|
|
84
84
|
* ## Errors
|
|
85
85
|
* Node handlers surface failures via RxJS / `StatefulObservable` (`catchError`,
|
|
86
|
-
* port `error$`). Runner telemetry emits `
|
|
86
|
+
* port `error$`). Runner telemetry emits `ResponseDto` `{ error }`
|
|
87
87
|
* on the source; wiring then drops `ResponseError` so edges do not cascade
|
|
88
88
|
* error chrome to downstream nodes.
|
|
89
89
|
*
|
|
@@ -118,17 +118,16 @@ export type MetaFromStatefulObservable<T extends StatefulObservable<unknown, unk
|
|
|
118
118
|
* optional {@link RuntimeNode.stopsRun} completion.
|
|
119
119
|
*/
|
|
120
120
|
export type RuntimeRunnerStatus = 'idle' | 'running' | 'stopped';
|
|
121
|
-
export type RuntimePortSignalState = 'pending' | 'value' | 'error';
|
|
122
121
|
/**
|
|
123
122
|
* Port signal tuple — `portDir` `'out'` = output emitted, `'in'` = input received.
|
|
124
|
-
*
|
|
123
|
+
* Slot 3 is `@rx-evo` {@link ResponseDto} (JSON-safe pending / inactive / error /
|
|
124
|
+
* value). No per-frame `runId`; session snapshots own run correlation.
|
|
125
125
|
*/
|
|
126
126
|
export type PortTelemetry = readonly [
|
|
127
127
|
portDir: 'in' | 'out',
|
|
128
128
|
nodeId: NodeId,
|
|
129
129
|
portId: string,
|
|
130
|
-
|
|
131
|
-
value: unknown,
|
|
130
|
+
response: ResponseDto<unknown>,
|
|
132
131
|
portIdx: number,
|
|
133
132
|
edgeIds: readonly EdgeId[],
|
|
134
133
|
/** Absent feed meta — use `null` (never `undefined`; JSON arrays drop undefined). */
|
|
@@ -138,6 +137,21 @@ export type PortTelemetry = readonly [
|
|
|
138
137
|
export type RuntimeDoneTelemetry = readonly ['done'] | readonly ['done', RunId];
|
|
139
138
|
export type RuntimeRunnerEvent = PortTelemetry | RuntimeDoneTelemetry;
|
|
140
139
|
export declare const isPortTelemetry: (event: RuntimeRunnerEvent) => event is PortTelemetry;
|
|
140
|
+
export declare const isPortValueTelemetry: (event: RuntimeRunnerEvent) => event is PortTelemetry & {
|
|
141
|
+
readonly 3: Extract<ResponseDto<unknown>, {
|
|
142
|
+
value: unknown;
|
|
143
|
+
}>;
|
|
144
|
+
};
|
|
145
|
+
export declare const isPortErrorTelemetry: (event: RuntimeRunnerEvent) => event is PortTelemetry & {
|
|
146
|
+
readonly 3: Extract<ResponseDto<unknown>, {
|
|
147
|
+
error: unknown;
|
|
148
|
+
}>;
|
|
149
|
+
};
|
|
150
|
+
export declare const isPortPendingTelemetry: (event: RuntimeRunnerEvent) => event is PortTelemetry & {
|
|
151
|
+
readonly 3: {
|
|
152
|
+
readonly pending: true;
|
|
153
|
+
};
|
|
154
|
+
};
|
|
141
155
|
export declare const isRuntimeDone: (event: RuntimeRunnerEvent) => event is RuntimeDoneTelemetry;
|
|
142
156
|
/** Seed value pushed into an input port slot on {@link RuntimeRunner.start}. */
|
|
143
157
|
export type RuntimeSeedPortValue = {
|
|
@@ -309,7 +323,9 @@ export type RuntimeEditorApi = {
|
|
|
309
323
|
export type RuntimeRunnerApi = {
|
|
310
324
|
/**
|
|
311
325
|
* Wire every cluster that does not contain a {@link RuntimeNode.chatEntry}
|
|
312
|
-
* node
|
|
326
|
+
* node. Assigns `runId` and sets `'running'` **synchronously**, then wires
|
|
327
|
+
* ports on a microtask so callers (WS `runner.started`) can fence before
|
|
328
|
+
* any `runner.port` telemetry.
|
|
313
329
|
*
|
|
314
330
|
* Chat-entry clusters are omitted — start them via
|
|
315
331
|
* {@link RuntimeRunnerApi.pushIntoInput}. When every cluster is chat-entry
|
|
@@ -326,7 +342,8 @@ export type RuntimeRunnerApi = {
|
|
|
326
342
|
start(initialPayload?: Readonly<Record<string, ReadonlyArray<RuntimeSeedPortValue>>>, runId?: RunId): RunId | false;
|
|
327
343
|
/**
|
|
328
344
|
* Run-from-node: wire the weakly connected cluster containing `nodeId`
|
|
329
|
-
* (orphan nodes elsewhere on the canvas are not wired).
|
|
345
|
+
* (orphan nodes elsewhere on the canvas are not wired). Same runId /
|
|
346
|
+
* `'running'` sync + microtask wiring as {@link RuntimeRunnerApi.start}.
|
|
330
347
|
*
|
|
331
348
|
* Run stays `'running'` until {@link RuntimeRunnerApi.interrupt} or a
|
|
332
349
|
* {@link RuntimeNode.stopsRun} node in scope emits.
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export const isPortTelemetry = (event) => Array.isArray(event) && (event[0] === 'in' || event[0] === 'out');
|
|
2
|
+
export const isPortValueTelemetry = (event) => isPortTelemetry(event) && 'value' in event[3];
|
|
3
|
+
export const isPortErrorTelemetry = (event) => isPortTelemetry(event) && 'error' in event[3];
|
|
4
|
+
export const isPortPendingTelemetry = (event) => isPortTelemetry(event) && 'pending' in event[3];
|
|
2
5
|
export const isRuntimeDone = (event) => Array.isArray(event) && event[0] === 'done';
|
|
@@ -73,8 +73,9 @@ export const buildExecutionContext = async (context, runId, node, hooks, preload
|
|
|
73
73
|
webFetch,
|
|
74
74
|
};
|
|
75
75
|
const enabledToolIds = toolPermissionsToEnabledIds(toolPermissions);
|
|
76
|
-
const
|
|
77
|
-
const
|
|
76
|
+
const builtinHandles = wrapBuiltinToolHandles(harness, enabledToolIds, permission);
|
|
77
|
+
const mcpTools = filterMcpHandlesByIds(runMcpHandles ?? [], parseEnabledMcpIds(node.params.enabledMcpIds)).flatMap((handle) => handle.tools);
|
|
78
|
+
const toolHandles = [...builtinHandles, ...mcpTools];
|
|
78
79
|
const base = {
|
|
79
80
|
projectDir: context.projectDir,
|
|
80
81
|
runId,
|
|
@@ -85,7 +86,6 @@ export const buildExecutionContext = async (context, runId, node, hooks, preload
|
|
|
85
86
|
params: node.params,
|
|
86
87
|
})?.uiSchema ?? []),
|
|
87
88
|
...(toolHandles.length > 0 ? { toolHandles } : {}),
|
|
88
|
-
...(mcpHandles.length > 0 ? { mcpHandles } : {}),
|
|
89
89
|
};
|
|
90
90
|
const defaultChat = parseDefaultChatModel(config.model);
|
|
91
91
|
return attachRunHostServices(base, {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ToolHandle } from '@langflower/node-sdk';
|
|
2
2
|
import type { LangflowerSession } from '../session/langflower-session.js';
|
|
3
3
|
/**
|
|
4
|
-
* Activate current editor `tools`
|
|
5
|
-
* so a later inbound peek in
|
|
4
|
+
* Activate current editor `tools` / `subagent-registration` outputs
|
|
5
|
+
* (including post-swap instances) so a later inbound peek in
|
|
6
|
+
* {@link getLiveWiredTools} sees live handles.
|
|
6
7
|
*/
|
|
7
8
|
export declare const refreshLiveWiredToolPacks: (session: LangflowerSession) => void;
|
|
8
9
|
/**
|
|
@@ -40,14 +40,17 @@ const peekPortValue = (port) => {
|
|
|
40
40
|
return peeked;
|
|
41
41
|
};
|
|
42
42
|
/**
|
|
43
|
-
* Activate current editor `tools`
|
|
44
|
-
* so a later inbound peek in
|
|
43
|
+
* Activate current editor `tools` / `subagent-registration` outputs
|
|
44
|
+
* (including post-swap instances) so a later inbound peek in
|
|
45
|
+
* {@link getLiveWiredTools} sees live handles.
|
|
45
46
|
*/
|
|
46
47
|
export const refreshLiveWiredToolPacks = (session) => {
|
|
47
48
|
for (const node of session.runtime.editor.getNodes()) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
for (const portId of ['tools', 'subagent-registration']) {
|
|
50
|
+
const port = resolveToolsOutput(node.outputs, portId);
|
|
51
|
+
if (port !== undefined) {
|
|
52
|
+
peekPortValue(port);
|
|
53
|
+
}
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
};
|
|
@@ -3,6 +3,7 @@ import { buildWorkflowFingerprint, } from '@langflower/shared/langflower.js';
|
|
|
3
3
|
import { Subscription } from 'rxjs';
|
|
4
4
|
import { listResumableCheckpoints } from '../checkpoint/list-resumable-checkpoints.js';
|
|
5
5
|
import { resolveCheckpointBoundary } from '../checkpoint/resolve-checkpoint-boundary.js';
|
|
6
|
+
import { resetSessionExecutionFeed } from '../session/reset-session-execution-feed.js';
|
|
6
7
|
import { buildContextSeeds, applyObservableContextSeeds, } from './build-execution-context.js';
|
|
7
8
|
import { getLiveWiredTools } from './get-live-wired-tools.js';
|
|
8
9
|
import { createLangflowerToolsRpc } from './langflower-tools-rpc.js';
|
|
@@ -293,8 +294,7 @@ export const wireRunnerHandlers = (bridge, context, session, checkpoints) => {
|
|
|
293
294
|
if (session.runnerStatus === 'running') {
|
|
294
295
|
return;
|
|
295
296
|
}
|
|
296
|
-
session
|
|
297
|
-
session.runId = undefined;
|
|
297
|
+
resetSessionExecutionFeed(session);
|
|
298
298
|
// Re-broadcast the (now empty) feed to every tab.
|
|
299
299
|
bridgeEmit(bridge, 'executionFeed.snapshot', session.buildExecutionFeed());
|
|
300
300
|
}));
|