graphai 2.0.13 → 2.0.14

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/bundle.esm.js DELETED
@@ -1,1658 +0,0 @@
1
- var NodeState;
2
- (function (NodeState) {
3
- NodeState["Waiting"] = "waiting";
4
- NodeState["Queued"] = "queued";
5
- NodeState["Executing"] = "executing";
6
- NodeState["ExecutingServer"] = "executing-server";
7
- NodeState["Failed"] = "failed";
8
- NodeState["TimedOut"] = "timed-out";
9
- NodeState["Abort"] = "abort";
10
- NodeState["Completed"] = "completed";
11
- NodeState["Injected"] = "injected";
12
- NodeState["Skipped"] = "skipped";
13
- })(NodeState || (NodeState = {}));
14
-
15
- const enabledLevels = {
16
- debug: true,
17
- info: true,
18
- log: true,
19
- warn: true,
20
- error: true,
21
- };
22
- let customLogger = null;
23
- function setLevelEnabled(level, enabled) {
24
- enabledLevels[level] = enabled;
25
- }
26
- function setLogger(logger) {
27
- customLogger = logger;
28
- }
29
- function output(level, ...args) {
30
- if (!enabledLevels[level])
31
- return;
32
- if (customLogger) {
33
- customLogger(level, ...args);
34
- }
35
- else {
36
- (console[level] || console.log)(...args);
37
- }
38
- }
39
- function debug(...args) {
40
- output("debug", ...args);
41
- }
42
- function info(...args) {
43
- output("info", ...args);
44
- }
45
- function log(...args) {
46
- output("log", ...args);
47
- }
48
- function warn(...args) {
49
- output("warn", ...args);
50
- }
51
- function error(...args) {
52
- output("error", ...args);
53
- }
54
- const GraphAILogger = {
55
- setLevelEnabled,
56
- setLogger,
57
- debug,
58
- info,
59
- log,
60
- warn,
61
- error,
62
- };
63
-
64
- const propFunctionRegex = /^[a-zA-Z]+\([^)]*\)$/;
65
- const propArrayFunction = (result, propId) => {
66
- if (Array.isArray(result)) {
67
- if (propId === "length()") {
68
- return result.length;
69
- }
70
- if (propId === "flat()") {
71
- return result.flat();
72
- }
73
- if (propId === "toJSON()") {
74
- return JSON.stringify(result, null, 2);
75
- }
76
- if (propId === "isEmpty()") {
77
- return result.length === 0;
78
- }
79
- if (propId === "average()" || propId === "mean()") {
80
- return result.length ? result.reduce((a, b) => a + b, 0) / result.length : 0;
81
- }
82
- if (propId === "sum()") {
83
- return result.reduce((a, b) => a + b, 0);
84
- }
85
- if (propId === "max()") {
86
- return result.length ? Math.max(...result) : 0;
87
- }
88
- if (propId === "min()") {
89
- return result.length ? Math.min(...result) : 0;
90
- }
91
- // array join
92
- const matchJoin = propId.match(/^join\(([,-\s]?)\)$/);
93
- if (matchJoin && Array.isArray(matchJoin)) {
94
- return result.join(matchJoin[1] ?? "");
95
- }
96
- }
97
- return undefined;
98
- };
99
- const propObjectFunction = (result, propId) => {
100
- if (isObject(result)) {
101
- if (propId === "keys()") {
102
- return Object.keys(result);
103
- }
104
- if (propId === "values()") {
105
- return Object.values(result);
106
- }
107
- if (propId === "toJSON()") {
108
- return JSON.stringify(result, null, 2);
109
- }
110
- }
111
- return undefined;
112
- };
113
- const propStringFunction = (result, propId) => {
114
- if (typeof result === "string") {
115
- if (propId === "codeBlock()") {
116
- const match = ("\n" + result).match(/\n```[a-zA-Z]*([\s\S]*?)\n```/);
117
- if (match) {
118
- return match[1];
119
- }
120
- }
121
- if (propId === "codeBlockOrRaw()") {
122
- const match = ("\n" + result).match(/\n```[a-zA-Z]*([\s\S]*?)\n```/);
123
- if (match) {
124
- return match[1];
125
- }
126
- return result;
127
- }
128
- if (propId === "jsonParse()") {
129
- return JSON.parse(result);
130
- }
131
- if (propId === "toNumber()") {
132
- const ret = Number(result);
133
- if (!isNaN(ret)) {
134
- return ret;
135
- }
136
- }
137
- if (propId === "trim()") {
138
- return result.trim();
139
- }
140
- if (propId === "toLowerCase()") {
141
- return result.toLowerCase();
142
- }
143
- if (propId === "toUpperCase()") {
144
- return result.toUpperCase();
145
- }
146
- const equalMatch = propId.match(/^equal\(([A-Za-z0-9!#$%&()*+,\-./:;<=>?@]+)\)/);
147
- if (equalMatch) {
148
- return result === equalMatch[1];
149
- }
150
- const sliceMatch = propId.match(/^slice\((-?\d+)(?:,\s*(-?\d+))?\)/);
151
- if (sliceMatch) {
152
- if (sliceMatch[2] !== undefined) {
153
- return result.slice(Number(sliceMatch[1]), Number(sliceMatch[2]));
154
- }
155
- if (sliceMatch[1] !== undefined) {
156
- return result.slice(Number(sliceMatch[1]));
157
- }
158
- GraphAILogger.warn("slice is not valid format: " + sliceMatch);
159
- }
160
- const splitMatch = propId.match(/^split\(([-_:;.,\s\n]+)\)$/);
161
- if (splitMatch) {
162
- return result.split(splitMatch[1]);
163
- }
164
- }
165
- return undefined;
166
- };
167
- const propNumberFunction = (result, propId) => {
168
- if (result !== undefined && Number.isFinite(result)) {
169
- if (propId === "toString()") {
170
- return String(result);
171
- }
172
- const regex = /^add\((-?\d+)\)$/;
173
- const match = propId.match(regex);
174
- if (match) {
175
- return Number(result) + Number(match[1]);
176
- }
177
- const equalMatch = propId.match(/^equal\(([A-Za-z0-9!#$%&()*+,\-/:;<=>?@]+)\)/);
178
- if (equalMatch) {
179
- return result === Number(equalMatch[1]);
180
- }
181
- }
182
- return undefined;
183
- };
184
- const propBooleanFunction = (result, propId) => {
185
- if (typeof result === "boolean") {
186
- if (propId === "not()") {
187
- return !result;
188
- }
189
- }
190
- return undefined;
191
- };
192
- const propUndefinrdFunction = (result, propId) => {
193
- if (result === undefined) {
194
- const equalMatch = propId.match(/^default\(([A-Za-z0-9!#$%&()*+,\-/:;<=>?@]+)\)/);
195
- if (equalMatch) {
196
- if (equalMatch[1].match(/^[0-9-]+$/)) {
197
- return Number(equalMatch[1]);
198
- }
199
- return equalMatch[1];
200
- }
201
- }
202
- return undefined;
203
- };
204
- // TODO if (result === undefined) {default()}
205
- const propFunctions = [propArrayFunction, propObjectFunction, propStringFunction, propNumberFunction, propBooleanFunction, propUndefinrdFunction];
206
- const utilsFunctions = (input, nodes) => {
207
- if (input === "@now" || input === "@now_ms") {
208
- return Date.now();
209
- }
210
- if (input === "@now_s") {
211
- return Math.floor(Date.now() / 1000);
212
- }
213
- if (input === "@loop") {
214
- return nodes[loopCounterKey].result;
215
- }
216
- // If a placeholder does not match any key, replace it with an empty string.
217
- GraphAILogger.warn("not match template utility function: ${" + input + "}");
218
- return "";
219
- };
220
-
221
- const sleep = async (milliseconds) => {
222
- return await new Promise((resolve) => setTimeout(resolve, milliseconds));
223
- };
224
- const parseNodeName = (inputNodeId, isSelfNode = false, nodes) => {
225
- if (isSelfNode) {
226
- if (typeof inputNodeId === "string" && inputNodeId[0] === ".") {
227
- const parts = inputNodeId.split(".");
228
- return { nodeId: "self", propIds: parts.slice(1) };
229
- }
230
- return { value: inputNodeId };
231
- }
232
- if (typeof inputNodeId === "string") {
233
- const regex = /^:(.*)$/;
234
- const match = inputNodeId.match(regex);
235
- if (match) {
236
- const parts = match[1].split(/(?<!\()\.(?!\))/);
237
- if (parts.length == 1) {
238
- return { nodeId: parts[0] };
239
- }
240
- return { nodeId: parts[0], propIds: parts.slice(1) };
241
- }
242
- const regexUtil = /^@(.*)$/;
243
- const matchUtil = inputNodeId.match(regexUtil);
244
- // Only when just called from resultsOfInner
245
- if (nodes && matchUtil) {
246
- return { value: utilsFunctions(inputNodeId, nodes) };
247
- }
248
- }
249
- return { value: inputNodeId }; // non-string literal
250
- };
251
- function assert(condition, message, isWarn = false) {
252
- if (!condition) {
253
- if (!isWarn) {
254
- throw new Error(message);
255
- }
256
- GraphAILogger.warn("warn: " + message);
257
- }
258
- }
259
- const isObject = (x) => {
260
- return x !== null && typeof x === "object";
261
- };
262
- const isNull = (data) => {
263
- return data === null || data === undefined;
264
- };
265
- const strIntentionalError = "Intentional Error for Debugging";
266
- const defaultAgentInfo = {
267
- name: "defaultAgentInfo",
268
- samples: [
269
- {
270
- inputs: [],
271
- params: {},
272
- result: {},
273
- },
274
- ],
275
- description: "",
276
- category: [],
277
- author: "",
278
- repository: "",
279
- license: "",
280
- };
281
- const agentInfoWrapper = (agent) => {
282
- return {
283
- agent,
284
- mock: agent,
285
- ...defaultAgentInfo,
286
- };
287
- };
288
- const objectToKeyArray = (innerData) => {
289
- const ret = [];
290
- Object.keys(innerData).forEach((key) => {
291
- ret.push([key]);
292
- if (Object.keys(innerData[key]).length > 0) {
293
- objectToKeyArray(innerData[key]).forEach((tmp) => {
294
- ret.push([key, ...tmp]);
295
- });
296
- }
297
- });
298
- return ret;
299
- };
300
- const debugResultKey = (agentId, result) => {
301
- return objectToKeyArray({ [agentId]: debugResultKeyInner(result) }).map((objectKeys) => {
302
- return ":" + objectKeys.join(".");
303
- });
304
- };
305
- const debugResultKeyInner = (result) => {
306
- if (result === null || result === undefined) {
307
- return {};
308
- }
309
- if (typeof result === "string") {
310
- return {};
311
- }
312
- if (Array.isArray(result)) {
313
- return Array.from(result.keys()).reduce((tmp, index) => {
314
- tmp["$" + String(index)] = debugResultKeyInner(result[index]);
315
- return tmp;
316
- }, {});
317
- }
318
- return Object.keys(result).reduce((tmp, key) => {
319
- tmp[key] = debugResultKeyInner(result[key]);
320
- return tmp;
321
- }, {});
322
- };
323
- const isLogicallyTrue = (value) => {
324
- // Notice that empty aray is not true under GraphAI
325
- if (Array.isArray(value) ? value.length === 0 : !value) {
326
- return false;
327
- }
328
- return true;
329
- };
330
- const defaultTestContext = {
331
- debugInfo: {
332
- nodeId: "test",
333
- retry: 0,
334
- verbose: true,
335
- state: NodeState.Executing,
336
- subGraphs: new Map(),
337
- },
338
- params: {},
339
- filterParams: {},
340
- agents: {},
341
- log: [],
342
- };
343
- const isNamedInputs = (namedInputs) => {
344
- return isObject(namedInputs) && !Array.isArray(namedInputs) && Object.keys(namedInputs || {}).length > 0;
345
- };
346
- const isComputedNodeData = (node) => {
347
- return "agent" in node;
348
- };
349
- const isStaticNodeData = (node) => {
350
- return !("agent" in node);
351
- };
352
- const loopCounterKey = "__loopIndex";
353
-
354
- // for dataSource
355
- const inputs2dataSources = (inputs) => {
356
- if (Array.isArray(inputs)) {
357
- return inputs.map((inp) => inputs2dataSources(inp)).flat();
358
- }
359
- if (isObject(inputs)) {
360
- return Object.values(inputs)
361
- .map((input) => inputs2dataSources(input))
362
- .flat();
363
- }
364
- if (typeof inputs === "string") {
365
- const templateMatch = [...inputs.matchAll(/\${(:[^}]+)}/g)].map((m) => m[1]);
366
- if (templateMatch.length > 0) {
367
- return inputs2dataSources(templateMatch);
368
- }
369
- }
370
- return parseNodeName(inputs);
371
- };
372
- // TODO: Maybe it's a remnant of old array inputs. Check and delete.
373
- const dataSourceNodeIds = (sources) => {
374
- if (!Array.isArray(sources)) {
375
- throw new Error("sources must be array!! maybe inputs is invalid");
376
- }
377
- return sources.filter((source) => source.nodeId).map((source) => source.nodeId);
378
- };
379
-
380
- class TransactionLog {
381
- constructor(nodeId, mapIndex) {
382
- this.nodeId = nodeId;
383
- this.state = NodeState.Waiting;
384
- this.mapIndex = mapIndex;
385
- }
386
- initForComputedNode(node, graph) {
387
- this.agentId = node.getAgentId();
388
- this.params = node.params;
389
- graph.appendLog(this);
390
- }
391
- onInjected(node, graph, injectFrom) {
392
- const isUpdating = "endTime" in this;
393
- this.result = node.result;
394
- this.state = node.state;
395
- this.endTime = Date.now();
396
- this.injectFrom = injectFrom;
397
- graph.setLoopLog(this);
398
- // console.log(this)
399
- if (isUpdating) {
400
- graph.updateLog(this);
401
- }
402
- else {
403
- graph.appendLog(this);
404
- }
405
- }
406
- onComplete(node, graph, localLog) {
407
- this.result = node.result;
408
- this.resultKeys = debugResultKey(this.agentId || "", node.result);
409
- this.state = node.state;
410
- this.endTime = Date.now();
411
- graph.setLoopLog(this);
412
- if (localLog.length > 0) {
413
- this.log = localLog;
414
- }
415
- graph.updateLog(this);
416
- }
417
- beforeExecute(node, graph, transactionId, inputs) {
418
- this.state = node.state;
419
- this.retryCount = node.retryCount > 0 ? node.retryCount : undefined;
420
- this.startTime = transactionId;
421
- this.inputs = dataSourceNodeIds(node.dataSources);
422
- this.inputsData = inputs.length > 0 ? inputs : undefined;
423
- graph.setLoopLog(this);
424
- graph.appendLog(this);
425
- }
426
- beforeAddTask(node, graph) {
427
- this.state = node.state;
428
- graph.setLoopLog(this);
429
- graph.appendLog(this);
430
- }
431
- onError(node, graph, errorMessage) {
432
- this.state = node.state;
433
- this.errorMessage = errorMessage;
434
- this.endTime = Date.now();
435
- graph.setLoopLog(this);
436
- graph.updateLog(this);
437
- }
438
- onSkipped(node, graph) {
439
- this.state = node.state;
440
- graph.setLoopLog(this);
441
- graph.updateLog(this);
442
- }
443
- }
444
-
445
- const getNestedData = (result, propId, propFunctions) => {
446
- const match = propId.match(propFunctionRegex);
447
- if (match) {
448
- for (const propFunction of propFunctions) {
449
- const ret = propFunction(result, propId);
450
- if (!isNull(ret)) {
451
- return ret;
452
- }
453
- }
454
- }
455
- // for array.
456
- if (Array.isArray(result)) {
457
- // $0, $1. array value.
458
- const regex = /^\$(\d+)$/;
459
- const match = propId.match(regex);
460
- if (match) {
461
- const index = parseInt(match[1], 10);
462
- return result[index];
463
- }
464
- if (propId === "$last") {
465
- return result[result.length - 1];
466
- }
467
- }
468
- else if (isObject(result)) {
469
- if (propId in result) {
470
- return result[propId];
471
- }
472
- }
473
- return undefined;
474
- };
475
- const innerGetDataFromSource = (result, propIds, propFunctions) => {
476
- if (propIds && propIds.length > 0) {
477
- const propId = propIds[0];
478
- const ret = getNestedData(result, propId, propFunctions);
479
- if (ret === undefined) {
480
- GraphAILogger.error(`prop: ${propIds.join(".")} is not hit`);
481
- }
482
- if (propIds.length > 1) {
483
- return innerGetDataFromSource(ret, propIds.slice(1), propFunctions);
484
- }
485
- return ret;
486
- }
487
- return result;
488
- };
489
- const getDataFromSource = (result, source, propFunctions = []) => {
490
- if (!source.nodeId) {
491
- return source.value;
492
- }
493
- return innerGetDataFromSource(result, source.propIds, propFunctions);
494
- };
495
-
496
- const replaceTemplatePlaceholders = (input, templateMatch, nodes, propFunctions, isSelfNode) => {
497
- // GOD format ${:node.prop1.prop2}
498
- const godResults = resultsOfInner(templateMatch.filter((text) => text.startsWith(":")), nodes, propFunctions, isSelfNode);
499
- // utilsFunctions ${@now}
500
- const utilsFuncResult = templateMatch
501
- .filter((text) => text.startsWith("@"))
502
- .reduce((tmp, key) => {
503
- tmp[key] = utilsFunctions(key, nodes);
504
- return tmp;
505
- }, {});
506
- return Array.from(templateMatch.keys()).reduce((tmp, key) => {
507
- if (templateMatch[key].startsWith(":")) {
508
- return tmp.replaceAll("${" + templateMatch[key] + "}", godResults[key]);
509
- }
510
- return tmp.replaceAll("${" + templateMatch[key] + "}", utilsFuncResult[templateMatch[key]]);
511
- }, input);
512
- };
513
- const resultsOfInner = (input, nodes, propFunctions, isSelfNode = false) => {
514
- if (Array.isArray(input)) {
515
- return input.map((inp) => resultsOfInner(inp, nodes, propFunctions, isSelfNode));
516
- }
517
- if (isNamedInputs(input)) {
518
- return resultsOf(input, nodes, propFunctions, isSelfNode);
519
- }
520
- if (typeof input === "string") {
521
- const templateMatch = [...input.matchAll(/\${([:@][^}]+)}/g)].map((m) => m[1]);
522
- if (templateMatch.length > 0) {
523
- return replaceTemplatePlaceholders(input, templateMatch, nodes, propFunctions, isSelfNode);
524
- }
525
- }
526
- // :node.prod
527
- return resultOf(parseNodeName(input, isSelfNode, nodes), nodes, propFunctions);
528
- };
529
- const resultsOf = (inputs, nodes, propFunctions, isSelfNode = false) => {
530
- return Object.keys(inputs).reduce((tmp, key) => {
531
- const input = inputs[key];
532
- tmp[key] = isNamedInputs(input) ? resultsOf(input, nodes, propFunctions, isSelfNode) : resultsOfInner(input, nodes, propFunctions, isSelfNode);
533
- return tmp;
534
- }, {});
535
- };
536
- const resultOf = (source, nodes, propFunctions) => {
537
- const { result } = source.nodeId ? nodes[source.nodeId] : { result: undefined };
538
- return getDataFromSource(result, source, propFunctions);
539
- };
540
- // clean up object for anyInput
541
- const cleanResultInner = (results) => {
542
- if (Array.isArray(results)) {
543
- return results.map((result) => cleanResultInner(result)).filter((result) => !isNull(result));
544
- }
545
- if (isObject(results)) {
546
- return Object.keys(results).reduce((tmp, key) => {
547
- const value = cleanResultInner(results[key]);
548
- if (!isNull(value)) {
549
- tmp[key] = value;
550
- }
551
- return tmp;
552
- }, {});
553
- }
554
- return results;
555
- };
556
- const cleanResult = (results) => {
557
- return Object.keys(results).reduce((tmp, key) => {
558
- const value = cleanResultInner(results[key]);
559
- if (!isNull(value)) {
560
- tmp[key] = value;
561
- }
562
- return tmp;
563
- }, {});
564
- };
565
-
566
- class Node {
567
- constructor(nodeId, graph) {
568
- this.waitlist = new Set(); // List of nodes which need data from this node.
569
- this.state = NodeState.Waiting;
570
- this.result = undefined;
571
- this.nodeId = nodeId;
572
- this.graph = graph;
573
- this.log = new TransactionLog(nodeId, this.graph.mapIndex);
574
- this.console = {};
575
- }
576
- asString() {
577
- return `${this.nodeId}: ${this.state} ${[...this.waitlist]}`;
578
- }
579
- // This method is called either as the result of computation (computed node) or
580
- // injection (static node).
581
- onSetResult() {
582
- this.waitlist.forEach((waitingNodeId) => {
583
- const waitingNode = this.graph.nodes[waitingNodeId];
584
- if (waitingNode.isComputedNode) {
585
- waitingNode.removePending(this.nodeId);
586
- this.graph.pushQueueIfReadyAndRunning(waitingNode);
587
- }
588
- });
589
- }
590
- afterConsoleLog(result) {
591
- if (this.console === false) {
592
- return;
593
- }
594
- else if (this.console === true || this.console.after === true) {
595
- GraphAILogger.log(typeof result === "string" ? result : JSON.stringify(result, null, 2));
596
- }
597
- else if (this.console.after) {
598
- if (isObject(this.console.after)) {
599
- GraphAILogger.log(JSON.stringify(resultsOf(this.console.after, { self: { result } }, this.graph.propFunctions, true), null, 2));
600
- }
601
- else {
602
- GraphAILogger.log(this.console.after);
603
- }
604
- }
605
- }
606
- }
607
- class ComputedNode extends Node {
608
- constructor(graphId, nodeId, data, graph) {
609
- super(nodeId, graph);
610
- this.retryCount = 0;
611
- this.dataSources = []; // no longer needed. This is for transaction log.
612
- this.isSkip = false;
613
- this.isStaticNode = false;
614
- this.isComputedNode = true;
615
- this.graphId = graphId;
616
- this.params = data.params ?? {};
617
- this.console = data.console ?? {};
618
- this.filterParams = data.filterParams ?? {};
619
- this.passThrough = data.passThrough;
620
- this.retryLimit = data.retry ?? graph.retryLimit ?? 0;
621
- this.repeatUntil = data.repeatUntil;
622
- this.timeout = data.timeout;
623
- this.isResult = data.isResult ?? false;
624
- this.priority = data.priority ?? 0;
625
- assert(["function", "string"].includes(typeof data.agent), "agent must be either string or function");
626
- if (typeof data.agent === "string") {
627
- this.agentId = data.agent;
628
- }
629
- else {
630
- const agent = data.agent;
631
- this.agentFunction = async ({ namedInputs, params }) => agent(namedInputs, params);
632
- }
633
- this.anyInput = data.anyInput ?? false;
634
- this.inputs = data.inputs;
635
- this.output = data.output;
636
- this.dataSources = [
637
- ...(data.inputs ? inputs2dataSources(data.inputs).flat(10) : []),
638
- // ...(data.params ? inputs2dataSources(data.params).flat(10) : []),
639
- ...(this.agentId ? [parseNodeName(this.agentId)] : []),
640
- ...(data.passThrough ? inputs2dataSources(data.passThrough).flat(10) : []),
641
- ];
642
- if (data.inputs && Array.isArray(data.inputs)) {
643
- throw new Error(`array inputs have been deprecated. nodeId: ${nodeId}: see https://github.com/receptron/graphai/blob/main/docs/NamedInputs.md`);
644
- }
645
- this.pendings = new Set(dataSourceNodeIds(this.dataSources));
646
- if (data.graph) {
647
- this.nestedGraph = typeof data.graph === "string" ? this.addPendingNode(data.graph) : data.graph;
648
- }
649
- if (data.graphLoader && graph.graphLoader) {
650
- this.nestedGraph = graph.graphLoader(data.graphLoader);
651
- }
652
- if (data.if) {
653
- this.ifSource = this.addPendingNode(data.if);
654
- }
655
- if (data.unless) {
656
- this.unlessSource = this.addPendingNode(data.unless);
657
- }
658
- if (data.defaultValue) {
659
- this.defaultValue = data.defaultValue;
660
- }
661
- this.isSkip = false;
662
- this.log.initForComputedNode(this, graph);
663
- }
664
- getAgentId() {
665
- return this.agentId ?? "__custom__function"; // only for display purpose in the log.
666
- }
667
- getConfig(hasGraphData, agentId) {
668
- if (agentId) {
669
- if (hasGraphData) {
670
- return this.graph.config;
671
- }
672
- const config = this.graph.config ?? {};
673
- return {
674
- ...(config["global"] ?? {}),
675
- ...(config[agentId] ?? {}),
676
- };
677
- }
678
- return {};
679
- }
680
- addPendingNode(nodeId) {
681
- const source = parseNodeName(nodeId);
682
- assert(!!source.nodeId, `Invalid data source ${nodeId}`);
683
- this.pendings.add(source.nodeId);
684
- return source;
685
- }
686
- updateState(state) {
687
- this.state = state;
688
- if (this.debugInfo) {
689
- this.debugInfo.state = state;
690
- }
691
- }
692
- resetPending() {
693
- this.pendings.clear();
694
- if (this.state === NodeState.Executing) {
695
- this.updateState(NodeState.Abort);
696
- }
697
- if (this.debugInfo && this.debugInfo.subGraphs) {
698
- this.debugInfo.subGraphs.forEach((graph) => graph.abort(true));
699
- }
700
- }
701
- isReadyNode() {
702
- if (this.state !== NodeState.Waiting || this.pendings.size !== 0) {
703
- return false;
704
- }
705
- this.isSkip = !!((this.ifSource && !isLogicallyTrue(this.graph.resultOf(this.ifSource))) ||
706
- (this.unlessSource && isLogicallyTrue(this.graph.resultOf(this.unlessSource))));
707
- if (this.isSkip && this.defaultValue === undefined) {
708
- this.updateState(NodeState.Skipped);
709
- this.log.onSkipped(this, this.graph);
710
- return false;
711
- }
712
- return true;
713
- }
714
- // This private method (only called while executing execute()) performs
715
- // the "retry" if specified. The transaction log must be updated before
716
- // callling this method.
717
- retry(state, error) {
718
- this.updateState(state); // this.execute() will update to NodeState.Executing
719
- this.log.onError(this, this.graph, error.message);
720
- if (this.retryCount < this.retryLimit) {
721
- this.retryCount++;
722
- this.execute();
723
- }
724
- else {
725
- this.result = undefined;
726
- this.error = error;
727
- this.transactionId = undefined; // This is necessary for timeout case
728
- this.graph.onExecutionComplete(this);
729
- }
730
- }
731
- checkDataAvailability() {
732
- return Object.values(this.graph.resultsOf(this.inputs))
733
- .flat()
734
- .some((result) => result !== undefined);
735
- }
736
- // This method is called right before the Graph add this node to the task manager.
737
- beforeAddTask() {
738
- this.updateState(NodeState.Queued);
739
- this.log.beforeAddTask(this, this.graph);
740
- }
741
- // This method is called when the data became available on one of nodes,
742
- // which this node needs data from.
743
- removePending(nodeId) {
744
- if (this.anyInput) {
745
- if (this.checkDataAvailability()) {
746
- this.pendings.clear();
747
- }
748
- }
749
- else {
750
- this.pendings.delete(nodeId);
751
- }
752
- }
753
- isCurrentTransaction(transactionId) {
754
- return this.transactionId === transactionId;
755
- }
756
- // This private method (called only fro execute) checks if the callback from
757
- // the timer came before the completion of agent function call, record it
758
- // and attempt to retry (if specified).
759
- executeTimeout(transactionId) {
760
- if (this.state === NodeState.Executing && this.isCurrentTransaction(transactionId)) {
761
- GraphAILogger.warn(`-- timeout ${this.timeout} with ${this.nodeId}`);
762
- this.retry(NodeState.TimedOut, Error("Timeout"));
763
- }
764
- }
765
- // Check if we need to apply this filter to this node or not.
766
- shouldApplyAgentFilter(agentFilter, agentId) {
767
- if (agentFilter.agentIds && Array.isArray(agentFilter.agentIds) && agentFilter.agentIds.length > 0) {
768
- if (agentId && agentFilter.agentIds.includes(agentId)) {
769
- return true;
770
- }
771
- }
772
- if (agentFilter.nodeIds && Array.isArray(agentFilter.nodeIds) && agentFilter.nodeIds.length > 0) {
773
- if (agentFilter.nodeIds.includes(this.nodeId)) {
774
- return true;
775
- }
776
- }
777
- return !agentFilter.agentIds && !agentFilter.nodeIds;
778
- }
779
- agentFilterHandler(context, agentFunction, agentId) {
780
- let index = 0;
781
- const next = (innerContext) => {
782
- const agentFilter = this.graph.agentFilters[index++];
783
- if (agentFilter) {
784
- if (this.shouldApplyAgentFilter(agentFilter, agentId)) {
785
- if (agentFilter.filterParams) {
786
- innerContext.filterParams = { ...agentFilter.filterParams, ...innerContext.filterParams };
787
- }
788
- return agentFilter.agent(innerContext, next);
789
- }
790
- return next(innerContext);
791
- }
792
- return agentFunction(innerContext);
793
- };
794
- return next(context);
795
- }
796
- // This method is called when this computed node became ready to run.
797
- // It asynchronously calls the associated with agent function and set the result,
798
- // then it removes itself from the "running node" list of the graph.
799
- // Notice that setting the result of this node may make other nodes ready to run.
800
- async execute() {
801
- if (this.isSkip) {
802
- this.afterExecute(this.defaultValue, []);
803
- return;
804
- }
805
- const previousResults = this.graph.resultsOf(this.inputs, this.anyInput);
806
- const agentId = this.agentId ? this.graph.resultOf(parseNodeName(this.agentId)) : this.agentId;
807
- if (typeof agentId === "function") {
808
- this.agentFunction = agentId;
809
- }
810
- const hasNestedGraph = Boolean(this.nestedGraph) || Boolean(agentId && this.graph.getAgentFunctionInfo(agentId).hasGraphData);
811
- const config = this.getConfig(hasNestedGraph, agentId);
812
- const transactionId = Date.now();
813
- this.prepareExecute(transactionId, Object.values(previousResults));
814
- if (this.timeout && this.timeout > 0) {
815
- setTimeout(() => {
816
- this.executeTimeout(transactionId);
817
- }, this.timeout);
818
- }
819
- try {
820
- const agentFunction = this.agentFunction ?? this.graph.getAgentFunctionInfo(agentId).agent;
821
- const localLog = [];
822
- const context = this.getContext(previousResults, localLog, agentId, config);
823
- // NOTE: We use the existence of graph object in the agent-specific params to determine
824
- // if this is a nested agent or not.
825
- if (hasNestedGraph) {
826
- this.graph.taskManager.prepareForNesting();
827
- context.forNestedGraph = {
828
- graphData: this.nestedGraph
829
- ? "nodes" in this.nestedGraph
830
- ? this.nestedGraph
831
- : this.graph.resultOf(this.nestedGraph) // HACK: compiler work-around
832
- : { version: 0, nodes: {} },
833
- agents: this.graph.agentFunctionInfoDictionary,
834
- graphOptions: {
835
- agentFilters: this.graph.agentFilters,
836
- taskManager: this.graph.taskManager,
837
- bypassAgentIds: this.graph.bypassAgentIds,
838
- config,
839
- graphLoader: this.graph.graphLoader,
840
- },
841
- onLogCallback: this.graph.onLogCallback,
842
- callbacks: this.graph.callbacks,
843
- };
844
- }
845
- this.beforeConsoleLog(context);
846
- const result = await this.agentFilterHandler(context, agentFunction, agentId);
847
- this.afterConsoleLog(result);
848
- if (hasNestedGraph) {
849
- this.graph.taskManager.restoreAfterNesting();
850
- }
851
- if (!this.isCurrentTransaction(transactionId)) {
852
- // This condition happens when the agent function returns
853
- // after the timeout (either retried or not).
854
- GraphAILogger.log(`-- transactionId mismatch with ${this.nodeId} (probably timeout)`);
855
- return;
856
- }
857
- if (this.repeatUntil?.exists) {
858
- const dummyResult = { self: { result: this.getResult(result) } };
859
- const repeatResult = resultsOf({ data: this.repeatUntil?.exists }, dummyResult, [], true);
860
- if (isNull(repeatResult?.data)) {
861
- this.retry(NodeState.Failed, Error("Repeat Until"));
862
- return;
863
- }
864
- }
865
- // after process
866
- this.afterExecute(result, localLog);
867
- }
868
- catch (error) {
869
- this.errorProcess(error, transactionId, previousResults);
870
- }
871
- }
872
- afterExecute(result, localLog) {
873
- if (this.state == NodeState.Abort) {
874
- return;
875
- }
876
- this.updateState(NodeState.Completed);
877
- this.result = this.getResult(result);
878
- if (this.output) {
879
- this.result = resultsOf(this.output, { self: this }, this.graph.propFunctions, true);
880
- if (this.passThrough) {
881
- this.result = { ...this.result, ...this.graph.resultsOf(this.passThrough) };
882
- }
883
- }
884
- this.log.onComplete(this, this.graph, localLog);
885
- this.onSetResult();
886
- this.graph.onExecutionComplete(this);
887
- }
888
- // This private method (called only by execute()) prepares the ComputedNode object
889
- // for execution, and create a new transaction to record it.
890
- prepareExecute(transactionId, inputs) {
891
- this.updateState(NodeState.Executing);
892
- this.log.beforeExecute(this, this.graph, transactionId, inputs);
893
- this.transactionId = transactionId;
894
- }
895
- // This private method (called only by execute) processes an error received from
896
- // the agent function. It records the error in the transaction log and handles
897
- // the retry if specified.
898
- errorProcess(error, transactionId, namedInputs) {
899
- if (error instanceof Error && error.message !== strIntentionalError) {
900
- GraphAILogger.error(`<-- NodeId: ${this.nodeId}, Agent: ${this.agentId}`);
901
- GraphAILogger.error({ namedInputs });
902
- GraphAILogger.error(error);
903
- GraphAILogger.error("-->");
904
- }
905
- if (!this.isCurrentTransaction(transactionId)) {
906
- GraphAILogger.warn(`-- transactionId mismatch with ${this.nodeId} (not timeout)`);
907
- return;
908
- }
909
- if (error instanceof Error) {
910
- this.retry(NodeState.Failed, error);
911
- }
912
- else {
913
- GraphAILogger.error(`-- NodeId: ${this.nodeId}: Unknown error was caught`);
914
- this.retry(NodeState.Failed, Error("Unknown"));
915
- }
916
- }
917
- getContext(previousResults, localLog, agentId, config) {
918
- // Pass debugInfo by reference, and the state of this node will be received by agent/agentFilter.
919
- // From graphAgent(nested, map), set the instance of graphai, and use abort on the child graphai.
920
- this.debugInfo = this.getDebugInfo(agentId);
921
- const params = {
922
- ...(this.params ?? {}),
923
- ...(isNamedInputs(previousResults?.params) ? previousResults?.params : {}),
924
- };
925
- const context = {
926
- //params: this.graph.resultsOf(this.params),
927
- params,
928
- namedInputs: previousResults,
929
- inputSchema: this.agentFunction ? undefined : this.graph.getAgentFunctionInfo(agentId)?.inputs,
930
- debugInfo: this.debugInfo,
931
- cacheType: this.agentFunction ? undefined : this.graph.getAgentFunctionInfo(agentId)?.cacheType,
932
- filterParams: this.filterParams,
933
- config,
934
- log: localLog,
935
- };
936
- return context;
937
- }
938
- getResult(result) {
939
- if (result && this.passThrough) {
940
- if (isObject(result) && !Array.isArray(result)) {
941
- return { ...result, ...this.graph.resultsOf(this.passThrough) };
942
- }
943
- else if (Array.isArray(result)) {
944
- return result.map((r) => (isObject(r) && !Array.isArray(r) ? { ...r, ...this.graph.resultsOf(this.passThrough) } : r));
945
- }
946
- }
947
- return result;
948
- }
949
- getDebugInfo(agentId) {
950
- return {
951
- nodeId: this.nodeId,
952
- agentId,
953
- retry: this.retryCount,
954
- state: this.state,
955
- subGraphs: new Map(),
956
- verbose: this.graph.verbose,
957
- version: this.graph.version,
958
- isResult: this.isResult,
959
- };
960
- }
961
- beforeConsoleLog(context) {
962
- if (this.console === false) {
963
- return;
964
- }
965
- else if (this.console === true || this.console.before === true) {
966
- GraphAILogger.log(JSON.stringify(context.namedInputs, null, 2));
967
- }
968
- else if (this.console.before) {
969
- GraphAILogger.log(this.console.before);
970
- }
971
- }
972
- }
973
- class StaticNode extends Node {
974
- constructor(nodeId, data, graph) {
975
- super(nodeId, graph);
976
- this.isStaticNode = true;
977
- this.isComputedNode = false;
978
- this.value = data.value;
979
- this.update = data.update ? parseNodeName(data.update) : undefined;
980
- this.isResult = data.isResult ?? false;
981
- this.console = data.console ?? {};
982
- }
983
- updateValue(value, injectFrom) {
984
- this.value = value;
985
- this.log.onInjected(this, this.graph, injectFrom);
986
- }
987
- setResultValue(injectFrom) {
988
- this.state = NodeState.Injected;
989
- this.result = this.value;
990
- this.log.onInjected(this, this.graph, injectFrom);
991
- this.onSetResult();
992
- }
993
- consoleLog() {
994
- this.afterConsoleLog(this.result);
995
- }
996
- }
997
-
998
- const graphDataAttributeKeys = ["nodes", "concurrency", "agentId", "loop", "verbose", "version", "metadata"];
999
- const computedNodeAttributeKeys = [
1000
- "inputs",
1001
- "output",
1002
- "anyInput",
1003
- "params",
1004
- "retry",
1005
- "repeatUntil",
1006
- "timeout",
1007
- "agent",
1008
- "graph",
1009
- "graphLoader",
1010
- "isResult",
1011
- "priority",
1012
- "if",
1013
- "unless",
1014
- "defaultValue",
1015
- "filterParams",
1016
- "console",
1017
- "passThrough",
1018
- ];
1019
- const staticNodeAttributeKeys = ["value", "update", "isResult", "console"];
1020
- class ValidationError extends Error {
1021
- constructor(message) {
1022
- super(`\x1b[41m${message}\x1b[0m`); // Pass the message to the base Error class
1023
- // Set the prototype explicitly to ensure correct prototype chain
1024
- Object.setPrototypeOf(this, ValidationError.prototype);
1025
- }
1026
- }
1027
-
1028
- const graphNodesValidator = (data) => {
1029
- if (data.nodes === undefined) {
1030
- throw new ValidationError("Invalid Graph Data: no nodes");
1031
- }
1032
- if (typeof data.nodes !== "object") {
1033
- throw new ValidationError("Invalid Graph Data: invalid nodes");
1034
- }
1035
- if (Array.isArray(data.nodes)) {
1036
- throw new ValidationError("Invalid Graph Data: nodes must be object");
1037
- }
1038
- if (Object.keys(data.nodes).length === 0) {
1039
- throw new ValidationError("Invalid Graph Data: nodes is empty");
1040
- }
1041
- Object.keys(data).forEach((key) => {
1042
- if (!graphDataAttributeKeys.includes(key)) {
1043
- throw new ValidationError("Graph Data does not allow " + key);
1044
- }
1045
- });
1046
- };
1047
- const graphDataValidator = (data) => {
1048
- if (data.loop) {
1049
- if (data.loop.count === undefined && data.loop.while === undefined) {
1050
- throw new ValidationError("Loop: Either count or while is required in loop");
1051
- }
1052
- if (data.loop.count !== undefined && data.loop.while !== undefined) {
1053
- throw new ValidationError("Loop: Both count and while cannot be set");
1054
- }
1055
- }
1056
- if (data.concurrency !== undefined) {
1057
- if (!Number.isInteger(data.concurrency)) {
1058
- throw new ValidationError("Concurrency must be an integer");
1059
- }
1060
- if (data.concurrency < 1) {
1061
- throw new ValidationError("Concurrency must be a positive integer");
1062
- }
1063
- }
1064
- };
1065
-
1066
- const nodeValidator = (nodeData) => {
1067
- if (nodeData.agent && nodeData.value) {
1068
- throw new ValidationError("Cannot set both agent and value");
1069
- }
1070
- // if (!("agent" in nodeData) && !("value" in nodeData)) {
1071
- // throw new ValidationError("Either agent or value is required");
1072
- // }
1073
- return true;
1074
- };
1075
-
1076
- const staticNodeValidator = (nodeData) => {
1077
- Object.keys(nodeData).forEach((key) => {
1078
- if (!staticNodeAttributeKeys.includes(key)) {
1079
- throw new ValidationError("Static node does not allow " + key);
1080
- }
1081
- });
1082
- return true;
1083
- };
1084
-
1085
- const computedNodeValidator = (nodeData) => {
1086
- Object.keys(nodeData).forEach((key) => {
1087
- if (!computedNodeAttributeKeys.includes(key)) {
1088
- throw new ValidationError("Computed node does not allow " + key);
1089
- }
1090
- });
1091
- return true;
1092
- };
1093
-
1094
- const relationValidator = (graphData, staticNodeIds, computedNodeIds) => {
1095
- const nodeIds = new Set(Object.keys(graphData.nodes));
1096
- const pendings = {};
1097
- const waitlist = {};
1098
- // validate input relation and set pendings and wait list
1099
- computedNodeIds.forEach((computedNodeId) => {
1100
- const nodeData = graphData.nodes[computedNodeId];
1101
- pendings[computedNodeId] = new Set();
1102
- const dataSourceValidator = (sourceType, sourceNodeIds) => {
1103
- sourceNodeIds.forEach((sourceNodeId) => {
1104
- if (sourceNodeId) {
1105
- if (!nodeIds.has(sourceNodeId)) {
1106
- throw new ValidationError(`${sourceType} not match: NodeId ${computedNodeId}, Inputs: ${sourceNodeId}`);
1107
- }
1108
- waitlist[sourceNodeId] === undefined && (waitlist[sourceNodeId] = new Set());
1109
- pendings[computedNodeId].add(sourceNodeId);
1110
- waitlist[sourceNodeId].add(computedNodeId);
1111
- }
1112
- });
1113
- };
1114
- if (nodeData && isComputedNodeData(nodeData)) {
1115
- if (nodeData.inputs) {
1116
- const sourceNodeIds = dataSourceNodeIds(inputs2dataSources(nodeData.inputs));
1117
- dataSourceValidator("Inputs", sourceNodeIds);
1118
- }
1119
- /*
1120
- if (nodeData.params) {
1121
- const sourceNodeIds = dataSourceNodeIds(inputs2dataSources(nodeData.params));
1122
- dataSourceValidator("Params", sourceNodeIds);
1123
- }
1124
- */
1125
- if (nodeData.if) {
1126
- const sourceNodeIds = dataSourceNodeIds(inputs2dataSources({ if: nodeData.if }));
1127
- dataSourceValidator("If", sourceNodeIds);
1128
- }
1129
- if (nodeData.unless) {
1130
- const sourceNodeIds = dataSourceNodeIds(inputs2dataSources({ unless: nodeData.unless }));
1131
- dataSourceValidator("Unless", sourceNodeIds);
1132
- }
1133
- if (nodeData.graph && typeof nodeData?.graph === "string") {
1134
- const sourceNodeIds = dataSourceNodeIds(inputs2dataSources({ graph: nodeData.graph }));
1135
- dataSourceValidator("Graph", sourceNodeIds);
1136
- }
1137
- if (typeof nodeData.agent === "string" && nodeData.agent[0] === ":") {
1138
- const sourceNodeIds = dataSourceNodeIds(inputs2dataSources({ agent: nodeData.agent }));
1139
- dataSourceValidator("Agent", sourceNodeIds);
1140
- }
1141
- }
1142
- });
1143
- // TODO. validate update
1144
- staticNodeIds.forEach((staticNodeId) => {
1145
- const nodeData = graphData.nodes[staticNodeId];
1146
- if (isStaticNodeData(nodeData) && nodeData.update) {
1147
- const update = nodeData.update;
1148
- const updateNodeId = parseNodeName(update).nodeId;
1149
- if (!updateNodeId) {
1150
- throw new ValidationError("Update it a literal");
1151
- }
1152
- if (!nodeIds.has(updateNodeId)) {
1153
- throw new ValidationError(`Update not match: NodeId ${staticNodeId}, update: ${update}`);
1154
- }
1155
- }
1156
- });
1157
- const cycle = (possibles) => {
1158
- possibles.forEach((possobleNodeId) => {
1159
- (waitlist[possobleNodeId] || []).forEach((waitingNodeId) => {
1160
- pendings[waitingNodeId].delete(possobleNodeId);
1161
- });
1162
- });
1163
- const running = [];
1164
- Object.keys(pendings).forEach((pendingNodeId) => {
1165
- if (pendings[pendingNodeId].size === 0) {
1166
- running.push(pendingNodeId);
1167
- delete pendings[pendingNodeId];
1168
- }
1169
- });
1170
- return running;
1171
- };
1172
- let runningQueue = cycle(staticNodeIds);
1173
- if (runningQueue.length === 0) {
1174
- throw new ValidationError("No Initial Runnning Node");
1175
- }
1176
- do {
1177
- runningQueue = cycle(runningQueue);
1178
- } while (runningQueue.length > 0);
1179
- if (Object.keys(pendings).length > 0) {
1180
- throw new ValidationError("Some nodes are not executed: " + Object.keys(pendings).join(", "));
1181
- }
1182
- };
1183
-
1184
- const agentValidator = (graphAgentIds, agentIds) => {
1185
- graphAgentIds.forEach((agentId) => {
1186
- // agentId or dynamic agentId
1187
- if (!agentIds.has(agentId) && agentId[0] !== ":") {
1188
- throw new ValidationError("Invalid Agent : " + agentId + " is not in AgentFunctionInfoDictionary.");
1189
- }
1190
- });
1191
- return true;
1192
- };
1193
-
1194
- const validateGraphData = (data, agentIds) => {
1195
- graphNodesValidator(data);
1196
- graphDataValidator(data);
1197
- const computedNodeIds = [];
1198
- const staticNodeIds = [];
1199
- const graphAgentIds = new Set();
1200
- Object.keys(data.nodes).forEach((nodeId) => {
1201
- const node = data.nodes[nodeId];
1202
- const isStaticNode = isStaticNodeData(node);
1203
- nodeValidator(node);
1204
- const agentId = isStaticNode ? "" : node.agent;
1205
- isStaticNode && staticNodeValidator(node) && staticNodeIds.push(nodeId);
1206
- !isStaticNode && computedNodeValidator(node) && computedNodeIds.push(nodeId) && typeof agentId === "string" && graphAgentIds.add(agentId);
1207
- });
1208
- agentValidator(graphAgentIds, new Set(agentIds));
1209
- relationValidator(data, staticNodeIds, computedNodeIds);
1210
- return true;
1211
- };
1212
- const validateAgent = (agentFunctionInfoDictionary) => {
1213
- Object.keys(agentFunctionInfoDictionary).forEach((agentId) => {
1214
- if (agentId !== "default") {
1215
- const agentInfo = agentFunctionInfoDictionary[agentId];
1216
- if (!agentInfo || !agentInfo.agent) {
1217
- throw new ValidationError("No Agent: " + agentId + " is not in AgentFunctionInfoDictionary.");
1218
- }
1219
- }
1220
- });
1221
- };
1222
-
1223
- // TaskManage object controls the concurrency of ComputedNode execution.
1224
- //
1225
- // NOTE: A TaskManager instance will be shared between parent graph and its children
1226
- // when nested agents are involved.
1227
- class TaskManager {
1228
- constructor(concurrency) {
1229
- this.taskQueue = [];
1230
- this.runningNodes = new Set();
1231
- this.concurrency = concurrency;
1232
- }
1233
- // This internal method dequeus a task from the task queue
1234
- // and call the associated callback method, if the number of
1235
- // running task is lower than the spcified limit.
1236
- dequeueTaskIfPossible() {
1237
- if (this.runningNodes.size < this.concurrency) {
1238
- const task = this.taskQueue.shift();
1239
- if (task) {
1240
- this.runningNodes.add(task.node);
1241
- task.callback(task.node);
1242
- }
1243
- }
1244
- }
1245
- // Node will call this method to put itself in the execution queue.
1246
- // We call the associated callback function when it is dequeued.
1247
- addTask(node, graphId, callback) {
1248
- // Finder tasks in the queue, which has either the same or higher priority.
1249
- const count = this.taskQueue.filter((task) => {
1250
- return task.node.priority >= node.priority;
1251
- }).length;
1252
- assert(count <= this.taskQueue.length, "TaskManager.addTask: Something is really wrong.");
1253
- this.taskQueue.splice(count, 0, { node, graphId, callback });
1254
- this.dequeueTaskIfPossible();
1255
- }
1256
- isRunning(graphId) {
1257
- const count = [...this.runningNodes].filter((node) => {
1258
- return node.graphId == graphId;
1259
- }).length;
1260
- return count > 0 || Array.from(this.taskQueue).filter((data) => data.graphId === graphId).length > 0;
1261
- }
1262
- // Node MUST call this method once the execution of agent function is completed
1263
- // either successfully or not.
1264
- onComplete(node) {
1265
- assert(this.runningNodes.has(node), `TaskManager.onComplete node(${node.nodeId}) is not in list`);
1266
- this.runningNodes.delete(node);
1267
- this.dequeueTaskIfPossible();
1268
- }
1269
- // Node will call this method before it hands the task manager from the graph
1270
- // to a nested agent. We need to make it sure that there is enough room to run
1271
- // computed nodes inside the nested graph to avoid a deadlock.
1272
- prepareForNesting() {
1273
- this.concurrency++;
1274
- }
1275
- restoreAfterNesting() {
1276
- this.concurrency--;
1277
- }
1278
- getStatus(verbose = false) {
1279
- const runningNodes = Array.from(this.runningNodes).map((node) => node.nodeId);
1280
- const queuedNodes = this.taskQueue.map((task) => task.node.nodeId);
1281
- const nodes = verbose ? { runningNodes, queuedNodes } : {};
1282
- return {
1283
- concurrency: this.concurrency,
1284
- queue: this.taskQueue.length,
1285
- running: this.runningNodes.size,
1286
- ...nodes,
1287
- };
1288
- }
1289
- reset() {
1290
- this.taskQueue.length = 0;
1291
- this.runningNodes.clear();
1292
- }
1293
- }
1294
-
1295
- const defaultConcurrency = 8;
1296
- const graphDataLatestVersion = 0.5;
1297
- class GraphAI {
1298
- // This method is called when either the GraphAI obect was created,
1299
- // or we are about to start n-th iteration (n>2).
1300
- createNodes(graphData) {
1301
- const nodes = Object.keys(graphData.nodes).reduce((_nodes, nodeId) => {
1302
- const nodeData = graphData.nodes[nodeId];
1303
- if (isComputedNodeData(nodeData)) {
1304
- _nodes[nodeId] = new ComputedNode(this.graphId, nodeId, nodeData, this);
1305
- }
1306
- else {
1307
- const updateValue = this.staticNodeInitData[nodeId];
1308
- _nodes[nodeId] = new StaticNode(nodeId, updateValue !== undefined ? { ...nodeData, value: updateValue } : nodeData, this);
1309
- }
1310
- return _nodes;
1311
- }, {});
1312
- // Generate the waitlist for each node.
1313
- Object.keys(nodes).forEach((nodeId) => {
1314
- const node = nodes[nodeId];
1315
- if (node.isComputedNode) {
1316
- node.pendings.forEach((pending) => {
1317
- if (nodes[pending]) {
1318
- nodes[pending].waitlist.add(nodeId); // previousNode
1319
- }
1320
- else {
1321
- throw new Error(`createNode: invalid input ${pending} for node, ${nodeId}`);
1322
- }
1323
- });
1324
- }
1325
- });
1326
- return nodes;
1327
- }
1328
- getValueFromResults(source, results) {
1329
- return getDataFromSource(source.nodeId ? results[source.nodeId] : undefined, source, this.propFunctions);
1330
- }
1331
- // for static
1332
- setStaticNodeResults(enableConsoleLog = false) {
1333
- // If the result property is specified, inject it.
1334
- // If the previousResults exists (indicating we are in a loop),
1335
- // process the update property (nodeId or nodeId.propId).
1336
- Object.keys(this.graphData.nodes).forEach((nodeId) => {
1337
- const node = this.nodes[nodeId];
1338
- if (node?.isStaticNode) {
1339
- const value = node?.value;
1340
- if (value !== undefined) {
1341
- node.setResultValue(nodeId);
1342
- }
1343
- if (enableConsoleLog) {
1344
- node.consoleLog();
1345
- }
1346
- }
1347
- });
1348
- }
1349
- updateStaticNodes(previousResults, enableConsoleLog = false) {
1350
- // If the result property is specified, inject it.
1351
- // If the previousResults exists (indicating we are in a loop),
1352
- // process the update property (nodeId or nodeId.propId).
1353
- Object.keys(this.graphData.nodes).forEach((nodeId) => {
1354
- const node = this.nodes[nodeId];
1355
- if (node?.isStaticNode) {
1356
- const update = node?.update;
1357
- if (update && previousResults) {
1358
- const result = this.getValueFromResults(update, previousResults);
1359
- this.updateStaticNodeValue(nodeId, result, update.nodeId);
1360
- }
1361
- if (enableConsoleLog) {
1362
- node.consoleLog();
1363
- }
1364
- }
1365
- });
1366
- }
1367
- constructor(graphData, agentFunctionInfoDictionary, options = {
1368
- taskManager: undefined,
1369
- agentFilters: [],
1370
- bypassAgentIds: [],
1371
- config: {},
1372
- graphLoader: undefined,
1373
- forceLoop: false,
1374
- mapIndex: undefined,
1375
- }) {
1376
- this.staticNodeInitData = {};
1377
- this.logs = [];
1378
- this.config = {};
1379
- this.onLogCallback = (__log, __isUpdate) => { };
1380
- this.callbacks = [];
1381
- this.repeatCount = 0;
1382
- if (!graphData.version && !options.taskManager) {
1383
- GraphAILogger.warn("------------ missing version number");
1384
- }
1385
- this.version = graphData.version ?? graphDataLatestVersion;
1386
- if (this.version < graphDataLatestVersion) {
1387
- GraphAILogger.warn(`------------ upgrade to ${graphDataLatestVersion}!`);
1388
- }
1389
- this.retryLimit = graphData.retry; // optional
1390
- this.graphId = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}`; // URL.createObjectURL(new Blob()).slice(-36);
1391
- this.agentFunctionInfoDictionary = agentFunctionInfoDictionary;
1392
- this.propFunctions = propFunctions;
1393
- this.taskManager = options.taskManager ?? new TaskManager(graphData.concurrency ?? defaultConcurrency);
1394
- this.agentFilters = options.agentFilters ?? [];
1395
- this.bypassAgentIds = options.bypassAgentIds ?? [];
1396
- this.config = options.config;
1397
- this.graphLoader = options.graphLoader;
1398
- this.forceLoop = options.forceLoop ?? false;
1399
- this.mapIndex = options.mapIndex;
1400
- this.loop = graphData.loop;
1401
- this.verbose = graphData.verbose === true;
1402
- this.onComplete = (__isAbort) => {
1403
- throw new Error("SOMETHING IS WRONG: onComplete is called without run()");
1404
- };
1405
- validateGraphData(graphData, [...Object.keys(agentFunctionInfoDictionary), ...this.bypassAgentIds]);
1406
- validateAgent(agentFunctionInfoDictionary);
1407
- this.graphData = {
1408
- ...graphData,
1409
- nodes: {
1410
- ...graphData.nodes,
1411
- [loopCounterKey]: { value: 0, update: `:${loopCounterKey}.add(1)` },
1412
- },
1413
- };
1414
- this.nodes = this.createNodes(this.graphData);
1415
- }
1416
- getAgentFunctionInfo(agentId) {
1417
- if (agentId && this.agentFunctionInfoDictionary[agentId]) {
1418
- return this.agentFunctionInfoDictionary[agentId];
1419
- }
1420
- if (agentId && this.bypassAgentIds.includes(agentId)) {
1421
- return {
1422
- agent: async () => {
1423
- return null;
1424
- },
1425
- hasGraphData: false,
1426
- inputs: null,
1427
- cacheType: undefined, // for node.getContext
1428
- };
1429
- }
1430
- // We are not supposed to hit this error because the validator will catch it.
1431
- throw new Error("No agent: " + agentId);
1432
- }
1433
- asString() {
1434
- return Object.values(this.nodes)
1435
- .map((node) => node.asString())
1436
- .join("\n");
1437
- }
1438
- // Public API
1439
- results(all, internalUse = false) {
1440
- return Object.keys(this.nodes)
1441
- .filter((nodeId) => (all && (internalUse || nodeId !== loopCounterKey)) || this.nodes[nodeId].isResult)
1442
- .reduce((results, nodeId) => {
1443
- const node = this.nodes[nodeId];
1444
- if (node.result !== undefined) {
1445
- results[nodeId] = node.result;
1446
- }
1447
- return results;
1448
- }, {});
1449
- }
1450
- // Public API
1451
- errors() {
1452
- return Object.keys(this.nodes).reduce((errors, nodeId) => {
1453
- const node = this.nodes[nodeId];
1454
- if (node.isComputedNode) {
1455
- if (node.error !== undefined) {
1456
- errors[nodeId] = node.error;
1457
- }
1458
- }
1459
- return errors;
1460
- }, {});
1461
- }
1462
- pushReadyNodesIntoQueue() {
1463
- // Nodes without pending data should run immediately.
1464
- Object.keys(this.nodes).forEach((nodeId) => {
1465
- const node = this.nodes[nodeId];
1466
- if (node.isComputedNode) {
1467
- this.pushQueueIfReady(node);
1468
- }
1469
- });
1470
- }
1471
- pushQueueIfReady(node) {
1472
- if (node.isReadyNode()) {
1473
- this.pushQueue(node);
1474
- }
1475
- }
1476
- pushQueueIfReadyAndRunning(node) {
1477
- if (this.isRunning()) {
1478
- this.pushQueueIfReady(node);
1479
- }
1480
- }
1481
- // for computed
1482
- pushQueue(node) {
1483
- node.beforeAddTask();
1484
- this.taskManager.addTask(node, this.graphId, (_node) => {
1485
- assert(node.nodeId === _node.nodeId, "GraphAI.pushQueue node mismatch");
1486
- node.execute();
1487
- });
1488
- }
1489
- // Public API
1490
- async run(all = false) {
1491
- this.setStaticNodeResults();
1492
- const invalidStaticNodes = Object.values(this.nodes)
1493
- .filter((node) => node.isStaticNode)
1494
- .filter((node) => node.result === undefined && node.update === undefined);
1495
- if (invalidStaticNodes.length > 0) {
1496
- const nodeIds = invalidStaticNodes.map((node) => node.nodeId).join(", ");
1497
- throw new Error(`Static node(s) must have value. Set value, injectValue, or set update. Affected nodeIds: ${nodeIds}`);
1498
- }
1499
- if (this.isRunning()) {
1500
- throw new Error("This GraphAI instance is already running");
1501
- }
1502
- this.pushReadyNodesIntoQueue();
1503
- if (!this.isRunning()) {
1504
- GraphAILogger.warn("-- nothing to execute");
1505
- return {};
1506
- }
1507
- return new Promise((resolve, reject) => {
1508
- this.onComplete = (isAbort = false) => {
1509
- const errors = this.errors();
1510
- const errorNodeIds = Object.keys(errors);
1511
- if (errorNodeIds.length > 0 || isAbort) {
1512
- reject(errors[errorNodeIds[0]]);
1513
- }
1514
- else {
1515
- resolve(this.results(all));
1516
- }
1517
- };
1518
- });
1519
- }
1520
- abort(isChild = false) {
1521
- if (this.isRunning()) {
1522
- this.resetPending();
1523
- // Stop All Running node.
1524
- }
1525
- // For an agent like an event agent, where an external promise remains unresolved,
1526
- // aborting and then retrying can cause nodes or the graph to execute again.
1527
- // To prevent this, the transactionId is updated to ensure the retry fails.
1528
- Object.values(this.nodes).forEach((node) => node.isComputedNode && (node.transactionId = undefined));
1529
- if (!isChild) {
1530
- this.taskManager.reset();
1531
- }
1532
- this.onComplete(this.isRunning());
1533
- }
1534
- resetPending() {
1535
- Object.values(this.nodes).map((node) => {
1536
- if (node.isComputedNode) {
1537
- node.resetPending();
1538
- }
1539
- });
1540
- }
1541
- // Public only for testing
1542
- isRunning() {
1543
- return this.taskManager.isRunning(this.graphId);
1544
- }
1545
- // callback from execute
1546
- onExecutionComplete(node) {
1547
- this.taskManager.onComplete(node);
1548
- if (this.isRunning() || this.processLoopIfNecessary()) {
1549
- return; // continue running
1550
- }
1551
- if (this.verbose) {
1552
- const notExecutedNodes = Object.values(this.nodes).filter((node) => node.isComputedNode && node.state !== "completed");
1553
- if (notExecutedNodes.length > 0) {
1554
- console.log("Those nodes are not running. " + notExecutedNodes.map((node) => `${node.nodeId}: ${node.state}`).join(", "));
1555
- }
1556
- }
1557
- this.onComplete(false); // Nothing to run. Finish it.
1558
- }
1559
- // Must be called only from onExecutionComplete righ after removeRunning
1560
- // Check if there is any running computed nodes.
1561
- // In case of no running computed note, start the another iteration if ncessary (loop)
1562
- processLoopIfNecessary() {
1563
- //
1564
- if (!this.forceLoop && Object.keys(this.errors()).length > 0) {
1565
- return false;
1566
- }
1567
- this.repeatCount++;
1568
- const loop = this.loop;
1569
- if (!loop) {
1570
- return false;
1571
- }
1572
- // We need to update static nodes, before checking the condition
1573
- const previousResults = this.results(true, true); // results from previous loop
1574
- this.updateStaticNodes(previousResults);
1575
- this.setStaticNodeResults();
1576
- if (loop.count === undefined || this.repeatCount < loop.count) {
1577
- if (loop.while) {
1578
- const source = parseNodeName(loop.while);
1579
- const value = this.getValueFromResults(source, this.results(true, true));
1580
- // NOTE: We treat an empty array as false.
1581
- if (!isLogicallyTrue(value)) {
1582
- return false; // while condition is not met
1583
- }
1584
- }
1585
- this.nodes = this.createNodes(this.graphData);
1586
- this.updateStaticNodes(previousResults, true);
1587
- this.setStaticNodeResults();
1588
- this.pushReadyNodesIntoQueue();
1589
- return true; // Indicating that we are going to continue.
1590
- }
1591
- return false;
1592
- }
1593
- initializeGraphAI() {
1594
- if (this.isRunning()) {
1595
- throw new Error("This GraphAI instance is running");
1596
- }
1597
- this.nodes = this.createNodes(this.graphData);
1598
- this.setStaticNodeResults();
1599
- }
1600
- setPreviousResults(previousResults) {
1601
- this.updateStaticNodes(previousResults);
1602
- }
1603
- setLoopLog(log) {
1604
- log.isLoop = !!this.loop;
1605
- log.repeatCount = this.repeatCount;
1606
- }
1607
- appendLog(log) {
1608
- this.logs.push(log);
1609
- this.onLogCallback(log, false);
1610
- this.callbacks.forEach((callback) => callback(log, false));
1611
- }
1612
- updateLog(log) {
1613
- this.onLogCallback(log, true);
1614
- this.callbacks.forEach((callback) => callback(log, false));
1615
- }
1616
- registerCallback(callback) {
1617
- this.callbacks.push(callback);
1618
- }
1619
- clearCallbacks() {
1620
- this.callbacks = [];
1621
- }
1622
- // Public API
1623
- transactionLogs() {
1624
- return this.logs;
1625
- }
1626
- // Public API
1627
- injectValue(nodeId, value, injectFrom) {
1628
- this.staticNodeInitData[nodeId] = value;
1629
- this.updateStaticNodeValue(nodeId, value, injectFrom);
1630
- }
1631
- setLoopCount(count) {
1632
- this.loop = {
1633
- count,
1634
- };
1635
- }
1636
- updateStaticNodeValue(nodeId, value, injectFrom) {
1637
- const node = this.nodes[nodeId];
1638
- if (node && node.isStaticNode) {
1639
- node.updateValue(value, injectFrom);
1640
- }
1641
- else {
1642
- throw new Error(`injectValue with Invalid nodeId, ${nodeId}`);
1643
- }
1644
- }
1645
- resultsOf(inputs, anyInput = false) {
1646
- const results = resultsOf(inputs ?? {}, this.nodes, this.propFunctions);
1647
- if (anyInput) {
1648
- return cleanResult(results);
1649
- }
1650
- return results;
1651
- }
1652
- resultOf(source) {
1653
- return resultOf(source, this.nodes, this.propFunctions);
1654
- }
1655
- }
1656
-
1657
- export { GraphAI, GraphAILogger, NodeState, TaskManager, ValidationError, agentInfoWrapper, assert, debugResultKey, defaultAgentInfo, defaultConcurrency, defaultTestContext, graphDataLatestVersion, inputs2dataSources, isComputedNodeData, isNull, isObject, isStaticNodeData, parseNodeName, sleep, strIntentionalError };
1658
- //# sourceMappingURL=bundle.esm.js.map