@bian-womp/spark-graph 0.3.0 → 0.3.2
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/lib/cjs/index.cjs +74 -145
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/index.d.ts +1 -1
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts +6 -14
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/cjs/src/runtime/{UnifiedEngine.d.ts → LocalEngine.d.ts} +18 -5
- package/lib/cjs/src/runtime/LocalEngine.d.ts.map +1 -0
- package/lib/cjs/src/runtime/components/ExecutionScheduler.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/ValuePropagator.d.ts.map +1 -1
- package/lib/esm/index.js +74 -145
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/index.d.ts +1 -1
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts +6 -14
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/src/runtime/{UnifiedEngine.d.ts → LocalEngine.d.ts} +18 -5
- package/lib/esm/src/runtime/LocalEngine.d.ts.map +1 -0
- package/lib/esm/src/runtime/components/ExecutionScheduler.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/ValuePropagator.d.ts.map +1 -1
- package/package.json +2 -2
- package/lib/cjs/src/runtime/AbstractEngine.d.ts +0 -28
- package/lib/cjs/src/runtime/AbstractEngine.d.ts.map +0 -1
- package/lib/cjs/src/runtime/UnifiedEngine.d.ts.map +0 -1
- package/lib/esm/src/runtime/AbstractEngine.d.ts +0 -28
- package/lib/esm/src/runtime/AbstractEngine.d.ts.map +0 -1
- package/lib/esm/src/runtime/UnifiedEngine.d.ts.map +0 -1
package/lib/cjs/index.cjs
CHANGED
|
@@ -1070,10 +1070,13 @@ class ValuePropagator {
|
|
|
1070
1070
|
}
|
|
1071
1071
|
}
|
|
1072
1072
|
}
|
|
1073
|
-
// Schedule downstream execution if propagation is enabled
|
|
1074
|
-
|
|
1073
|
+
// Schedule downstream execution if propagation is enabled.
|
|
1074
|
+
// In manual mode we still allow scheduling for run-context-aware runs.
|
|
1075
|
+
const paused = this.runtimeCoordinator.isPaused();
|
|
1076
|
+
const canSchedule = (!paused || isRunContextAware) &&
|
|
1075
1077
|
shouldPropagate &&
|
|
1076
|
-
this.executionScheduler.allInboundHaveValue(e.target.nodeId)
|
|
1078
|
+
this.executionScheduler.allInboundHaveValue(e.target.nodeId);
|
|
1079
|
+
if (canSchedule) {
|
|
1077
1080
|
if (isRunContextAware && effectiveRunContexts) {
|
|
1078
1081
|
this.executionScheduler.scheduleInputsChangedWithRunContexts(e.target.nodeId, effectiveRunContexts);
|
|
1079
1082
|
}
|
|
@@ -1357,7 +1360,8 @@ class ExecutionScheduler {
|
|
|
1357
1360
|
const node = this.graphStructure.getNode(nodeId);
|
|
1358
1361
|
if (!node)
|
|
1359
1362
|
return;
|
|
1360
|
-
|
|
1363
|
+
// Block only auto-mode scheduling while paused; allow run-context runs
|
|
1364
|
+
if (!runContextIds && this.runtimeCoordinator.isPaused())
|
|
1361
1365
|
return;
|
|
1362
1366
|
// If run-context IDs are provided, attach them to the node
|
|
1363
1367
|
if (runContextIds) {
|
|
@@ -1654,6 +1658,8 @@ class GraphRuntime {
|
|
|
1654
1658
|
// State
|
|
1655
1659
|
this.paused = false;
|
|
1656
1660
|
this.environment = {};
|
|
1661
|
+
this.runMode = null;
|
|
1662
|
+
this.pauseRefCount = 0;
|
|
1657
1663
|
// Initialize components
|
|
1658
1664
|
this.graphStructure = new GraphStructure();
|
|
1659
1665
|
this.eventEmitter = new EventEmitter();
|
|
@@ -1800,13 +1806,16 @@ class GraphRuntime {
|
|
|
1800
1806
|
anyChanged = true;
|
|
1801
1807
|
}
|
|
1802
1808
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1809
|
+
// In auto mode, input updates can trigger execution; in manual mode they never should.
|
|
1810
|
+
const isAutoMode = this.runMode === "auto" || this.runMode === null;
|
|
1811
|
+
const canAutoSchedule = !this.paused && isAutoMode;
|
|
1812
|
+
if (canAutoSchedule) {
|
|
1813
|
+
if (anyChanged && this.executionScheduler.allInboundHaveValue(nodeId)) {
|
|
1806
1814
|
this.executionScheduler.scheduleInputsChangedInternal(nodeId);
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
if (anyChanged) {
|
|
1818
|
+
this.handleResolver.scheduleRecomputeHandles(nodeId);
|
|
1810
1819
|
}
|
|
1811
1820
|
}
|
|
1812
1821
|
getOutput(nodeId, output) {
|
|
@@ -1820,14 +1829,12 @@ class GraphRuntime {
|
|
|
1820
1829
|
return;
|
|
1821
1830
|
const oldResolved = this.graphStructure.getResolvedHandles(nodeId);
|
|
1822
1831
|
this.graphStructure.setResolvedHandles(nodeId, handles);
|
|
1823
|
-
// Clear outputs that are no longer valid handles
|
|
1824
1832
|
const oldOutputs = oldResolved?.outputs ?? {};
|
|
1825
1833
|
const newOutputs = handles.outputs ?? {};
|
|
1826
1834
|
const oldOutputHandles = new Set(Object.keys(oldOutputs));
|
|
1827
1835
|
const newOutputHandles = new Set(Object.keys(newOutputs));
|
|
1828
1836
|
for (const handle of oldOutputHandles) {
|
|
1829
1837
|
if (!newOutputHandles.has(handle)) {
|
|
1830
|
-
// Output handle was removed - clear it and emit undefined to invalidate downstream
|
|
1831
1838
|
delete node.outputs[handle];
|
|
1832
1839
|
this.eventEmitter.emit("value", {
|
|
1833
1840
|
nodeId,
|
|
@@ -1838,19 +1845,17 @@ class GraphRuntime {
|
|
|
1838
1845
|
}
|
|
1839
1846
|
}
|
|
1840
1847
|
const edges = this.graphStructure.getEdges();
|
|
1841
|
-
// Recompute edge converter/type for edges where this node is source or target
|
|
1842
1848
|
for (const e of edges) {
|
|
1843
1849
|
const srcNode = this.graphStructure.getNode(e.source.nodeId);
|
|
1844
1850
|
const dstNode = this.graphStructure.getNode(e.target.nodeId);
|
|
1845
|
-
let srcDeclared = e.effectiveTypeId;
|
|
1851
|
+
let srcDeclared = e.effectiveTypeId;
|
|
1846
1852
|
let dstDeclared = e.dstDeclared;
|
|
1847
|
-
const oldDstDeclared = dstDeclared;
|
|
1853
|
+
const oldDstDeclared = dstDeclared;
|
|
1848
1854
|
if (e.source.nodeId === nodeId) {
|
|
1849
1855
|
const resolved = this.graphStructure.getResolvedHandles(nodeId);
|
|
1850
1856
|
srcDeclared = resolved
|
|
1851
1857
|
? resolved.outputs[e.source.handle]
|
|
1852
1858
|
: srcDeclared;
|
|
1853
|
-
// Update effectiveTypeId if original wasn't explicit
|
|
1854
1859
|
if (!e.typeId) {
|
|
1855
1860
|
e.effectiveTypeId = Array.isArray(srcDeclared)
|
|
1856
1861
|
? srcDeclared?.[0] ?? "untyped"
|
|
@@ -1867,7 +1872,6 @@ class GraphRuntime {
|
|
|
1867
1872
|
const conv = GraphStructure.buildEdgeConverters(srcDeclared, dstDeclared, registry, `updateNodeHandles: ${srcNode?.typeId || ""}.${e.source.nodeId}.${e.source.handle} -> ${dstNode?.typeId || ""}.${e.target.nodeId}.${e.target.handle}`);
|
|
1868
1873
|
e.convert = conv.convert;
|
|
1869
1874
|
e.convertAsync = conv.convertAsync;
|
|
1870
|
-
// If target handle was just resolved (was undefined, now has a type), re-propagate values
|
|
1871
1875
|
if (e.target.nodeId === nodeId &&
|
|
1872
1876
|
oldDstDeclared === undefined &&
|
|
1873
1877
|
dstDeclared !== undefined) {
|
|
@@ -1875,8 +1879,6 @@ class GraphRuntime {
|
|
|
1875
1879
|
if (srcNode) {
|
|
1876
1880
|
const srcValue = srcNode.outputs[e.source.handle];
|
|
1877
1881
|
if (srcValue !== undefined) {
|
|
1878
|
-
// Re-propagate through the now-resolved edge converter
|
|
1879
|
-
// Preserve run-contexts if source node has them
|
|
1880
1882
|
const runContextIds = srcNode.activeRunContexts.size > 0
|
|
1881
1883
|
? new Set(srcNode.activeRunContexts)
|
|
1882
1884
|
: undefined;
|
|
@@ -1885,11 +1887,9 @@ class GraphRuntime {
|
|
|
1885
1887
|
}
|
|
1886
1888
|
}
|
|
1887
1889
|
}
|
|
1888
|
-
// Re-emit only valid outputs (after clearing removed ones)
|
|
1889
1890
|
this.valuePropagator.reemitNodeOutputs(nodeId);
|
|
1890
1891
|
}
|
|
1891
1892
|
launch(invalidate = false) {
|
|
1892
|
-
// call onActivated for nodes that implement it
|
|
1893
1893
|
for (const node of this.graphStructure.getNodes().values()) {
|
|
1894
1894
|
const effectiveInputs = this.executionScheduler.getEffectiveInputs(node.nodeId);
|
|
1895
1895
|
const ctrl = new AbortController();
|
|
@@ -1902,7 +1902,6 @@ class GraphRuntime {
|
|
|
1902
1902
|
node.runtime.onActivated?.();
|
|
1903
1903
|
}
|
|
1904
1904
|
if (invalidate) {
|
|
1905
|
-
// After activation, schedule nodes that have all inbound inputs present
|
|
1906
1905
|
for (const nodeId of this.graphStructure.getNodes().keys()) {
|
|
1907
1906
|
if (this.executionScheduler.allInboundHaveValue(nodeId))
|
|
1908
1907
|
this.executionScheduler.scheduleInputsChangedInternal(nodeId);
|
|
@@ -1913,11 +1912,9 @@ class GraphRuntime {
|
|
|
1913
1912
|
const node = this.graphStructure.getNode(nodeId);
|
|
1914
1913
|
if (!node)
|
|
1915
1914
|
return;
|
|
1916
|
-
// Forward event to node's onExternalEvent handler for custom actions
|
|
1917
1915
|
node.runtime.onExternalEvent?.(event, node.state);
|
|
1918
1916
|
}
|
|
1919
1917
|
dispose() {
|
|
1920
|
-
// Resolve all pending run-context promises before cleanup
|
|
1921
1918
|
this.runContextManager.resolveAll();
|
|
1922
1919
|
for (const node of this.graphStructure.getNodes().values()) {
|
|
1923
1920
|
node.runtime.onDeactivated?.();
|
|
@@ -1932,7 +1929,6 @@ class GraphRuntime {
|
|
|
1932
1929
|
getNodeIds() {
|
|
1933
1930
|
return Array.from(this.graphStructure.getNodes().keys());
|
|
1934
1931
|
}
|
|
1935
|
-
// Unsafe helpers for serializer: read-only accessors and hydration
|
|
1936
1932
|
getNodeData(nodeId) {
|
|
1937
1933
|
const node = this.graphStructure.getNode(nodeId);
|
|
1938
1934
|
if (!node)
|
|
@@ -1952,12 +1948,10 @@ class GraphRuntime {
|
|
|
1952
1948
|
this.environment = { ...env };
|
|
1953
1949
|
this.handleResolver.setEnvironment(this.environment);
|
|
1954
1950
|
this.executionScheduler.setEnvironment(this.environment);
|
|
1955
|
-
// Recompute dynamic handles for all nodes when environment changes
|
|
1956
1951
|
for (const nodeId of this.graphStructure.getNodes().keys()) {
|
|
1957
1952
|
this.handleResolver.scheduleRecomputeHandles(nodeId);
|
|
1958
1953
|
}
|
|
1959
1954
|
}
|
|
1960
|
-
// Export a GraphDefinition reflecting the current runtime view
|
|
1961
1955
|
getGraphDef() {
|
|
1962
1956
|
const nodes = Array.from(this.graphStructure.getNodes().values()).map((n) => {
|
|
1963
1957
|
const resolved = this.graphStructure.getResolvedHandles(n.nodeId);
|
|
@@ -1974,12 +1968,11 @@ class GraphRuntime {
|
|
|
1974
1968
|
id: e.id,
|
|
1975
1969
|
source: { nodeId: e.source.nodeId, handle: e.source.handle },
|
|
1976
1970
|
target: { nodeId: e.target.nodeId, handle: e.target.handle },
|
|
1977
|
-
typeId: e.typeId,
|
|
1971
|
+
typeId: e.typeId,
|
|
1978
1972
|
}));
|
|
1979
1973
|
return { nodes, edges };
|
|
1980
1974
|
}
|
|
1981
1975
|
async whenIdle() {
|
|
1982
|
-
// If we have active run-contexts, wait for all of them to complete
|
|
1983
1976
|
const allRunContexts = this.runContextManager.getAllRunContexts();
|
|
1984
1977
|
if (allRunContexts.size > 0) {
|
|
1985
1978
|
await new Promise((resolve) => {
|
|
@@ -2015,65 +2008,58 @@ class GraphRuntime {
|
|
|
2015
2008
|
setTimeout(check, 10);
|
|
2016
2009
|
});
|
|
2017
2010
|
}
|
|
2018
|
-
/**
|
|
2019
|
-
* Run this node and optionally all dynamically reachable downstream nodes as a run-context.
|
|
2020
|
-
* Includes nodes added later behind the same path (via re-emits).
|
|
2021
|
-
* @param startNodeId - The node to start execution from
|
|
2022
|
-
* @param options - Execution options
|
|
2023
|
-
* @param options.skipPropagateValues - If true, don't set inputs of linked nodes (default: false)
|
|
2024
|
-
* @param options.propagate - If false, don't schedule downstream nodes (default: true)
|
|
2025
|
-
*/
|
|
2026
2011
|
async runFromHereContext(startNodeId, options) {
|
|
2027
2012
|
const node = this.graphStructure.getNode(startNodeId);
|
|
2028
|
-
if (!node)
|
|
2029
|
-
// Node doesn't exist - resolve immediately
|
|
2013
|
+
if (!node)
|
|
2030
2014
|
return;
|
|
2031
|
-
}
|
|
2032
2015
|
const ctx = this.runContextManager.createRunContext(startNodeId, options);
|
|
2033
|
-
// Create promise that resolves when context finishes
|
|
2034
2016
|
const promise = new Promise((resolve) => {
|
|
2035
2017
|
ctx.resolve = resolve;
|
|
2036
2018
|
});
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
this.paused = false;
|
|
2041
|
-
}
|
|
2042
|
-
try {
|
|
2043
|
-
// Seed the start node with this run-context
|
|
2044
|
-
node.activeRunContexts.add(ctx.id);
|
|
2045
|
-
this.scheduleInputsChangedWithRunContexts(startNodeId, new Set([ctx.id]));
|
|
2046
|
-
await promise;
|
|
2047
|
-
}
|
|
2048
|
-
finally {
|
|
2049
|
-
// Restore pause state if it was paused and no other run-contexts are active
|
|
2050
|
-
if (wasPaused && this.runContextManager.getAllRunContexts().size === 0) {
|
|
2051
|
-
this.paused = true;
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2019
|
+
node.activeRunContexts.add(ctx.id);
|
|
2020
|
+
this.scheduleInputsChangedWithRunContexts(startNodeId, new Set([ctx.id]));
|
|
2021
|
+
await promise;
|
|
2054
2022
|
}
|
|
2055
|
-
/**
|
|
2056
|
-
* Schedule a node with run-context IDs attached
|
|
2057
|
-
*/
|
|
2058
2023
|
scheduleInputsChangedWithRunContexts(nodeId, runContextIds) {
|
|
2059
2024
|
const node = this.graphStructure.getNode(nodeId);
|
|
2060
2025
|
if (!node)
|
|
2061
2026
|
return;
|
|
2062
|
-
// Attach run-contexts to the node
|
|
2063
2027
|
for (const id of runContextIds) {
|
|
2064
2028
|
node.activeRunContexts.add(id);
|
|
2065
2029
|
}
|
|
2066
2030
|
this.executionScheduler.scheduleInputsChangedInternal(nodeId, runContextIds);
|
|
2067
2031
|
}
|
|
2068
|
-
|
|
2069
|
-
this.
|
|
2032
|
+
setRunMode(runMode) {
|
|
2033
|
+
this.runMode = runMode;
|
|
2034
|
+
this.updatePausedState();
|
|
2035
|
+
}
|
|
2036
|
+
requestPause() {
|
|
2037
|
+
this.pauseRefCount++;
|
|
2038
|
+
this.updatePausedState();
|
|
2039
|
+
let released = false;
|
|
2040
|
+
return () => {
|
|
2041
|
+
if (released)
|
|
2042
|
+
return;
|
|
2043
|
+
released = true;
|
|
2044
|
+
this.pauseRefCount--;
|
|
2045
|
+
this.updatePausedState();
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
2048
|
+
updatePausedState() {
|
|
2049
|
+
if (this.pauseRefCount > 0) {
|
|
2050
|
+
this.paused = true;
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
if (this.runMode === "manual") {
|
|
2054
|
+
this.paused = true;
|
|
2055
|
+
}
|
|
2056
|
+
else if (this.runMode === "auto") {
|
|
2057
|
+
this.paused = false;
|
|
2058
|
+
}
|
|
2070
2059
|
}
|
|
2071
2060
|
isPaused() {
|
|
2072
2061
|
return this.paused;
|
|
2073
2062
|
}
|
|
2074
|
-
resume() {
|
|
2075
|
-
this.paused = false;
|
|
2076
|
-
}
|
|
2077
2063
|
invalidateDownstream(nodeId) {
|
|
2078
2064
|
this.valuePropagator.reemitNodeOutputs(nodeId);
|
|
2079
2065
|
}
|
|
@@ -2084,19 +2070,13 @@ class GraphRuntime {
|
|
|
2084
2070
|
this.executionScheduler.cancelNodeRuns(nodeIds);
|
|
2085
2071
|
}
|
|
2086
2072
|
copyOutputs(fromNodeId, toNodeId, options) {
|
|
2087
|
-
// Get outputs from source node
|
|
2088
2073
|
const fromNode = this.getNodeData(fromNodeId);
|
|
2089
2074
|
if (!fromNode?.outputs)
|
|
2090
2075
|
return;
|
|
2091
|
-
// Copy outputs to target node using hydrate
|
|
2092
|
-
// hydrate already pauses internally, so we don't need to handle dry option here
|
|
2093
|
-
// reemit: !options?.dry means don't propagate downstream if dry mode
|
|
2094
2076
|
this.hydrate({ outputs: { [toNodeId]: { ...fromNode.outputs } } }, { reemit: !options?.dry });
|
|
2095
2077
|
}
|
|
2096
|
-
// Hydrate inputs/outputs without triggering computation; optionally re-emit outputs downstream
|
|
2097
2078
|
hydrate(payload, opts) {
|
|
2098
|
-
const
|
|
2099
|
-
this.paused = true;
|
|
2079
|
+
const releasePause = this.requestPause();
|
|
2100
2080
|
try {
|
|
2101
2081
|
const ins = payload?.inputs || {};
|
|
2102
2082
|
for (const [nodeId, map] of Object.entries(ins)) {
|
|
@@ -2105,7 +2085,6 @@ class GraphRuntime {
|
|
|
2105
2085
|
continue;
|
|
2106
2086
|
for (const [h, v] of Object.entries(map || {})) {
|
|
2107
2087
|
node.inputs[h] = structuredClone(v);
|
|
2108
|
-
// emit input value event
|
|
2109
2088
|
this.eventEmitter.emit("value", {
|
|
2110
2089
|
nodeId,
|
|
2111
2090
|
handle: h,
|
|
@@ -2122,7 +2101,6 @@ class GraphRuntime {
|
|
|
2122
2101
|
continue;
|
|
2123
2102
|
for (const [h, v] of Object.entries(map || {})) {
|
|
2124
2103
|
node.outputs[h] = structuredClone(v);
|
|
2125
|
-
// emit output value event
|
|
2126
2104
|
this.eventEmitter.emit("value", {
|
|
2127
2105
|
nodeId,
|
|
2128
2106
|
handle: h,
|
|
@@ -2139,31 +2117,23 @@ class GraphRuntime {
|
|
|
2139
2117
|
}
|
|
2140
2118
|
}
|
|
2141
2119
|
finally {
|
|
2142
|
-
|
|
2120
|
+
releasePause();
|
|
2143
2121
|
}
|
|
2144
2122
|
}
|
|
2145
|
-
// Incrementally update nodes/edges to match new definition without full rebuild
|
|
2146
2123
|
update(def, registry) {
|
|
2147
|
-
// Handle node additions and removals
|
|
2148
2124
|
const desiredIds = new Set(def.nodes.map((n) => n.nodeId));
|
|
2149
2125
|
const currentIds = new Set(this.graphStructure.getNodes().keys());
|
|
2150
|
-
// Remove nodes not present
|
|
2151
2126
|
for (const nodeId of Array.from(currentIds)) {
|
|
2152
2127
|
if (!desiredIds.has(nodeId)) {
|
|
2153
2128
|
const node = this.graphStructure.getNode(nodeId);
|
|
2154
|
-
// Cancel all active runs and emit cancellation events
|
|
2155
2129
|
this.executionScheduler.cancelNodeActiveRuns(node, "node-deleted");
|
|
2156
|
-
|
|
2157
|
-
this.runContextManager.cancelNodeInRunContexts(nodeId,
|
|
2158
|
-
/* includeDownstream */ true, this.graphStructure.getEdges(), this.graphStructure.getNodes());
|
|
2159
|
-
// Check for run-context completion (they may finish if pending reaches 0)
|
|
2130
|
+
this.runContextManager.cancelNodeInRunContexts(nodeId, true, this.graphStructure.getEdges(), this.graphStructure.getNodes());
|
|
2160
2131
|
const allRunContexts = this.runContextManager.getAllRunContexts();
|
|
2161
2132
|
for (const ctx of Array.from(allRunContexts.values())) {
|
|
2162
2133
|
if (ctx.pending === 0) {
|
|
2163
2134
|
this.runContextManager.finishRunContext(ctx.id, this.graphStructure.getNodes());
|
|
2164
2135
|
}
|
|
2165
2136
|
}
|
|
2166
|
-
// Cleanup node resources
|
|
2167
2137
|
node.runtime.onDeactivated?.();
|
|
2168
2138
|
node.runtime.dispose?.();
|
|
2169
2139
|
node.lifecycle?.dispose?.({
|
|
@@ -2178,7 +2148,6 @@ class GraphRuntime {
|
|
|
2178
2148
|
for (const n of def.nodes) {
|
|
2179
2149
|
const existing = this.graphStructure.getNode(n.nodeId);
|
|
2180
2150
|
if (!existing) {
|
|
2181
|
-
// create new runtime node
|
|
2182
2151
|
const desc = registry.nodes.get(n.typeId);
|
|
2183
2152
|
if (!desc)
|
|
2184
2153
|
throw new Error(`Unknown node type: ${n.typeId}`);
|
|
@@ -2218,7 +2187,6 @@ class GraphRuntime {
|
|
|
2218
2187
|
activeRunContexts: new Set(),
|
|
2219
2188
|
};
|
|
2220
2189
|
this.graphStructure.setNode(n.nodeId, rn);
|
|
2221
|
-
// Activate new node
|
|
2222
2190
|
const effectiveInputs = this.executionScheduler.getEffectiveInputs(rn.nodeId);
|
|
2223
2191
|
const ctrl = new AbortController();
|
|
2224
2192
|
const ctx = this.executionScheduler.createExecutionContext(rn.nodeId, rn, effectiveInputs, `${rn.nodeId}:init`, ctrl.signal);
|
|
@@ -2230,7 +2198,6 @@ class GraphRuntime {
|
|
|
2230
2198
|
rn.runtime.onActivated?.();
|
|
2231
2199
|
}
|
|
2232
2200
|
else {
|
|
2233
|
-
// update params/policy
|
|
2234
2201
|
existing.params = n.params;
|
|
2235
2202
|
if (!existing.stats) {
|
|
2236
2203
|
existing.stats = {
|
|
@@ -2242,7 +2209,6 @@ class GraphRuntime {
|
|
|
2242
2209
|
}
|
|
2243
2210
|
}
|
|
2244
2211
|
}
|
|
2245
|
-
// Capture previous inbound map before rebuilding edges
|
|
2246
2212
|
const edges = this.graphStructure.getEdges();
|
|
2247
2213
|
const prevInbound = new Map();
|
|
2248
2214
|
for (const e of edges) {
|
|
@@ -2250,7 +2216,6 @@ class GraphRuntime {
|
|
|
2250
2216
|
set.add(e.target.handle);
|
|
2251
2217
|
prevInbound.set(e.target.nodeId, set);
|
|
2252
2218
|
}
|
|
2253
|
-
// Capture previous per-handle target sets before rebuilding edges
|
|
2254
2219
|
const prevOutTargets = new Map();
|
|
2255
2220
|
for (const e of edges) {
|
|
2256
2221
|
const tmap = prevOutTargets.get(e.source.nodeId) ?? new Map();
|
|
@@ -2259,9 +2224,7 @@ class GraphRuntime {
|
|
|
2259
2224
|
tmap.set(e.source.handle, tset);
|
|
2260
2225
|
prevOutTargets.set(e.source.nodeId, tmap);
|
|
2261
2226
|
}
|
|
2262
|
-
// Precompute per-node resolved handles for updated graph (include dynamic)
|
|
2263
2227
|
const resolved = GraphStructure.computeResolvedHandleMap(def, registry, this.environment);
|
|
2264
|
-
// Check which handles changed and emit events for those
|
|
2265
2228
|
const changedHandles = {};
|
|
2266
2229
|
for (const [nodeId, newHandles] of resolved.map) {
|
|
2267
2230
|
const oldHandles = this.graphStructure.getResolvedHandles(nodeId);
|
|
@@ -2270,14 +2233,11 @@ class GraphRuntime {
|
|
|
2270
2233
|
changedHandles[nodeId] = newHandles;
|
|
2271
2234
|
}
|
|
2272
2235
|
}
|
|
2273
|
-
// Update resolved handles
|
|
2274
2236
|
for (const [nodeId, handles] of resolved.map) {
|
|
2275
2237
|
this.graphStructure.setResolvedHandles(nodeId, handles);
|
|
2276
2238
|
}
|
|
2277
|
-
// Rebuild edges mapping with coercions
|
|
2278
2239
|
const newEdges = GraphStructure.buildEdges(def, registry, this.graphStructure.getResolvedHandlesMap());
|
|
2279
2240
|
this.graphStructure.setEdges(newEdges);
|
|
2280
|
-
// Build new inbound map
|
|
2281
2241
|
const nextInbound = new Map();
|
|
2282
2242
|
const updatedEdges = this.graphStructure.getEdges();
|
|
2283
2243
|
for (const e of updatedEdges) {
|
|
@@ -2285,7 +2245,6 @@ class GraphRuntime {
|
|
|
2285
2245
|
set.add(e.target.handle);
|
|
2286
2246
|
nextInbound.set(e.target.nodeId, set);
|
|
2287
2247
|
}
|
|
2288
|
-
// For inputs that lost inbound connections, clear and schedule recompute
|
|
2289
2248
|
for (const [nodeId, prevSet] of prevInbound) {
|
|
2290
2249
|
const currSet = nextInbound.get(nodeId) ?? new Set();
|
|
2291
2250
|
const node = this.graphStructure.getNode(nodeId);
|
|
@@ -2301,12 +2260,10 @@ class GraphRuntime {
|
|
|
2301
2260
|
}
|
|
2302
2261
|
}
|
|
2303
2262
|
if (changed) {
|
|
2304
|
-
// Clear buckets for handles that lost inbound (handled by ValuePropagator)
|
|
2305
2263
|
this.valuePropagator.clearArrayBuckets(nodeId);
|
|
2306
2264
|
this.executionScheduler.scheduleInputsChangedInternal(nodeId);
|
|
2307
2265
|
}
|
|
2308
2266
|
}
|
|
2309
|
-
// Re-emit outputs when per-handle target sets change (precise and simple)
|
|
2310
2267
|
const nextOutTargets = new Map();
|
|
2311
2268
|
for (const e of updatedEdges) {
|
|
2312
2269
|
const tmap = nextOutTargets.get(e.source.nodeId) ?? new Map();
|
|
@@ -2315,7 +2272,7 @@ class GraphRuntime {
|
|
|
2315
2272
|
tmap.set(e.source.handle, tset);
|
|
2316
2273
|
nextOutTargets.set(e.source.nodeId, tmap);
|
|
2317
2274
|
}
|
|
2318
|
-
const
|
|
2275
|
+
const setsEqual = (a, b) => {
|
|
2319
2276
|
if (!a && !b)
|
|
2320
2277
|
return true;
|
|
2321
2278
|
if (!a || !b)
|
|
@@ -2341,7 +2298,7 @@ class GraphRuntime {
|
|
|
2341
2298
|
for (const handle of handles) {
|
|
2342
2299
|
const pset = pmap.get(handle) ?? new Set();
|
|
2343
2300
|
const nset = nmap.get(handle) ?? new Set();
|
|
2344
|
-
if (!
|
|
2301
|
+
if (!setsEqual(pset, nset)) {
|
|
2345
2302
|
const val = this.getOutput(nodeId, handle);
|
|
2346
2303
|
if (val !== undefined)
|
|
2347
2304
|
this.valuePropagator.propagate(nodeId, handle, val);
|
|
@@ -2350,11 +2307,6 @@ class GraphRuntime {
|
|
|
2350
2307
|
}
|
|
2351
2308
|
}
|
|
2352
2309
|
}
|
|
2353
|
-
// Prune array bucket contributions for edges that no longer exist
|
|
2354
|
-
// This is handled by ValuePropagator - array buckets are managed there
|
|
2355
|
-
// The buckets will be cleaned up automatically when edges are removed
|
|
2356
|
-
// Schedule async recompute for nodes that indicated Promise-based resolveHandles in this update
|
|
2357
|
-
// Emit event for changed handles (if any)
|
|
2358
2310
|
if (Object.keys(changedHandles).length > 0) {
|
|
2359
2311
|
this.eventEmitter.emit("invalidate", {
|
|
2360
2312
|
reason: "graph-updated",
|
|
@@ -2585,21 +2537,25 @@ class GraphBuilder {
|
|
|
2585
2537
|
}
|
|
2586
2538
|
}
|
|
2587
2539
|
|
|
2588
|
-
|
|
2589
|
-
|
|
2540
|
+
/**
|
|
2541
|
+
* Unified Engine implementation that handles both manual and auto run modes.
|
|
2542
|
+
* - Manual mode: Runtime is paused, nodes execute only when explicitly called via computeNode/runFromHere
|
|
2543
|
+
* - Auto mode: Runtime is resumed, nodes automatically execute when inputs change
|
|
2544
|
+
*/
|
|
2545
|
+
class LocalEngine {
|
|
2546
|
+
constructor(graphRuntime, runMode) {
|
|
2590
2547
|
this.graphRuntime = graphRuntime;
|
|
2548
|
+
this.setRunMode(runMode ?? "manual");
|
|
2591
2549
|
}
|
|
2592
2550
|
setInputs(nodeId, inputs, options) {
|
|
2593
2551
|
if (options?.dry) {
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
this.graphRuntime.pause();
|
|
2552
|
+
// Use requestPause to temporarily pause without affecting base run mode
|
|
2553
|
+
const releasePause = this.graphRuntime.requestPause();
|
|
2597
2554
|
try {
|
|
2598
2555
|
this.graphRuntime.setInputs(nodeId, inputs);
|
|
2599
2556
|
}
|
|
2600
2557
|
finally {
|
|
2601
|
-
|
|
2602
|
-
this.graphRuntime.resume();
|
|
2558
|
+
releasePause();
|
|
2603
2559
|
}
|
|
2604
2560
|
}
|
|
2605
2561
|
else {
|
|
@@ -2608,15 +2564,13 @@ class AbstractEngine {
|
|
|
2608
2564
|
}
|
|
2609
2565
|
triggerExternal(nodeId, event, options) {
|
|
2610
2566
|
if (options?.dry) {
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
this.graphRuntime.pause();
|
|
2567
|
+
// Use requestPause to temporarily pause without affecting base run mode
|
|
2568
|
+
const releasePause = this.graphRuntime.requestPause();
|
|
2614
2569
|
try {
|
|
2615
2570
|
this.graphRuntime.triggerExternal(nodeId, event);
|
|
2616
2571
|
}
|
|
2617
2572
|
finally {
|
|
2618
|
-
|
|
2619
|
-
this.graphRuntime.resume();
|
|
2573
|
+
releasePause();
|
|
2620
2574
|
}
|
|
2621
2575
|
}
|
|
2622
2576
|
else {
|
|
@@ -2641,19 +2595,6 @@ class AbstractEngine {
|
|
|
2641
2595
|
dispose() {
|
|
2642
2596
|
// this.graphRuntime.dispose();
|
|
2643
2597
|
}
|
|
2644
|
-
}
|
|
2645
|
-
|
|
2646
|
-
/**
|
|
2647
|
-
* Unified Engine implementation that handles both manual and auto run modes.
|
|
2648
|
-
* - Manual mode: Runtime is paused, nodes execute only when explicitly called via computeNode/runFromHere
|
|
2649
|
-
* - Auto mode: Runtime is resumed, nodes automatically execute when inputs change
|
|
2650
|
-
*/
|
|
2651
|
-
class UnifiedEngine extends AbstractEngine {
|
|
2652
|
-
constructor(graphRuntime, runMode) {
|
|
2653
|
-
super(graphRuntime);
|
|
2654
|
-
this.runMode = "manual";
|
|
2655
|
-
this.setRunMode(runMode ?? "manual");
|
|
2656
|
-
}
|
|
2657
2598
|
launch(invalidate, runMode) {
|
|
2658
2599
|
if (runMode)
|
|
2659
2600
|
this.setRunMode(runMode);
|
|
@@ -2680,20 +2621,8 @@ class UnifiedEngine extends AbstractEngine {
|
|
|
2680
2621
|
async runFromHere(nodeId) {
|
|
2681
2622
|
await this.graphRuntime.runFromHereContext(nodeId);
|
|
2682
2623
|
}
|
|
2683
|
-
getRunMode() {
|
|
2684
|
-
return this.runMode;
|
|
2685
|
-
}
|
|
2686
2624
|
setRunMode(runMode) {
|
|
2687
|
-
|
|
2688
|
-
return;
|
|
2689
|
-
this.runMode = runMode;
|
|
2690
|
-
// Update runtime pause/resume state based on new mode
|
|
2691
|
-
if (runMode === "manual") {
|
|
2692
|
-
this.graphRuntime.pause();
|
|
2693
|
-
}
|
|
2694
|
-
else {
|
|
2695
|
-
this.graphRuntime.resume();
|
|
2696
|
-
}
|
|
2625
|
+
this.graphRuntime.setRunMode(runMode);
|
|
2697
2626
|
}
|
|
2698
2627
|
}
|
|
2699
2628
|
|
|
@@ -4781,8 +4710,8 @@ exports.CompositeCategory = CompositeCategory;
|
|
|
4781
4710
|
exports.ComputeCategory = ComputeCategory;
|
|
4782
4711
|
exports.GraphBuilder = GraphBuilder;
|
|
4783
4712
|
exports.GraphRuntime = GraphRuntime;
|
|
4713
|
+
exports.LocalEngine = LocalEngine;
|
|
4784
4714
|
exports.Registry = Registry;
|
|
4785
|
-
exports.UnifiedEngine = UnifiedEngine;
|
|
4786
4715
|
exports.buildValueConverter = buildValueConverter;
|
|
4787
4716
|
exports.computeGraphCenter = computeGraphCenter;
|
|
4788
4717
|
exports.createAsyncGraphDef = createAsyncGraphDef;
|