@ai.ntellect/core 0.6.20 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.json +2 -1
- package/README.md +87 -148
- package/graph/controller.ts +1 -1
- package/graph/event-manager.ts +288 -0
- package/graph/index.ts +152 -384
- package/graph/logger.ts +70 -0
- package/graph/node.ts +398 -0
- package/graph/observer.ts +361 -0
- package/interfaces/index.ts +102 -1
- package/modules/agenda/index.ts +3 -16
- package/modules/embedding/index.ts +3 -3
- package/package.json +12 -20
- package/test/graph/index.test.ts +296 -154
- package/test/graph/observer.test.ts +398 -0
- package/test/modules/agenda/node-cron.test.ts +37 -16
- package/test/modules/memory/adapters/in-memory.test.ts +2 -2
- package/test/modules/memory/adapters/meilisearch.test.ts +28 -24
- package/test/modules/memory/base.test.ts +3 -3
- package/tsconfig.json +4 -2
- package/types/index.ts +23 -2
- package/utils/generate-action-schema.ts +8 -7
- package/.env.example +0 -2
- package/dist/graph/controller.js +0 -75
- package/dist/graph/index.js +0 -402
- package/dist/index.js +0 -41
- package/dist/interfaces/index.js +0 -17
- package/dist/modules/agenda/adapters/node-cron/index.js +0 -29
- package/dist/modules/agenda/index.js +0 -140
- package/dist/modules/embedding/adapters/ai/index.js +0 -57
- package/dist/modules/embedding/index.js +0 -59
- package/dist/modules/memory/adapters/in-memory/index.js +0 -210
- package/dist/modules/memory/adapters/meilisearch/index.js +0 -320
- package/dist/modules/memory/adapters/redis/index.js +0 -158
- package/dist/modules/memory/index.js +0 -103
- package/dist/types/index.js +0 -2
- package/dist/utils/generate-action-schema.js +0 -42
- package/dist/utils/header-builder.js +0 -34
- package/graph.ts +0 -74
- package/test/modules/embedding/ai.test.ts +0 -78
- package/test/modules/memory/adapters/redis.test.ts +0 -169
- package/test/services/agenda.test.ts +0 -279
package/test/graph/index.test.ts
CHANGED
@@ -2,41 +2,63 @@ import { expect } from "chai";
|
|
2
2
|
import EventEmitter from "events";
|
3
3
|
import sinon from "sinon";
|
4
4
|
import { z } from "zod";
|
5
|
+
import { GraphFlowController } from "../../graph/controller";
|
5
6
|
import { GraphFlow } from "../../graph/index";
|
6
|
-
import { GraphDefinition, Node } from "../../types";
|
7
|
+
import { GraphContext, GraphDefinition, Node } from "../../types";
|
8
|
+
|
7
9
|
/**
|
8
|
-
*
|
10
|
+
* Test schema definition using Zod for graph context validation
|
11
|
+
* Defines a schema with:
|
12
|
+
* - value: numeric value for tracking state changes
|
13
|
+
* - eventPayload: optional object containing transaction metadata
|
9
14
|
*/
|
10
15
|
const TestSchema = z.object({
|
11
16
|
value: z.number().default(0),
|
17
|
+
eventPayload: z
|
18
|
+
.object({
|
19
|
+
transactionId: z.string().optional(),
|
20
|
+
status: z.string().optional(),
|
21
|
+
})
|
22
|
+
.optional(),
|
12
23
|
});
|
13
24
|
|
14
|
-
/**
|
15
|
-
* ✅ Define the schema type for TypeScript inference.
|
16
|
-
*/
|
17
25
|
type TestSchema = typeof TestSchema;
|
18
26
|
|
27
|
+
/**
|
28
|
+
* Test suite for the Graph Flow implementation
|
29
|
+
* This suite validates the core functionality of the graph-based workflow system:
|
30
|
+
* - Node execution and state management through context
|
31
|
+
* - Event handling (emission, correlation, waiting)
|
32
|
+
* - Error handling and retry mechanisms
|
33
|
+
* - Input/Output validation using Zod schemas
|
34
|
+
* - Complex workflows with multiple branches and conditions
|
35
|
+
* - Parallel and sequential execution patterns
|
36
|
+
*
|
37
|
+
* The tests use a simple numeric value-based context to demonstrate state changes
|
38
|
+
* and a transaction-based event payload for testing event correlation.
|
39
|
+
*/
|
19
40
|
describe("Graph", function () {
|
20
41
|
let graph: GraphFlow<TestSchema>;
|
21
42
|
let eventEmitter: EventEmitter;
|
22
43
|
|
23
44
|
beforeEach(() => {
|
24
45
|
eventEmitter = new EventEmitter();
|
25
|
-
graph = new GraphFlow(
|
26
|
-
"TestGraph",
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
eventEmitter: eventEmitter,
|
33
|
-
},
|
34
|
-
{ verbose: true }
|
35
|
-
);
|
46
|
+
graph = new GraphFlow("TestGraph", {
|
47
|
+
name: "TestGraph",
|
48
|
+
nodes: [],
|
49
|
+
context: { value: 0 },
|
50
|
+
schema: TestSchema,
|
51
|
+
eventEmitter: eventEmitter,
|
52
|
+
});
|
36
53
|
});
|
37
54
|
|
38
55
|
/**
|
39
|
-
*
|
56
|
+
* Tests basic node execution and context update functionality
|
57
|
+
* Validates that:
|
58
|
+
* - A node can be added to the graph
|
59
|
+
* - The node's execute function is called
|
60
|
+
* - The context is properly updated
|
61
|
+
* - The updated context is accessible after execution
|
40
62
|
*/
|
41
63
|
it("should execute a simple node and update the context", async function () {
|
42
64
|
const simpleNode: Node<TestSchema> = {
|
@@ -55,7 +77,11 @@ describe("Graph", function () {
|
|
55
77
|
});
|
56
78
|
|
57
79
|
/**
|
58
|
-
*
|
80
|
+
* Tests event emission for node lifecycle events
|
81
|
+
* Validates that the graph properly emits events for:
|
82
|
+
* - Node execution start (nodeStarted)
|
83
|
+
* - Node execution completion (nodeCompleted)
|
84
|
+
* This is crucial for monitoring and debugging workflow execution
|
59
85
|
*/
|
60
86
|
it("should trigger `nodeStarted` and `nodeCompleted` events", async function () {
|
61
87
|
const nodeStartedSpy = sinon.spy();
|
@@ -80,7 +106,12 @@ describe("Graph", function () {
|
|
80
106
|
});
|
81
107
|
|
82
108
|
/**
|
83
|
-
*
|
109
|
+
* Tests error handling and error event emission
|
110
|
+
* Validates that:
|
111
|
+
* - Errors in node execution are properly caught
|
112
|
+
* - The nodeError event is emitted
|
113
|
+
* - The error message is preserved
|
114
|
+
* This ensures robust error handling in the workflow
|
84
115
|
*/
|
85
116
|
it("should handle errors and trigger `nodeError` event", async function () {
|
86
117
|
const errorNode: Node<TestSchema> = {
|
@@ -105,7 +136,12 @@ describe("Graph", function () {
|
|
105
136
|
});
|
106
137
|
|
107
138
|
/**
|
108
|
-
*
|
139
|
+
* Tests context validation using Zod schema
|
140
|
+
* Validates that:
|
141
|
+
* - Invalid context values are rejected
|
142
|
+
* - Proper error messages are generated
|
143
|
+
* - Type safety is maintained during execution
|
144
|
+
* This ensures data integrity throughout the workflow
|
109
145
|
*/
|
110
146
|
it("should validate context with Zod", async function () {
|
111
147
|
const invalidContext = { value: "invalid_string" };
|
@@ -129,7 +165,12 @@ describe("Graph", function () {
|
|
129
165
|
});
|
130
166
|
|
131
167
|
/**
|
132
|
-
*
|
168
|
+
* Tests node execution with input/output validation
|
169
|
+
* Demonstrates:
|
170
|
+
* - Input parameter validation
|
171
|
+
* - Output state validation
|
172
|
+
* - Integration between node execution and validation
|
173
|
+
* Ensures type safety and data consistency in node interactions
|
133
174
|
*/
|
134
175
|
it("should execute a node with validated inputs and outputs", async function () {
|
135
176
|
const paramNode: Node<TestSchema, { increment: number }> = {
|
@@ -154,7 +195,12 @@ describe("Graph", function () {
|
|
154
195
|
});
|
155
196
|
|
156
197
|
/**
|
157
|
-
*
|
198
|
+
* Tests conditional node execution
|
199
|
+
* Validates that:
|
200
|
+
* - Nodes can have conditional execution logic
|
201
|
+
* - Conditions are evaluated against current context
|
202
|
+
* - Nodes are skipped when conditions are not met
|
203
|
+
* This enables dynamic workflow paths based on state
|
158
204
|
*/
|
159
205
|
it("should not execute a node when condition is false", async function () {
|
160
206
|
const conditionalNode: Node<TestSchema> = {
|
@@ -174,7 +220,13 @@ describe("Graph", function () {
|
|
174
220
|
});
|
175
221
|
|
176
222
|
/**
|
177
|
-
*
|
223
|
+
* Tests node retry functionality
|
224
|
+
* Validates the retry mechanism:
|
225
|
+
* - Maximum attempt limits
|
226
|
+
* - Retry delays
|
227
|
+
* - Success after retry
|
228
|
+
* - Context preservation between attempts
|
229
|
+
* Essential for handling transient failures in workflows
|
178
230
|
*/
|
179
231
|
it("should retry a node execution when it fails", async function () {
|
180
232
|
let attemptCount = 0;
|
@@ -200,45 +252,7 @@ describe("Graph", function () {
|
|
200
252
|
});
|
201
253
|
|
202
254
|
/**
|
203
|
-
*
|
204
|
-
*/
|
205
|
-
it("should trigger a node execution from an event", async function () {
|
206
|
-
this.timeout(5000); // Ensure we have enough time to complete the test
|
207
|
-
|
208
|
-
const eventNode: Node<TestSchema> = {
|
209
|
-
name: "eventNode",
|
210
|
-
events: ["customEvent"],
|
211
|
-
execute: async (context) => {
|
212
|
-
context.value = (context.value ?? 0) + 1;
|
213
|
-
},
|
214
|
-
next: [],
|
215
|
-
};
|
216
|
-
|
217
|
-
graph.addNode(eventNode);
|
218
|
-
|
219
|
-
// Use a promise to ensure the event is properly handled
|
220
|
-
await new Promise<void>((resolve, reject) => {
|
221
|
-
const timeout = setTimeout(
|
222
|
-
() => reject(new Error("Event did not trigger")),
|
223
|
-
1500
|
224
|
-
);
|
225
|
-
|
226
|
-
graph.on("nodeCompleted", ({ name }) => {
|
227
|
-
if (name === "eventNode") {
|
228
|
-
clearTimeout(timeout);
|
229
|
-
resolve();
|
230
|
-
}
|
231
|
-
});
|
232
|
-
|
233
|
-
graph.emit("customEvent").catch(reject);
|
234
|
-
});
|
235
|
-
|
236
|
-
const context = graph.getContext();
|
237
|
-
expect(context.value).to.equal(1);
|
238
|
-
});
|
239
|
-
|
240
|
-
/**
|
241
|
-
* ✅ Ensure that removing a node works correctly.
|
255
|
+
* Tests node removal functionality
|
242
256
|
*/
|
243
257
|
it("should remove a node from the graph", function () {
|
244
258
|
const testNode: Node<TestSchema> = {
|
@@ -251,6 +265,9 @@ describe("Graph", function () {
|
|
251
265
|
expect(graph.getNodes().length).to.equal(0);
|
252
266
|
});
|
253
267
|
|
268
|
+
/**
|
269
|
+
* Tests graph reloading functionality
|
270
|
+
*/
|
254
271
|
it("should clear and reload the graph using `load`", function () {
|
255
272
|
const nodeA: Node<TestSchema> = {
|
256
273
|
name: "A",
|
@@ -275,7 +292,7 @@ describe("Graph", function () {
|
|
275
292
|
});
|
276
293
|
|
277
294
|
/**
|
278
|
-
*
|
295
|
+
* Tests input validation error handling
|
279
296
|
*/
|
280
297
|
it("should throw error when node input validation fails", async function () {
|
281
298
|
const nodeWithInput: Node<TestSchema, { amount: number }> = {
|
@@ -302,7 +319,7 @@ describe("Graph", function () {
|
|
302
319
|
});
|
303
320
|
|
304
321
|
/**
|
305
|
-
*
|
322
|
+
* Tests output validation error handling
|
306
323
|
*/
|
307
324
|
it("should throw error when node output validation fails", async function () {
|
308
325
|
const nodeWithOutput: Node<TestSchema> = {
|
@@ -329,7 +346,7 @@ describe("Graph", function () {
|
|
329
346
|
});
|
330
347
|
|
331
348
|
/**
|
332
|
-
*
|
349
|
+
* Tests successful input/output validation flow
|
333
350
|
*/
|
334
351
|
it("should successfully validate both inputs and outputs", async function () {
|
335
352
|
const validatedNode: Node<TestSchema, { increment: number }> = {
|
@@ -364,7 +381,7 @@ describe("Graph", function () {
|
|
364
381
|
});
|
365
382
|
|
366
383
|
/**
|
367
|
-
*
|
384
|
+
* Tests handling of missing required inputs
|
368
385
|
*/
|
369
386
|
it("should throw error when required inputs are missing", async function () {
|
370
387
|
const nodeWithRequiredInput: Node<TestSchema, { required: string }> = {
|
@@ -387,9 +404,15 @@ describe("Graph", function () {
|
|
387
404
|
});
|
388
405
|
|
389
406
|
/**
|
390
|
-
*
|
407
|
+
* Tests complex workflow execution with multiple branches
|
408
|
+
* Demonstrates:
|
409
|
+
* - Multiple execution paths
|
410
|
+
* - Node chaining
|
411
|
+
* - Parallel branch execution
|
412
|
+
* - Context accumulation across branches
|
413
|
+
* This validates the graph's ability to handle complex business processes
|
391
414
|
*/
|
392
|
-
it("should execute a complex workflow with multiple
|
415
|
+
it("should execute a complex workflow with multiple nodes and accumulate the value", async function () {
|
393
416
|
const nodeA: Node<TestSchema> = {
|
394
417
|
name: "nodeA",
|
395
418
|
execute: async (context) => {
|
@@ -417,20 +440,18 @@ describe("Graph", function () {
|
|
417
440
|
const nodeC: Node<TestSchema> = {
|
418
441
|
name: "nodeC",
|
419
442
|
execute: async (context) => {
|
420
|
-
|
421
|
-
const newValue = (context.value ?? 0) + 5;
|
422
|
-
context.value = newValue;
|
443
|
+
context.value = (context.value ?? 0) + 5;
|
423
444
|
},
|
424
445
|
};
|
425
446
|
|
426
447
|
[nodeA, nodeB1, nodeB2, nodeC].forEach((node) => graph.addNode(node));
|
427
448
|
|
428
449
|
await graph.execute("nodeA");
|
429
|
-
expect(graph.getContext().value).to.equal(
|
450
|
+
expect(graph.getContext().value).to.equal(15);
|
430
451
|
});
|
431
452
|
|
432
453
|
/**
|
433
|
-
*
|
454
|
+
* Tests conditional branching in workflows
|
434
455
|
*/
|
435
456
|
it("should execute different branches based on conditions", async function () {
|
436
457
|
const startNode: Node<TestSchema> = {
|
@@ -438,141 +459,262 @@ describe("Graph", function () {
|
|
438
459
|
execute: async (context) => {
|
439
460
|
context.value = (context.value ?? 0) + 5;
|
440
461
|
},
|
441
|
-
next: ["
|
462
|
+
next: ["end"],
|
442
463
|
};
|
443
464
|
|
444
|
-
const
|
445
|
-
name: "
|
446
|
-
condition: (context) => (context.value ?? 0) < 10,
|
465
|
+
const endNode: Node<TestSchema> = {
|
466
|
+
name: "end",
|
447
467
|
execute: async (context) => {
|
448
|
-
|
468
|
+
if ((context.value ?? 0) < 10) {
|
469
|
+
context.value = (context.value ?? 0) * 2;
|
470
|
+
} else {
|
471
|
+
context.value = (context.value ?? 0) + 1;
|
472
|
+
}
|
449
473
|
},
|
450
|
-
next: ["end"],
|
451
474
|
};
|
452
475
|
|
453
|
-
|
454
|
-
|
455
|
-
|
476
|
+
[startNode, endNode].forEach((node) => graph.addNode(node));
|
477
|
+
|
478
|
+
await graph.execute("start");
|
479
|
+
expect(graph.getContext().value).to.equal(10);
|
480
|
+
});
|
481
|
+
|
482
|
+
/**
|
483
|
+
* Tests parallel workflow execution using GraphController
|
484
|
+
* Validates:
|
485
|
+
* - Multiple graph execution in parallel
|
486
|
+
* - Independent context maintenance
|
487
|
+
* - Proper result aggregation
|
488
|
+
* - Concurrency control
|
489
|
+
* Essential for scaling workflow processing
|
490
|
+
*/
|
491
|
+
it("should handle parallel workflows using GraphController", async function () {
|
492
|
+
// Graph 1
|
493
|
+
const graph1 = new GraphFlow("Graph1", {
|
494
|
+
name: "Graph1",
|
495
|
+
nodes: [],
|
496
|
+
context: { value: 0 },
|
497
|
+
schema: TestSchema,
|
498
|
+
});
|
499
|
+
|
500
|
+
const processNode1: Node<TestSchema> = {
|
501
|
+
name: "process1",
|
456
502
|
execute: async (context) => {
|
457
|
-
context.value =
|
503
|
+
context.value = 1;
|
458
504
|
},
|
459
|
-
next: ["
|
505
|
+
next: ["finalize1"],
|
460
506
|
};
|
461
507
|
|
462
|
-
const
|
463
|
-
name: "
|
508
|
+
const finalizeNode1: Node<TestSchema> = {
|
509
|
+
name: "finalize1",
|
464
510
|
execute: async (context) => {
|
465
|
-
context.value = (context.value ?? 0)
|
511
|
+
context.value = (context.value ?? 0) * 2;
|
466
512
|
},
|
467
513
|
};
|
468
514
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
await graph.load({
|
474
|
-
name: "TestGraph",
|
475
|
-
nodes: [startNode, branchA, branchB, endNode],
|
515
|
+
// Graph 2
|
516
|
+
const graph2 = new GraphFlow("Graph2", {
|
517
|
+
name: "Graph2",
|
518
|
+
nodes: [],
|
476
519
|
context: { value: 0 },
|
477
520
|
schema: TestSchema,
|
478
521
|
});
|
479
522
|
|
480
|
-
|
481
|
-
|
523
|
+
const processNode2: Node<TestSchema> = {
|
524
|
+
name: "process2",
|
525
|
+
execute: async (context) => {
|
526
|
+
context.value = 2;
|
527
|
+
},
|
528
|
+
next: ["finalize2"],
|
529
|
+
};
|
530
|
+
|
531
|
+
const finalizeNode2: Node<TestSchema> = {
|
532
|
+
name: "finalize2",
|
533
|
+
execute: async (context) => {
|
534
|
+
context.value = (context.value ?? 0) + 3;
|
535
|
+
},
|
536
|
+
};
|
537
|
+
|
538
|
+
graph1.addNode(processNode1);
|
539
|
+
graph1.addNode(finalizeNode1);
|
540
|
+
graph2.addNode(processNode2);
|
541
|
+
graph2.addNode(finalizeNode2);
|
542
|
+
|
543
|
+
const results = await GraphFlowController.executeParallel(
|
544
|
+
[graph1, graph2],
|
545
|
+
["process1", "process2"],
|
546
|
+
2,
|
547
|
+
[{}, {}]
|
548
|
+
);
|
549
|
+
|
550
|
+
expect(results[0].value).to.equal(2); // Graph1: 1 * 2
|
551
|
+
expect(results[1].value).to.equal(5); // Graph2: 2 + 3
|
482
552
|
});
|
483
553
|
|
484
554
|
/**
|
485
|
-
*
|
555
|
+
* Tests sequential workflow execution using GraphController
|
486
556
|
*/
|
487
|
-
it("should handle
|
488
|
-
|
489
|
-
const
|
557
|
+
it("should handle sequential workflows using GraphController", async function () {
|
558
|
+
// Graph 1
|
559
|
+
const graph1 = new GraphFlow("Graph1", {
|
560
|
+
name: "Graph1",
|
561
|
+
nodes: [],
|
562
|
+
context: { value: 1 },
|
563
|
+
schema: TestSchema,
|
564
|
+
});
|
490
565
|
|
491
|
-
const
|
492
|
-
name: "
|
493
|
-
events: ["startWorkflow"],
|
566
|
+
const startNode1: Node<TestSchema> = {
|
567
|
+
name: "start1",
|
494
568
|
execute: async (context) => {
|
495
|
-
context.value =
|
569
|
+
context.value = (context.value ?? 0) * 2;
|
496
570
|
},
|
497
|
-
next: ["process"],
|
498
571
|
};
|
499
572
|
|
500
|
-
|
501
|
-
|
502
|
-
|
573
|
+
// Graph 2
|
574
|
+
const graph2 = new GraphFlow("Graph2", {
|
575
|
+
name: "Graph2",
|
576
|
+
nodes: [],
|
577
|
+
context: { value: 3 },
|
578
|
+
schema: TestSchema,
|
579
|
+
});
|
580
|
+
|
581
|
+
const startNode2: Node<TestSchema> = {
|
582
|
+
name: "start2",
|
503
583
|
execute: async (context) => {
|
504
|
-
context.value = (context.value ?? 0)
|
584
|
+
context.value = (context.value ?? 0) + 2;
|
505
585
|
},
|
506
|
-
next: ["finalize"],
|
507
586
|
};
|
508
587
|
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
588
|
+
graph1.addNode(startNode1);
|
589
|
+
graph2.addNode(startNode2);
|
590
|
+
|
591
|
+
const results = await GraphFlowController.executeSequential(
|
592
|
+
[graph1, graph2],
|
593
|
+
["start1", "start2"],
|
594
|
+
[{}, {}]
|
595
|
+
);
|
596
|
+
|
597
|
+
expect(results[0].value).to.equal(2); // Graph1: 1 * 2
|
598
|
+
expect(results[1].value).to.equal(5); // Graph2: 3 + 2
|
599
|
+
});
|
600
|
+
|
601
|
+
/**
|
602
|
+
* Tests event correlation functionality
|
603
|
+
* Demonstrates:
|
604
|
+
* - Event correlation based on transaction ID
|
605
|
+
* - Timeout handling
|
606
|
+
* - Multiple event synchronization
|
607
|
+
* - Context updates after correlation
|
608
|
+
* Critical for integrating with external event sources
|
609
|
+
*/
|
610
|
+
it("should handle correlated events correctly", async function () {
|
611
|
+
this.timeout(10000);
|
612
|
+
const graph = new GraphFlow("test", {
|
613
|
+
name: "test",
|
614
|
+
nodes: [],
|
615
|
+
context: { value: 0 },
|
616
|
+
schema: TestSchema,
|
617
|
+
eventEmitter: new EventEmitter(),
|
618
|
+
});
|
619
|
+
|
620
|
+
let eventsReceived = 0;
|
621
|
+
const node = {
|
622
|
+
name: "testNode",
|
623
|
+
waitForEvents: {
|
624
|
+
events: ["eventA", "eventB"],
|
625
|
+
timeout: 5000,
|
626
|
+
strategy: "all" as const,
|
627
|
+
},
|
628
|
+
execute: async (context: GraphContext<typeof TestSchema>) => {
|
629
|
+
eventsReceived = 2;
|
630
|
+
context.value = 42;
|
515
631
|
},
|
516
632
|
};
|
517
633
|
|
518
|
-
|
519
|
-
graph.addNode(node)
|
520
|
-
);
|
634
|
+
graph.addNode(node);
|
521
635
|
|
522
|
-
|
523
|
-
await graph.emit("startWorkflow");
|
524
|
-
await graph.emit("processData");
|
525
|
-
await graph.emit("complete");
|
636
|
+
graph.execute("testNode");
|
526
637
|
|
527
|
-
|
528
|
-
expect(eventCounter.count).to.equal(1);
|
638
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
529
639
|
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
640
|
+
await graph.emit("eventA", { eventPayload: { status: "A" } });
|
641
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
642
|
+
await graph.emit("eventB", { eventPayload: { status: "B" } });
|
643
|
+
|
644
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
645
|
+
|
646
|
+
expect(eventsReceived).to.equal(2);
|
647
|
+
expect(graph.getContext().value).to.equal(42);
|
648
|
+
});
|
649
|
+
|
650
|
+
/**
|
651
|
+
* Tests multiple event waiting functionality
|
652
|
+
*/
|
653
|
+
it("should wait for multiple events before continuing", async function () {
|
654
|
+
this.timeout(10000);
|
655
|
+
const graph = new GraphFlow("test", {
|
656
|
+
name: "test",
|
657
|
+
nodes: [],
|
534
658
|
context: { value: 0 },
|
535
659
|
schema: TestSchema,
|
660
|
+
eventEmitter: new EventEmitter(),
|
536
661
|
});
|
537
662
|
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
663
|
+
const node = {
|
664
|
+
name: "testNode",
|
665
|
+
waitForEvents: {
|
666
|
+
events: ["event1", "event2"],
|
667
|
+
timeout: 5000,
|
668
|
+
strategy: "all" as const,
|
669
|
+
},
|
670
|
+
execute: async (context: GraphContext<typeof TestSchema>) => {
|
671
|
+
context.value = 42; // Ajouter une modification du contexte
|
672
|
+
},
|
673
|
+
};
|
674
|
+
|
675
|
+
graph.addNode(node);
|
676
|
+
graph.execute("testNode");
|
544
677
|
|
545
|
-
|
678
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
679
|
+
await graph.emit("event1", { eventPayload: { status: "1" } });
|
680
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
681
|
+
await graph.emit("event2", { eventPayload: { status: "2" } });
|
682
|
+
expect(graph.getContext().value).to.equal(42);
|
546
683
|
});
|
547
684
|
|
548
685
|
/**
|
549
|
-
*
|
686
|
+
* Tests single event waiting functionality
|
550
687
|
*/
|
551
|
-
it("should
|
552
|
-
|
688
|
+
it("should wait for a single event before continuing", async function () {
|
689
|
+
this.timeout(5000);
|
553
690
|
|
554
|
-
const
|
555
|
-
name: "
|
556
|
-
execute: async (context) => {
|
557
|
-
context.value =
|
558
|
-
iterationCount.count++;
|
691
|
+
const waitingNode: Node<TestSchema> = {
|
692
|
+
name: "waitingNode",
|
693
|
+
execute: async (context: GraphContext<typeof TestSchema>) => {
|
694
|
+
context.value = 1;
|
559
695
|
},
|
560
|
-
|
696
|
+
waitForEvent: true,
|
697
|
+
next: ["finalNode"],
|
561
698
|
};
|
562
699
|
|
563
|
-
const
|
564
|
-
name: "
|
565
|
-
execute: async () => {
|
566
|
-
|
567
|
-
return (context.value ?? 0) < 5 ? ["cycle"] : [];
|
700
|
+
const finalNode: Node<TestSchema> = {
|
701
|
+
name: "finalNode",
|
702
|
+
execute: async (context: GraphContext<typeof TestSchema>) => {
|
703
|
+
context.value = (context.value ?? 0) + 5;
|
568
704
|
},
|
569
705
|
};
|
570
706
|
|
571
|
-
[
|
707
|
+
[waitingNode, finalNode].forEach((node) => graph.addNode(node));
|
708
|
+
|
709
|
+
const resultPromise = graph.execute("waitingNode");
|
710
|
+
|
711
|
+
// Wait a bit to ensure the node is ready
|
712
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
572
713
|
|
573
|
-
|
714
|
+
// Emit the event
|
715
|
+
await graph.emit("someEvent");
|
574
716
|
|
575
|
-
|
576
|
-
expect(
|
717
|
+
const result = await resultPromise;
|
718
|
+
expect(result.value).to.equal(6); // 1 (waitingNode) + 5 (finalNode)
|
577
719
|
});
|
578
720
|
});
|