graphai 0.0.3 → 0.0.4
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/.github/workflows/node.js.yml +27 -0
- package/lib/graphai.d.ts +28 -18
- package/lib/graphai.js +62 -29
- package/package.json +3 -3
- package/src/graphai.ts +89 -43
- package/tests/graphs/test_multiple_functions_1.yml +21 -0
- package/tests/sample_gpt.ts +3 -3
- package/tests/test_multiple_functions.ts +47 -0
- package/tests/{sample_flow.ts → test_sample_flow.ts} +16 -10
- package/lib/file_utils.d.ts +0 -3
- package/lib/file_utils.js +0 -30
- package/lib/flow.d.ts +0 -51
- package/lib/flow.js +0 -118
- /package/{src → tests}/file_utils.ts +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
|
3
|
+
|
|
4
|
+
name: Node.js CI
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
pull_request
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
node-version: [18.x, 20.x]
|
|
17
|
+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
22
|
+
uses: actions/setup-node@v4
|
|
23
|
+
with:
|
|
24
|
+
node-version: ${{ matrix.node-version }}
|
|
25
|
+
cache: 'npm'
|
|
26
|
+
- run: yarn install
|
|
27
|
+
- run: yarn test
|
package/lib/graphai.d.ts
CHANGED
|
@@ -5,54 +5,64 @@ export declare enum NodeState {
|
|
|
5
5
|
TimedOut = 3,
|
|
6
6
|
Completed = 4
|
|
7
7
|
}
|
|
8
|
-
type ResultData = Record<string, any
|
|
8
|
+
type ResultData<ResultType = Record<string, any>> = ResultType | undefined;
|
|
9
|
+
type ResultDataDictonary<ResultType = Record<string, any>> = Record<string, ResultData<ResultType>>;
|
|
9
10
|
export type NodeDataParams = Record<string, any>;
|
|
10
11
|
type NodeData = {
|
|
11
12
|
inputs: undefined | Array<string>;
|
|
12
13
|
params: NodeDataParams;
|
|
13
14
|
retry: undefined | number;
|
|
14
15
|
timeout: undefined | number;
|
|
16
|
+
functionName: undefined | string;
|
|
15
17
|
};
|
|
16
18
|
type GraphData = {
|
|
17
19
|
nodes: Record<string, NodeData>;
|
|
20
|
+
concurrency: number;
|
|
18
21
|
};
|
|
19
|
-
export type NodeExecuteContext = {
|
|
22
|
+
export type NodeExecuteContext<ResultType> = {
|
|
20
23
|
nodeId: string;
|
|
21
24
|
retry: number;
|
|
22
25
|
params: NodeDataParams;
|
|
23
|
-
payload:
|
|
26
|
+
payload: ResultDataDictonary<ResultType>;
|
|
24
27
|
};
|
|
25
|
-
type NodeExecute = (context: NodeExecuteContext) => Promise<ResultData
|
|
26
|
-
declare class Node {
|
|
28
|
+
type NodeExecute<ResultType> = (context: NodeExecuteContext<ResultType>) => Promise<ResultData<ResultType>>;
|
|
29
|
+
declare class Node<ResultType = Record<string, any>> {
|
|
27
30
|
nodeId: string;
|
|
28
31
|
params: NodeDataParams;
|
|
29
32
|
inputs: Array<string>;
|
|
30
33
|
pendings: Set<string>;
|
|
31
34
|
waitlist: Set<string>;
|
|
32
35
|
state: NodeState;
|
|
33
|
-
|
|
36
|
+
functionName: string;
|
|
37
|
+
result: ResultData<ResultType>;
|
|
34
38
|
retryLimit: number;
|
|
35
39
|
retryCount: number;
|
|
36
40
|
transactionId: undefined | number;
|
|
37
41
|
timeout: number;
|
|
38
|
-
|
|
42
|
+
private graph;
|
|
43
|
+
constructor(nodeId: string, data: NodeData, graph: GraphAI<ResultType>);
|
|
39
44
|
asString(): string;
|
|
40
45
|
private retry;
|
|
41
|
-
removePending(nodeId: string
|
|
42
|
-
payload(
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
removePending(nodeId: string): void;
|
|
47
|
+
payload(): ResultDataDictonary<ResultType>;
|
|
48
|
+
pushQueueIfReady(): void;
|
|
49
|
+
execute(): Promise<void>;
|
|
45
50
|
}
|
|
46
|
-
type GraphNodes = Record<string, Node
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
type GraphNodes<ResultType> = Record<string, Node<ResultType>>;
|
|
52
|
+
type NodeExecuteDictonary<ResultType> = Record<string, NodeExecute<ResultType>>;
|
|
53
|
+
export declare class GraphAI<ResultType = Record<string, any>> {
|
|
54
|
+
nodes: GraphNodes<ResultType>;
|
|
55
|
+
callbackDictonary: NodeExecuteDictonary<ResultType>;
|
|
50
56
|
private runningNodes;
|
|
57
|
+
private nodeQueue;
|
|
51
58
|
private onComplete;
|
|
52
|
-
|
|
59
|
+
private concurrency;
|
|
60
|
+
constructor(data: GraphData, callbackDictonary: NodeExecuteDictonary<ResultType> | NodeExecute<ResultType>);
|
|
61
|
+
getCallback(functionName: string): NodeExecute<ResultType>;
|
|
53
62
|
asString(): string;
|
|
54
63
|
run(): Promise<unknown>;
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
private runNode;
|
|
65
|
+
pushQueue(node: Node<ResultType>): void;
|
|
66
|
+
removeRunning(node: Node<ResultType>): void;
|
|
57
67
|
}
|
|
58
68
|
export {};
|
package/lib/graphai.js
CHANGED
|
@@ -10,90 +10,102 @@ var NodeState;
|
|
|
10
10
|
NodeState[NodeState["Completed"] = 4] = "Completed";
|
|
11
11
|
})(NodeState || (exports.NodeState = NodeState = {}));
|
|
12
12
|
class Node {
|
|
13
|
-
constructor(nodeId, data) {
|
|
13
|
+
constructor(nodeId, data, graph) {
|
|
14
14
|
this.nodeId = nodeId;
|
|
15
15
|
this.inputs = data.inputs ?? [];
|
|
16
16
|
this.pendings = new Set(this.inputs);
|
|
17
17
|
this.params = data.params;
|
|
18
18
|
this.waitlist = new Set();
|
|
19
19
|
this.state = NodeState.Waiting;
|
|
20
|
-
this.
|
|
20
|
+
this.functionName = data.functionName ?? "default";
|
|
21
|
+
this.result = undefined;
|
|
21
22
|
this.retryLimit = data.retry ?? 0;
|
|
22
23
|
this.retryCount = 0;
|
|
23
24
|
this.timeout = data.timeout ?? 0;
|
|
25
|
+
this.graph = graph;
|
|
24
26
|
}
|
|
25
27
|
asString() {
|
|
26
28
|
return `${this.nodeId}: ${this.state} ${[...this.waitlist]}`;
|
|
27
29
|
}
|
|
28
|
-
retry(
|
|
30
|
+
retry(state, result) {
|
|
29
31
|
if (this.retryCount < this.retryLimit) {
|
|
30
32
|
this.retryCount++;
|
|
31
|
-
this.execute(
|
|
33
|
+
this.execute();
|
|
32
34
|
}
|
|
33
35
|
else {
|
|
34
36
|
this.state = state;
|
|
35
37
|
this.result = result;
|
|
36
|
-
graph.removeRunning(this);
|
|
38
|
+
this.graph.removeRunning(this);
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
|
-
removePending(nodeId
|
|
41
|
+
removePending(nodeId) {
|
|
40
42
|
this.pendings.delete(nodeId);
|
|
41
|
-
this.
|
|
43
|
+
this.pushQueueIfReady();
|
|
42
44
|
}
|
|
43
|
-
payload(
|
|
45
|
+
payload() {
|
|
44
46
|
return this.inputs.reduce((results, nodeId) => {
|
|
45
|
-
results[nodeId] = graph.nodes[nodeId].result;
|
|
47
|
+
results[nodeId] = this.graph.nodes[nodeId].result;
|
|
46
48
|
return results;
|
|
47
49
|
}, {});
|
|
48
50
|
}
|
|
49
|
-
|
|
50
|
-
if (this.pendings.size
|
|
51
|
-
graph.
|
|
52
|
-
this.execute(graph);
|
|
51
|
+
pushQueueIfReady() {
|
|
52
|
+
if (this.pendings.size === 0) {
|
|
53
|
+
this.graph.pushQueue(this);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
|
-
async execute(
|
|
56
|
+
async execute() {
|
|
56
57
|
this.state = NodeState.Executing;
|
|
57
58
|
const transactionId = Date.now();
|
|
58
59
|
this.transactionId = transactionId;
|
|
59
60
|
if (this.timeout > 0) {
|
|
60
61
|
setTimeout(() => {
|
|
61
|
-
if (this.state
|
|
62
|
+
if (this.state === NodeState.Executing && this.transactionId === transactionId) {
|
|
62
63
|
console.log("*** timeout", this.timeout);
|
|
63
|
-
this.retry(
|
|
64
|
+
this.retry(NodeState.TimedOut, undefined);
|
|
64
65
|
}
|
|
65
66
|
}, this.timeout);
|
|
66
67
|
}
|
|
67
68
|
try {
|
|
68
|
-
const
|
|
69
|
+
const callback = this.graph.getCallback(this.functionName);
|
|
70
|
+
const result = await callback({
|
|
71
|
+
nodeId: this.nodeId,
|
|
72
|
+
retry: this.retryCount,
|
|
73
|
+
params: this.params,
|
|
74
|
+
payload: this.payload(),
|
|
75
|
+
});
|
|
69
76
|
if (this.transactionId !== transactionId) {
|
|
70
|
-
console.log("******
|
|
77
|
+
console.log("****** transactionId mismatch (success)");
|
|
71
78
|
return;
|
|
72
79
|
}
|
|
73
80
|
this.state = NodeState.Completed;
|
|
74
81
|
this.result = result;
|
|
75
82
|
this.waitlist.forEach((nodeId) => {
|
|
76
|
-
const node = graph.nodes[nodeId];
|
|
77
|
-
node.removePending(this.nodeId
|
|
83
|
+
const node = this.graph.nodes[nodeId];
|
|
84
|
+
node.removePending(this.nodeId);
|
|
78
85
|
});
|
|
79
|
-
graph.removeRunning(this);
|
|
86
|
+
this.graph.removeRunning(this);
|
|
80
87
|
}
|
|
81
88
|
catch (e) {
|
|
82
89
|
if (this.transactionId !== transactionId) {
|
|
83
|
-
console.log("******
|
|
90
|
+
console.log("****** transactionId mismatch (failed)");
|
|
84
91
|
return;
|
|
85
92
|
}
|
|
86
|
-
this.retry(
|
|
93
|
+
this.retry(NodeState.Failed, undefined);
|
|
87
94
|
}
|
|
88
95
|
}
|
|
89
96
|
}
|
|
90
97
|
class GraphAI {
|
|
91
|
-
constructor(data,
|
|
92
|
-
this.
|
|
98
|
+
constructor(data, callbackDictonary) {
|
|
99
|
+
this.callbackDictonary = typeof callbackDictonary === "function" ? { default: callbackDictonary } : callbackDictonary;
|
|
100
|
+
if (this.callbackDictonary["default"] === undefined) {
|
|
101
|
+
throw new Error("No default function");
|
|
102
|
+
}
|
|
103
|
+
this.concurrency = data.concurrency ?? 2;
|
|
93
104
|
this.runningNodes = new Set();
|
|
105
|
+
this.nodeQueue = [];
|
|
94
106
|
this.onComplete = () => { };
|
|
95
107
|
this.nodes = Object.keys(data.nodes).reduce((nodes, nodeId) => {
|
|
96
|
-
nodes[nodeId] = new Node(nodeId, data.nodes[nodeId]);
|
|
108
|
+
nodes[nodeId] = new Node(nodeId, data.nodes[nodeId], this);
|
|
97
109
|
return nodes;
|
|
98
110
|
}, {});
|
|
99
111
|
// Generate the waitlist for each node
|
|
@@ -105,6 +117,12 @@ class GraphAI {
|
|
|
105
117
|
});
|
|
106
118
|
});
|
|
107
119
|
}
|
|
120
|
+
getCallback(functionName) {
|
|
121
|
+
if (functionName && this.callbackDictonary[functionName]) {
|
|
122
|
+
return this.callbackDictonary[functionName];
|
|
123
|
+
}
|
|
124
|
+
return this.callbackDictonary["default"];
|
|
125
|
+
}
|
|
108
126
|
asString() {
|
|
109
127
|
return Object.keys(this.nodes)
|
|
110
128
|
.map((nodeId) => {
|
|
@@ -116,7 +134,7 @@ class GraphAI {
|
|
|
116
134
|
// Nodes without pending data should run immediately.
|
|
117
135
|
Object.keys(this.nodes).forEach((nodeId) => {
|
|
118
136
|
const node = this.nodes[nodeId];
|
|
119
|
-
node.
|
|
137
|
+
node.pushQueueIfReady();
|
|
120
138
|
});
|
|
121
139
|
return new Promise((resolve, reject) => {
|
|
122
140
|
this.onComplete = () => {
|
|
@@ -128,12 +146,27 @@ class GraphAI {
|
|
|
128
146
|
};
|
|
129
147
|
});
|
|
130
148
|
}
|
|
131
|
-
|
|
149
|
+
runNode(node) {
|
|
132
150
|
this.runningNodes.add(node.nodeId);
|
|
151
|
+
node.execute();
|
|
152
|
+
}
|
|
153
|
+
pushQueue(node) {
|
|
154
|
+
if (this.runningNodes.size < this.concurrency) {
|
|
155
|
+
this.runNode(node);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
this.nodeQueue.push(node);
|
|
159
|
+
}
|
|
133
160
|
}
|
|
134
161
|
removeRunning(node) {
|
|
135
162
|
this.runningNodes.delete(node.nodeId);
|
|
136
|
-
if (this.
|
|
163
|
+
if (this.nodeQueue.length > 0) {
|
|
164
|
+
const n = this.nodeQueue.shift();
|
|
165
|
+
if (n) {
|
|
166
|
+
this.runNode(n);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (this.runningNodes.size === 0) {
|
|
137
170
|
this.onComplete();
|
|
138
171
|
}
|
|
139
172
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphai",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Asynchronous data flow execution engine to make it simple to build LLM apps.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "tsc",
|
|
8
8
|
"eslint": "eslint --fix --ext src/**/*.{ts} tests/**/*.ts",
|
|
9
9
|
"format": "prettier --write '{src,tests}/**/*.ts'",
|
|
10
|
-
"test": "node --test --require ts-node/register ./tests/
|
|
10
|
+
"test": "node --test --require ts-node/register ./tests/test_*.ts"
|
|
11
11
|
},
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"@typescript-eslint/parser": "^6.8.0",
|
|
26
26
|
"eslint": "^7.32.0 || ^8.2.0",
|
|
27
27
|
"eslint-plugin-import": "^2.25.2",
|
|
28
|
+
"openai": "^4.12.4",
|
|
28
29
|
"prettier": "^3.0.3",
|
|
29
30
|
"slashgpt": "^0.0.6",
|
|
30
31
|
"ts-node": "^10.9.1",
|
|
31
32
|
"typescript": "^5.2.2"
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
|
-
"openai": "^4.12.4",
|
|
35
35
|
"yaml": "^2.3.3"
|
|
36
36
|
},
|
|
37
37
|
"types": "./lib/index.d.ts",
|
package/src/graphai.ts
CHANGED
|
@@ -7,7 +7,9 @@ export enum NodeState {
|
|
|
7
7
|
TimedOut,
|
|
8
8
|
Completed,
|
|
9
9
|
}
|
|
10
|
-
type ResultData = Record<string, any
|
|
10
|
+
type ResultData<ResultType = Record<string, any>> = ResultType | undefined;
|
|
11
|
+
type ResultDataDictonary<ResultType = Record<string, any>> = Record<string, ResultData<ResultType>>;
|
|
12
|
+
|
|
11
13
|
export type NodeDataParams = Record<string, any>; // App-specific parameters
|
|
12
14
|
|
|
13
15
|
type NodeData = {
|
|
@@ -15,132 +17,154 @@ type NodeData = {
|
|
|
15
17
|
params: NodeDataParams;
|
|
16
18
|
retry: undefined | number;
|
|
17
19
|
timeout: undefined | number; // msec
|
|
20
|
+
functionName: undefined | string;
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
type GraphData = {
|
|
21
24
|
nodes: Record<string, NodeData>;
|
|
25
|
+
concurrency: number;
|
|
22
26
|
};
|
|
23
27
|
|
|
24
|
-
export type NodeExecuteContext = {
|
|
28
|
+
export type NodeExecuteContext<ResultType> = {
|
|
25
29
|
nodeId: string;
|
|
26
30
|
retry: number;
|
|
27
31
|
params: NodeDataParams;
|
|
28
|
-
payload:
|
|
32
|
+
payload: ResultDataDictonary<ResultType>;
|
|
29
33
|
};
|
|
30
34
|
|
|
31
|
-
type NodeExecute = (context: NodeExecuteContext) => Promise<ResultData
|
|
35
|
+
type NodeExecute<ResultType> = (context: NodeExecuteContext<ResultType>) => Promise<ResultData<ResultType>>;
|
|
32
36
|
|
|
33
|
-
class Node {
|
|
37
|
+
class Node<ResultType = Record<string, any>> {
|
|
34
38
|
public nodeId: string;
|
|
35
39
|
public params: NodeDataParams; // App-specific parameters
|
|
36
40
|
public inputs: Array<string>; // List of nodes this node needs data from.
|
|
37
41
|
public pendings: Set<string>; // List of nodes this node is waiting data from.
|
|
38
42
|
public waitlist: Set<string>; // List of nodes which need data from this node.
|
|
39
43
|
public state: NodeState;
|
|
40
|
-
public
|
|
44
|
+
public functionName: string;
|
|
45
|
+
public result: ResultData<ResultType>;
|
|
41
46
|
public retryLimit: number;
|
|
42
47
|
public retryCount: number;
|
|
43
48
|
public transactionId: undefined | number; // To reject callbacks from timed-out transactions
|
|
44
49
|
public timeout: number; // msec
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
private graph: GraphAI<ResultType>;
|
|
52
|
+
|
|
53
|
+
constructor(nodeId: string, data: NodeData, graph: GraphAI<ResultType>) {
|
|
47
54
|
this.nodeId = nodeId;
|
|
48
55
|
this.inputs = data.inputs ?? [];
|
|
49
56
|
this.pendings = new Set(this.inputs);
|
|
50
57
|
this.params = data.params;
|
|
51
58
|
this.waitlist = new Set<string>();
|
|
52
59
|
this.state = NodeState.Waiting;
|
|
53
|
-
this.
|
|
60
|
+
this.functionName = data.functionName ?? "default";
|
|
61
|
+
this.result = undefined;
|
|
54
62
|
this.retryLimit = data.retry ?? 0;
|
|
55
63
|
this.retryCount = 0;
|
|
56
64
|
this.timeout = data.timeout ?? 0;
|
|
65
|
+
|
|
66
|
+
this.graph = graph;
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
public asString() {
|
|
60
70
|
return `${this.nodeId}: ${this.state} ${[...this.waitlist]}`;
|
|
61
71
|
}
|
|
62
72
|
|
|
63
|
-
private retry(
|
|
73
|
+
private retry(state: NodeState, result: ResultData<ResultType>) {
|
|
64
74
|
if (this.retryCount < this.retryLimit) {
|
|
65
75
|
this.retryCount++;
|
|
66
|
-
this.execute(
|
|
76
|
+
this.execute();
|
|
67
77
|
} else {
|
|
68
78
|
this.state = state;
|
|
69
79
|
this.result = result;
|
|
70
|
-
graph.removeRunning(this);
|
|
80
|
+
this.graph.removeRunning(this);
|
|
71
81
|
}
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
public removePending(nodeId: string
|
|
84
|
+
public removePending(nodeId: string) {
|
|
75
85
|
this.pendings.delete(nodeId);
|
|
76
|
-
this.
|
|
86
|
+
this.pushQueueIfReady();
|
|
77
87
|
}
|
|
78
88
|
|
|
79
|
-
public payload(
|
|
80
|
-
return this.inputs.reduce((results:
|
|
81
|
-
results[nodeId] = graph.nodes[nodeId].result;
|
|
89
|
+
public payload() {
|
|
90
|
+
return this.inputs.reduce((results: ResultDataDictonary<ResultType>, nodeId) => {
|
|
91
|
+
results[nodeId] = this.graph.nodes[nodeId].result;
|
|
82
92
|
return results;
|
|
83
93
|
}, {});
|
|
84
94
|
}
|
|
85
95
|
|
|
86
|
-
public
|
|
87
|
-
if (this.pendings.size
|
|
88
|
-
graph.
|
|
89
|
-
this.execute(graph);
|
|
96
|
+
public pushQueueIfReady() {
|
|
97
|
+
if (this.pendings.size === 0) {
|
|
98
|
+
this.graph.pushQueue(this);
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
101
|
|
|
93
|
-
|
|
102
|
+
public async execute() {
|
|
94
103
|
this.state = NodeState.Executing;
|
|
95
104
|
const transactionId = Date.now();
|
|
96
105
|
this.transactionId = transactionId;
|
|
97
106
|
|
|
98
107
|
if (this.timeout > 0) {
|
|
99
108
|
setTimeout(() => {
|
|
100
|
-
if (this.state
|
|
109
|
+
if (this.state === NodeState.Executing && this.transactionId === transactionId) {
|
|
101
110
|
console.log("*** timeout", this.timeout);
|
|
102
|
-
this.retry(
|
|
111
|
+
this.retry(NodeState.TimedOut, undefined);
|
|
103
112
|
}
|
|
104
113
|
}, this.timeout);
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
try {
|
|
108
|
-
const
|
|
117
|
+
const callback = this.graph.getCallback(this.functionName);
|
|
118
|
+
const result = await callback({
|
|
119
|
+
nodeId: this.nodeId,
|
|
120
|
+
retry: this.retryCount,
|
|
121
|
+
params: this.params,
|
|
122
|
+
payload: this.payload(),
|
|
123
|
+
});
|
|
109
124
|
if (this.transactionId !== transactionId) {
|
|
110
|
-
console.log("******
|
|
125
|
+
console.log("****** transactionId mismatch (success)");
|
|
111
126
|
return;
|
|
112
127
|
}
|
|
113
128
|
this.state = NodeState.Completed;
|
|
114
129
|
this.result = result;
|
|
115
130
|
this.waitlist.forEach((nodeId) => {
|
|
116
|
-
const node = graph.nodes[nodeId];
|
|
117
|
-
node.removePending(this.nodeId
|
|
131
|
+
const node = this.graph.nodes[nodeId];
|
|
132
|
+
node.removePending(this.nodeId);
|
|
118
133
|
});
|
|
119
|
-
graph.removeRunning(this);
|
|
134
|
+
this.graph.removeRunning(this);
|
|
120
135
|
} catch (e) {
|
|
121
136
|
if (this.transactionId !== transactionId) {
|
|
122
|
-
console.log("******
|
|
137
|
+
console.log("****** transactionId mismatch (failed)");
|
|
123
138
|
return;
|
|
124
139
|
}
|
|
125
|
-
this.retry(
|
|
140
|
+
this.retry(NodeState.Failed, undefined);
|
|
126
141
|
}
|
|
127
142
|
}
|
|
128
143
|
}
|
|
129
144
|
|
|
130
|
-
type GraphNodes = Record<string, Node
|
|
145
|
+
type GraphNodes<ResultType> = Record<string, Node<ResultType>>;
|
|
131
146
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
147
|
+
type NodeExecuteDictonary<ResultType> = Record<string, NodeExecute<ResultType>>;
|
|
148
|
+
|
|
149
|
+
export class GraphAI<ResultType = Record<string, any>> {
|
|
150
|
+
public nodes: GraphNodes<ResultType>;
|
|
151
|
+
public callbackDictonary: NodeExecuteDictonary<ResultType>;
|
|
135
152
|
private runningNodes: Set<string>;
|
|
153
|
+
private nodeQueue: Array<Node<ResultType>>;
|
|
136
154
|
private onComplete: () => void;
|
|
155
|
+
private concurrency: number;
|
|
137
156
|
|
|
138
|
-
constructor(data: GraphData,
|
|
139
|
-
this.
|
|
157
|
+
constructor(data: GraphData, callbackDictonary: NodeExecuteDictonary<ResultType> | NodeExecute<ResultType>) {
|
|
158
|
+
this.callbackDictonary = typeof callbackDictonary === "function" ? { default: callbackDictonary } : callbackDictonary;
|
|
159
|
+
if (this.callbackDictonary["default"] === undefined) {
|
|
160
|
+
throw new Error("No default function");
|
|
161
|
+
}
|
|
162
|
+
this.concurrency = data.concurrency ?? 2;
|
|
140
163
|
this.runningNodes = new Set<string>();
|
|
164
|
+
this.nodeQueue = [];
|
|
141
165
|
this.onComplete = () => {};
|
|
142
|
-
this.nodes = Object.keys(data.nodes).reduce((nodes: GraphNodes
|
|
143
|
-
nodes[nodeId] = new Node(nodeId, data.nodes[nodeId]);
|
|
166
|
+
this.nodes = Object.keys(data.nodes).reduce((nodes: GraphNodes<ResultType>, nodeId: string) => {
|
|
167
|
+
nodes[nodeId] = new Node<ResultType>(nodeId, data.nodes[nodeId], this);
|
|
144
168
|
return nodes;
|
|
145
169
|
}, {});
|
|
146
170
|
|
|
@@ -154,6 +178,13 @@ export class GraphAI {
|
|
|
154
178
|
});
|
|
155
179
|
}
|
|
156
180
|
|
|
181
|
+
public getCallback(functionName: string) {
|
|
182
|
+
if (functionName && this.callbackDictonary[functionName]) {
|
|
183
|
+
return this.callbackDictonary[functionName];
|
|
184
|
+
}
|
|
185
|
+
return this.callbackDictonary["default"];
|
|
186
|
+
}
|
|
187
|
+
|
|
157
188
|
public asString() {
|
|
158
189
|
return Object.keys(this.nodes)
|
|
159
190
|
.map((nodeId) => {
|
|
@@ -166,12 +197,12 @@ export class GraphAI {
|
|
|
166
197
|
// Nodes without pending data should run immediately.
|
|
167
198
|
Object.keys(this.nodes).forEach((nodeId) => {
|
|
168
199
|
const node = this.nodes[nodeId];
|
|
169
|
-
node.
|
|
200
|
+
node.pushQueueIfReady();
|
|
170
201
|
});
|
|
171
202
|
|
|
172
203
|
return new Promise((resolve, reject) => {
|
|
173
204
|
this.onComplete = () => {
|
|
174
|
-
const results = Object.keys(this.nodes).reduce((results:
|
|
205
|
+
const results = Object.keys(this.nodes).reduce((results: ResultDataDictonary<ResultType>, nodeId) => {
|
|
175
206
|
results[nodeId] = this.nodes[nodeId].result;
|
|
176
207
|
return results;
|
|
177
208
|
}, {});
|
|
@@ -180,13 +211,28 @@ export class GraphAI {
|
|
|
180
211
|
});
|
|
181
212
|
}
|
|
182
213
|
|
|
183
|
-
|
|
214
|
+
private runNode(node: Node<ResultType>) {
|
|
184
215
|
this.runningNodes.add(node.nodeId);
|
|
216
|
+
node.execute();
|
|
185
217
|
}
|
|
186
218
|
|
|
187
|
-
public
|
|
219
|
+
public pushQueue(node: Node<ResultType>) {
|
|
220
|
+
if (this.runningNodes.size < this.concurrency) {
|
|
221
|
+
this.runNode(node);
|
|
222
|
+
} else {
|
|
223
|
+
this.nodeQueue.push(node);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
public removeRunning(node: Node<ResultType>) {
|
|
188
228
|
this.runningNodes.delete(node.nodeId);
|
|
189
|
-
if (this.
|
|
229
|
+
if (this.nodeQueue.length > 0) {
|
|
230
|
+
const n = this.nodeQueue.shift();
|
|
231
|
+
if (n) {
|
|
232
|
+
this.runNode(n);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (this.runningNodes.size === 0) {
|
|
190
236
|
this.onComplete();
|
|
191
237
|
}
|
|
192
238
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
nodes:
|
|
2
|
+
node1:
|
|
3
|
+
params:
|
|
4
|
+
delay: 500
|
|
5
|
+
node2:
|
|
6
|
+
params:
|
|
7
|
+
delay: 100
|
|
8
|
+
node3:
|
|
9
|
+
params:
|
|
10
|
+
delay: 500
|
|
11
|
+
inputs: [node1, node2]
|
|
12
|
+
functionName: test2
|
|
13
|
+
node4:
|
|
14
|
+
params:
|
|
15
|
+
delay: 100
|
|
16
|
+
inputs: [node3]
|
|
17
|
+
node5:
|
|
18
|
+
params:
|
|
19
|
+
delay: 500
|
|
20
|
+
inputs: [node2, node4]
|
|
21
|
+
functionName: test2
|
package/tests/sample_gpt.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { GraphAI, NodeExecuteContext } from "../src/graphai";
|
|
3
3
|
import { ChatSession, ChatConfig } from "slashgpt";
|
|
4
|
-
import { readManifestData } from "
|
|
4
|
+
import { readManifestData } from "./file_utils";
|
|
5
5
|
|
|
6
6
|
const config = new ChatConfig(path.resolve(__dirname));
|
|
7
7
|
|
|
8
|
-
const testFunction = async (context: NodeExecuteContext) => {
|
|
8
|
+
const testFunction = async (context: NodeExecuteContext<Record<string, string>>) => {
|
|
9
9
|
console.log("executing", context.nodeId, context.params, context.payload);
|
|
10
10
|
const session = new ChatSession(config, context.params.manifest ?? {});
|
|
11
11
|
const prompt = Object.keys(context.payload).reduce((prompt, key) => {
|
|
12
|
-
return prompt.replace("${" + key + "}", context.payload[key]["answer"]);
|
|
12
|
+
return prompt.replace("${" + key + "}", context.payload[key]!["answer"]);
|
|
13
13
|
}, context.params.prompt);
|
|
14
14
|
session.append_user_question(prompt);
|
|
15
15
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { GraphAI, NodeExecuteContext } from "../src/graphai";
|
|
3
|
+
import { readManifestData } from "../src/file_utils";
|
|
4
|
+
import { sleep } from "./utils";
|
|
5
|
+
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import assert from "node:assert";
|
|
8
|
+
|
|
9
|
+
const testFunction1 = async (context: NodeExecuteContext<Record<string, string>>) => {
|
|
10
|
+
const { nodeId, retry, params } = context;
|
|
11
|
+
console.log("executing", nodeId, params);
|
|
12
|
+
|
|
13
|
+
const result = { [nodeId]: "output 1" };
|
|
14
|
+
console.log("completing", nodeId, result);
|
|
15
|
+
return result;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const testFunction2 = async (context: NodeExecuteContext<Record<string, string>>) => {
|
|
19
|
+
const { nodeId, retry, params } = context;
|
|
20
|
+
console.log("executing", nodeId, params);
|
|
21
|
+
|
|
22
|
+
const result = { [nodeId]: "output 2" };
|
|
23
|
+
console.log("completing", nodeId, result);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const runTest = async (file: string) => {
|
|
28
|
+
const file_path = path.resolve(__dirname) + file;
|
|
29
|
+
const graph_data = readManifestData(file_path);
|
|
30
|
+
|
|
31
|
+
const graph = new GraphAI(graph_data, { default: testFunction1, test2: testFunction2 });
|
|
32
|
+
|
|
33
|
+
const results = await graph.run();
|
|
34
|
+
console.log(results);
|
|
35
|
+
return results;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
test("test sample1", async () => {
|
|
39
|
+
const result = await runTest("/graphs/test_multiple_functions_1.yml");
|
|
40
|
+
assert.deepStrictEqual(result, {
|
|
41
|
+
node1: { node1: "output 1" },
|
|
42
|
+
node2: { node2: "output 1" },
|
|
43
|
+
node3: { node3: "output 2" },
|
|
44
|
+
node4: { node4: "output 1" },
|
|
45
|
+
node5: { node5: "output 2" },
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { GraphAI, NodeExecuteContext } from "../src/graphai";
|
|
3
|
-
import { readManifestData } from "
|
|
3
|
+
import { readManifestData } from "./file_utils";
|
|
4
4
|
import { sleep } from "./utils";
|
|
5
5
|
|
|
6
6
|
import test from "node:test";
|
|
7
7
|
import assert from "node:assert";
|
|
8
8
|
|
|
9
|
-
const testFunction = async (context: NodeExecuteContext) => {
|
|
10
|
-
const { nodeId, retry, params } = context;
|
|
9
|
+
const testFunction = async (context: NodeExecuteContext<Record<string, string>>) => {
|
|
10
|
+
const { nodeId, retry, params, payload } = context;
|
|
11
11
|
console.log("executing", nodeId, params);
|
|
12
12
|
await sleep(params.delay / (retry + 1));
|
|
13
13
|
|
|
@@ -16,7 +16,13 @@ const testFunction = async (context: NodeExecuteContext) => {
|
|
|
16
16
|
console.log("failed", nodeId, result, retry);
|
|
17
17
|
throw new Error("Intentional Failure");
|
|
18
18
|
} else {
|
|
19
|
-
const result =
|
|
19
|
+
const result = Object.keys(payload).reduce(
|
|
20
|
+
(result, key) => {
|
|
21
|
+
result = { ...result, ...payload[key] };
|
|
22
|
+
return result;
|
|
23
|
+
},
|
|
24
|
+
{ [nodeId]: "output" },
|
|
25
|
+
);
|
|
20
26
|
console.log("completing", nodeId, result);
|
|
21
27
|
return result;
|
|
22
28
|
}
|
|
@@ -38,9 +44,9 @@ test("test sample1", async () => {
|
|
|
38
44
|
assert.deepStrictEqual(result, {
|
|
39
45
|
node1: { node1: "output" },
|
|
40
46
|
node2: { node2: "output" },
|
|
41
|
-
node3: { node3: "output" },
|
|
42
|
-
node4: { node4: "output" },
|
|
43
|
-
node5: { node5: "output" },
|
|
47
|
+
node3: { node3: "output", node1: "output", node2: "output" },
|
|
48
|
+
node4: { node4: "output", node3: "output", node1: "output", node2: "output" },
|
|
49
|
+
node5: { node5: "output", node4: "output", node3: "output", node1: "output", node2: "output" },
|
|
44
50
|
});
|
|
45
51
|
});
|
|
46
52
|
|
|
@@ -49,9 +55,9 @@ test("test sample1", async () => {
|
|
|
49
55
|
assert.deepStrictEqual(result, {
|
|
50
56
|
node1: { node1: "output" },
|
|
51
57
|
node2: { node2: "output" },
|
|
52
|
-
node3: { node3: "output" },
|
|
53
|
-
node4: { node4: "output" },
|
|
54
|
-
node5: { node5: "output" },
|
|
58
|
+
node3: { node3: "output", node1: "output", node2: "output" },
|
|
59
|
+
node4: { node4: "output", node3: "output", node1: "output", node2: "output" },
|
|
60
|
+
node5: { node5: "output", node4: "output", node3: "output", node1: "output", node2: "output" },
|
|
55
61
|
});
|
|
56
62
|
console.log("COMPLETE 2");
|
|
57
63
|
});
|
package/lib/file_utils.d.ts
DELETED
package/lib/file_utils.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.readYamlManifest = exports.readJsonManifest = exports.readManifestData = void 0;
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const yaml_1 = __importDefault(require("yaml"));
|
|
9
|
-
const readManifestData = (file) => {
|
|
10
|
-
if (file.endsWith(".yaml") || file.endsWith(".yml")) {
|
|
11
|
-
return (0, exports.readYamlManifest)(file);
|
|
12
|
-
}
|
|
13
|
-
if (file.endsWith(".json")) {
|
|
14
|
-
return (0, exports.readJsonManifest)(file);
|
|
15
|
-
}
|
|
16
|
-
throw new Error("No file exists " + file);
|
|
17
|
-
};
|
|
18
|
-
exports.readManifestData = readManifestData;
|
|
19
|
-
const readJsonManifest = (fileName) => {
|
|
20
|
-
const manifest_file = fs_1.default.readFileSync(fileName, "utf8");
|
|
21
|
-
const manifest = JSON.parse(manifest_file);
|
|
22
|
-
return manifest;
|
|
23
|
-
};
|
|
24
|
-
exports.readJsonManifest = readJsonManifest;
|
|
25
|
-
const readYamlManifest = (fileName) => {
|
|
26
|
-
const manifest_file = fs_1.default.readFileSync(fileName, "utf8");
|
|
27
|
-
const manifest = yaml_1.default.parse(manifest_file);
|
|
28
|
-
return manifest;
|
|
29
|
-
};
|
|
30
|
-
exports.readYamlManifest = readYamlManifest;
|
package/lib/flow.d.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
export declare enum NodeState {
|
|
2
|
-
Waiting = 0,
|
|
3
|
-
Executing = 1,
|
|
4
|
-
Failed = 2,
|
|
5
|
-
Completed = 3
|
|
6
|
-
}
|
|
7
|
-
type NodeData = {
|
|
8
|
-
inputs: undefined | Array<string>;
|
|
9
|
-
params: any;
|
|
10
|
-
retry: undefined | number;
|
|
11
|
-
};
|
|
12
|
-
type FlowData = {
|
|
13
|
-
nodes: Record<string, NodeData>;
|
|
14
|
-
};
|
|
15
|
-
export declare enum FlowCommand {
|
|
16
|
-
Log = 0,
|
|
17
|
-
Execute = 1,
|
|
18
|
-
OnComplete = 2
|
|
19
|
-
}
|
|
20
|
-
type FlowCallback = (params: Record<string, any>) => void;
|
|
21
|
-
declare class Node {
|
|
22
|
-
key: string;
|
|
23
|
-
inputs: Array<string>;
|
|
24
|
-
pendings: Set<string>;
|
|
25
|
-
params: any;
|
|
26
|
-
waitlist: Set<string>;
|
|
27
|
-
state: NodeState;
|
|
28
|
-
result: Record<string, any>;
|
|
29
|
-
retryLimit: number;
|
|
30
|
-
retryCount: number;
|
|
31
|
-
constructor(key: string, data: NodeData);
|
|
32
|
-
asString(): string;
|
|
33
|
-
complete(result: Record<string, any>, nodes: Record<string, Node>, graph: GraphAI): void;
|
|
34
|
-
reportError(result: Record<string, any>, nodes: Record<string, Node>, graph: GraphAI): void;
|
|
35
|
-
removePending(key: string, graph: GraphAI): void;
|
|
36
|
-
payload(graph: GraphAI): Record<string, any>;
|
|
37
|
-
executeIfReady(graph: GraphAI): void;
|
|
38
|
-
}
|
|
39
|
-
export declare class GraphAI {
|
|
40
|
-
nodes: Record<string, Node>;
|
|
41
|
-
callback: FlowCallback;
|
|
42
|
-
private runningNodes;
|
|
43
|
-
constructor(data: FlowData, callback: FlowCallback);
|
|
44
|
-
asString(): string;
|
|
45
|
-
run(): void;
|
|
46
|
-
feed(key: string, result: Record<string, any>): void;
|
|
47
|
-
reportError(key: string, result: Record<string, any>): void;
|
|
48
|
-
add(node: Node): void;
|
|
49
|
-
remove(node: Node): void;
|
|
50
|
-
}
|
|
51
|
-
export {};
|
package/lib/flow.js
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GraphAI = exports.FlowCommand = exports.NodeState = void 0;
|
|
4
|
-
var NodeState;
|
|
5
|
-
(function (NodeState) {
|
|
6
|
-
NodeState[NodeState["Waiting"] = 0] = "Waiting";
|
|
7
|
-
NodeState[NodeState["Executing"] = 1] = "Executing";
|
|
8
|
-
NodeState[NodeState["Failed"] = 2] = "Failed";
|
|
9
|
-
NodeState[NodeState["Completed"] = 3] = "Completed";
|
|
10
|
-
})(NodeState || (exports.NodeState = NodeState = {}));
|
|
11
|
-
var FlowCommand;
|
|
12
|
-
(function (FlowCommand) {
|
|
13
|
-
FlowCommand[FlowCommand["Log"] = 0] = "Log";
|
|
14
|
-
FlowCommand[FlowCommand["Execute"] = 1] = "Execute";
|
|
15
|
-
FlowCommand[FlowCommand["OnComplete"] = 2] = "OnComplete";
|
|
16
|
-
})(FlowCommand || (exports.FlowCommand = FlowCommand = {}));
|
|
17
|
-
class Node {
|
|
18
|
-
constructor(key, data) {
|
|
19
|
-
this.key = key;
|
|
20
|
-
this.inputs = data.inputs ?? [];
|
|
21
|
-
this.pendings = new Set(this.inputs);
|
|
22
|
-
this.params = data.params;
|
|
23
|
-
this.waitlist = new Set();
|
|
24
|
-
this.state = NodeState.Waiting;
|
|
25
|
-
this.result = {};
|
|
26
|
-
this.retryLimit = data.retry ?? 0;
|
|
27
|
-
this.retryCount = 0;
|
|
28
|
-
}
|
|
29
|
-
asString() {
|
|
30
|
-
return `${this.key}: ${this.state} ${[...this.waitlist]}`;
|
|
31
|
-
}
|
|
32
|
-
complete(result, nodes, graph) {
|
|
33
|
-
this.state = NodeState.Completed;
|
|
34
|
-
this.result = result;
|
|
35
|
-
this.waitlist.forEach(key => {
|
|
36
|
-
const node = nodes[key];
|
|
37
|
-
node.removePending(this.key, graph);
|
|
38
|
-
});
|
|
39
|
-
graph.remove(this);
|
|
40
|
-
}
|
|
41
|
-
reportError(result, nodes, graph) {
|
|
42
|
-
this.state = NodeState.Failed;
|
|
43
|
-
this.result = result;
|
|
44
|
-
if (this.retryCount < this.retryLimit) {
|
|
45
|
-
this.retryCount++;
|
|
46
|
-
this.state = NodeState.Executing;
|
|
47
|
-
graph.callback({ cmd: FlowCommand.Execute, node: this.key, params: this.params, retry: this.retryCount, payload: this.payload(graph) });
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
graph.remove(this);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
removePending(key, graph) {
|
|
54
|
-
this.pendings.delete(key);
|
|
55
|
-
this.executeIfReady(graph);
|
|
56
|
-
}
|
|
57
|
-
payload(graph) {
|
|
58
|
-
const foo = {};
|
|
59
|
-
return this.inputs.reduce((payload, key) => {
|
|
60
|
-
payload[key] = graph.nodes[key].result;
|
|
61
|
-
return payload;
|
|
62
|
-
}, foo);
|
|
63
|
-
}
|
|
64
|
-
executeIfReady(graph) {
|
|
65
|
-
if (this.pendings.size == 0) {
|
|
66
|
-
this.state = NodeState.Executing;
|
|
67
|
-
graph.add(this);
|
|
68
|
-
graph.callback({ cmd: FlowCommand.Execute, node: this.key, params: this.params, retry: 0, payload: this.payload(graph) });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
class GraphAI {
|
|
73
|
-
constructor(data, callback) {
|
|
74
|
-
this.callback = callback;
|
|
75
|
-
this.runningNodes = new Set();
|
|
76
|
-
const foo = {}; // HACK: Work around
|
|
77
|
-
this.nodes = Object.keys(data.nodes).reduce((nodes, key) => {
|
|
78
|
-
nodes[key] = new Node(key, data.nodes[key]);
|
|
79
|
-
return nodes;
|
|
80
|
-
}, foo);
|
|
81
|
-
// Generate the waitlist for each node
|
|
82
|
-
Object.keys(this.nodes).forEach(key => {
|
|
83
|
-
const node = this.nodes[key];
|
|
84
|
-
node.pendings.forEach(pending => {
|
|
85
|
-
const node2 = this.nodes[pending];
|
|
86
|
-
node2.waitlist.add(key);
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
asString() {
|
|
91
|
-
return Object.keys(this.nodes).map((key) => { return this.nodes[key].asString(); }).join('\n');
|
|
92
|
-
}
|
|
93
|
-
run() {
|
|
94
|
-
// Nodes without pending data should run immediately.
|
|
95
|
-
Object.keys(this.nodes).forEach(key => {
|
|
96
|
-
const node = this.nodes[key];
|
|
97
|
-
node.executeIfReady(this);
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
feed(key, result) {
|
|
101
|
-
const node = this.nodes[key];
|
|
102
|
-
node.complete(result, this.nodes, this);
|
|
103
|
-
}
|
|
104
|
-
reportError(key, result) {
|
|
105
|
-
const node = this.nodes[key];
|
|
106
|
-
node.reportError(result, this.nodes, this);
|
|
107
|
-
}
|
|
108
|
-
add(node) {
|
|
109
|
-
this.runningNodes.add(node.key);
|
|
110
|
-
}
|
|
111
|
-
remove(node) {
|
|
112
|
-
this.runningNodes.delete(node.key);
|
|
113
|
-
if (this.runningNodes.size == 0) {
|
|
114
|
-
this.callback({ cmd: FlowCommand.OnComplete });
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
exports.GraphAI = GraphAI;
|
|
File without changes
|