@ai.ntellect/core 0.5.0 → 0.6.1

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.
Files changed (131) hide show
  1. package/.mocharc.json +1 -1
  2. package/README.md +311 -272
  3. package/create-llm-to-select-multiple-graph copy.ts +243 -0
  4. package/create-llm-to-select-multiple-graph.ts +148 -0
  5. package/dist/graph/controller.js +63 -0
  6. package/dist/graph/engine.js +563 -0
  7. package/dist/index.js +6 -6
  8. package/dist/memory/adapters/meilisearch/index.js +249 -0
  9. package/dist/memory/adapters/redis/index.js +96 -0
  10. package/dist/memory/index.js +9 -0
  11. package/dist/services/agenda.js +115 -0
  12. package/dist/services/embedding.js +40 -0
  13. package/dist/services/queue.js +99 -103
  14. package/dist/test/graph/controller.test.js +170 -0
  15. package/dist/test/graph/engine.test.js +465 -0
  16. package/dist/test/memory/adapters/meilisearch.test.js +250 -0
  17. package/dist/test/memory/adapters/redis.test.js +143 -0
  18. package/dist/test/memory/base.test.js +209 -0
  19. package/dist/test/services/agenda.test.js +230 -0
  20. package/dist/test/services/queue.test.js +258 -0
  21. package/dist/types/index.js +2 -0
  22. package/dist/utils/generate-object.js +32 -11
  23. package/dist/utils/inject-actions.js +2 -2
  24. package/dist/utils/queue-item-transformer.js +2 -2
  25. package/dist/utils/state-manager.js +20 -0
  26. package/graph/controller.ts +64 -0
  27. package/graph/engine.ts +790 -0
  28. package/index copy.ts +81 -0
  29. package/index.ts +7 -7
  30. package/interfaces/index.ts +119 -0
  31. package/memory/adapters/meilisearch/index.ts +286 -0
  32. package/memory/adapters/redis/index.ts +103 -0
  33. package/memory/index.ts +22 -0
  34. package/package.json +7 -2
  35. package/services/agenda.ts +48 -43
  36. package/services/embedding.ts +26 -0
  37. package/services/queue.ts +2 -29
  38. package/test/.env.test +4 -0
  39. package/test/graph/controller.test.ts +186 -0
  40. package/test/graph/engine.test.ts +546 -0
  41. package/test/memory/adapters/meilisearch.test.ts +297 -0
  42. package/test/memory/adapters/redis.test.ts +160 -0
  43. package/test/memory/base.test.ts +229 -0
  44. package/test/services/agenda.test.ts +280 -0
  45. package/test/services/queue.test.ts +286 -44
  46. package/tsconfig.json +10 -10
  47. package/types/index.ts +278 -0
  48. package/utils/queue-item-transformer.ts +8 -11
  49. package/utils/setup-graphs.ts +45 -0
  50. package/utils/stringifiy-zod-schema.ts +45 -0
  51. package/.nvmrc +0 -1
  52. package/README.FR.md +0 -916
  53. package/agent/index.ts +0 -151
  54. package/agent/workflow/conditions.ts +0 -16
  55. package/agent/workflow/handlers/interpreter.handler.ts +0 -48
  56. package/agent/workflow/handlers/memory.handler.ts +0 -106
  57. package/agent/workflow/handlers/orchestrator.handler.ts +0 -23
  58. package/agent/workflow/handlers/queue.handler.ts +0 -34
  59. package/agent/workflow/handlers/scheduler.handler.ts +0 -61
  60. package/agent/workflow/index.ts +0 -62
  61. package/dist/agent/index.d.ts +0 -38
  62. package/dist/agent/index.js +0 -143
  63. package/dist/agent/tools/get-rss.d.ts +0 -16
  64. package/dist/agent/tools/get-rss.js +0 -62
  65. package/dist/bull.d.ts +0 -1
  66. package/dist/bull.js +0 -9
  67. package/dist/examples/index.d.ts +0 -2
  68. package/dist/examples/index.js +0 -89
  69. package/dist/index.d.ts +0 -7
  70. package/dist/llm/interpreter/context.d.ts +0 -15
  71. package/dist/llm/interpreter/context.js +0 -89
  72. package/dist/llm/interpreter/index.d.ts +0 -21
  73. package/dist/llm/interpreter/index.js +0 -87
  74. package/dist/llm/memory-manager/context.d.ts +0 -2
  75. package/dist/llm/memory-manager/context.js +0 -22
  76. package/dist/llm/memory-manager/index.d.ts +0 -17
  77. package/dist/llm/memory-manager/index.js +0 -107
  78. package/dist/llm/orchestrator/context.d.ts +0 -2
  79. package/dist/llm/orchestrator/context.js +0 -23
  80. package/dist/llm/orchestrator/index.d.ts +0 -44
  81. package/dist/llm/orchestrator/index.js +0 -139
  82. package/dist/llm/orchestrator/types.d.ts +0 -12
  83. package/dist/memory/cache.d.ts +0 -22
  84. package/dist/memory/cache.js +0 -165
  85. package/dist/memory/persistent.d.ts +0 -57
  86. package/dist/memory/persistent.js +0 -189
  87. package/dist/services/queue.d.ts +0 -13
  88. package/dist/services/redis-cache.d.ts +0 -37
  89. package/dist/services/redis-cache.js +0 -93
  90. package/dist/services/scheduler.d.ts +0 -40
  91. package/dist/services/scheduler.js +0 -99
  92. package/dist/services/telegram-monitor.d.ts +0 -0
  93. package/dist/services/telegram-monitor.js +0 -118
  94. package/dist/t.d.ts +0 -46
  95. package/dist/t.js +0 -102
  96. package/dist/test.d.ts +0 -0
  97. package/dist/test.js +0 -438
  98. package/dist/types.d.ts +0 -258
  99. package/dist/types.js +0 -22
  100. package/dist/utils/generate-object.d.ts +0 -12
  101. package/dist/utils/header-builder.d.ts +0 -11
  102. package/dist/utils/inject-actions.d.ts +0 -2
  103. package/dist/utils/queue-item-transformer.d.ts +0 -7
  104. package/dist/utils/sanitize-results.d.ts +0 -17
  105. package/dist/utils/schema-generator.d.ts +0 -16
  106. package/examples/actions/get-rss.ts +0 -71
  107. package/examples/index.ts +0 -98
  108. package/index.html +0 -42
  109. package/llm/dynamic-condition/example.ts +0 -36
  110. package/llm/dynamic-condition/index.ts +0 -108
  111. package/llm/interpreter/context.ts +0 -94
  112. package/llm/interpreter/index.ts +0 -140
  113. package/llm/memory-manager/context.ts +0 -19
  114. package/llm/memory-manager/index.ts +0 -115
  115. package/llm/orchestrator/context.ts +0 -19
  116. package/llm/orchestrator/index.ts +0 -192
  117. package/llm/orchestrator/types.ts +0 -14
  118. package/memory/cache.ts +0 -221
  119. package/memory/persistent.ts +0 -265
  120. package/script.js +0 -167
  121. package/services/cache.ts +0 -298
  122. package/services/telegram-monitor.ts +0 -138
  123. package/services/workflow.ts +0 -491
  124. package/t.py +0 -79
  125. package/t.ts +0 -25
  126. package/test/llm/orchestrator.test.ts +0 -47
  127. package/test/llm/synthesizer.test.ts +0 -31
  128. package/types.ts +0 -367
  129. package/utils/schema-generator.ts +0 -73
  130. package/utils/state-manager.ts +0 -25
  131. /package/dist/{llm/orchestrator/types.js → interfaces/index.js} +0 -0
@@ -0,0 +1,546 @@
1
+ import { GraphEngine } from "@/graph/engine";
2
+ import { Persistence, RealTimeNotifier } from "@/interfaces";
3
+ import { GraphDefinition, SharedState } from "@/types";
4
+ import { expect } from "chai";
5
+ import { z } from "zod";
6
+
7
+ /**
8
+ * Test suite for the Graph service
9
+ * This suite tests the workflow execution engine that manages state transitions and node execution
10
+ */
11
+ describe("Graph", () => {
12
+ /**
13
+ * Test schema definition using Zod
14
+ * Defines the structure and validation rules for the workflow state
15
+ */
16
+ const TestSchema = z.object({
17
+ status: z.string(),
18
+ step: z.number(),
19
+ });
20
+
21
+ type TestState = z.infer<typeof TestSchema>;
22
+
23
+ let graph: GraphEngine<TestState>;
24
+ /**
25
+ * Test definition of a simple workflow graph
26
+ * Contains 3 nodes: start -> process -> end
27
+ * Each node updates the state with new status and step values
28
+ */
29
+ const testDefinition: GraphDefinition<TestState> = {
30
+ name: "simple-workflow",
31
+ entryNode: "start",
32
+ nodes: {
33
+ start: {
34
+ name: "start",
35
+ description: "Starting node",
36
+ execute: async (_params: any, state: SharedState<TestState>) => {
37
+ return graph.updateState({
38
+ ...state.context,
39
+ status: "started",
40
+ step: 1,
41
+ });
42
+ },
43
+ relationships: [{ name: "process" }],
44
+ },
45
+ process: {
46
+ name: "process",
47
+ description: "Processing node",
48
+ execute: async (_params: any, state: SharedState<TestState>) => {
49
+ return graph.updateState({
50
+ ...state.context,
51
+ status: "processing",
52
+ step: 2,
53
+ });
54
+ },
55
+ condition: (state) => state.context.step === 1,
56
+ relationships: [{ name: "end" }],
57
+ },
58
+ end: {
59
+ name: "end",
60
+ description: "End node",
61
+ execute: async (_params: any, state: SharedState<TestState>) => {
62
+ return graph.updateState({
63
+ ...state.context,
64
+ status: "completed",
65
+ step: 3,
66
+ });
67
+ },
68
+ relationships: [],
69
+ },
70
+ },
71
+ schema: TestSchema,
72
+ };
73
+
74
+ beforeEach(() => {
75
+ graph = new GraphEngine(testDefinition);
76
+ });
77
+
78
+ describe("Workflow Execution", () => {
79
+ /**
80
+ * Tests the complete execution flow of the workflow
81
+ * Verifies that state transitions occur correctly from start to end
82
+ */
83
+ it("should execute the complete workflow sequence", async () => {
84
+ const initialState: SharedState<TestState> = {
85
+ context: {
86
+ status: "init",
87
+ step: 0,
88
+ },
89
+ };
90
+
91
+ // Initialiser le graph avec l'état initial
92
+ graph = new GraphEngine(testDefinition, {
93
+ schema: TestSchema,
94
+ initialState,
95
+ });
96
+
97
+ // Exécuter le workflow
98
+ await graph.execute(initialState, "start");
99
+ const result = graph.getState();
100
+
101
+ expect(result.context).to.deep.equal({
102
+ status: "completed",
103
+ step: 3,
104
+ });
105
+ });
106
+
107
+ /**
108
+ * Tests that conditional logic in nodes is respected
109
+ * The process node should only execute when step === 1
110
+ */
111
+ it("should respect conditions in workflow", async () => {
112
+ const initialState: SharedState<TestState> = {
113
+ context: {
114
+ status: "init",
115
+ step: 2,
116
+ },
117
+ };
118
+
119
+ // Initialiser le graph avec l'état initial
120
+ graph = new GraphEngine(testDefinition, {
121
+ schema: TestSchema,
122
+ initialState,
123
+ });
124
+
125
+ await graph.execute(initialState, "process");
126
+ const result = graph.getState();
127
+
128
+ expect(result.context).to.deep.equal({
129
+ status: "init",
130
+ step: 2,
131
+ });
132
+ });
133
+ });
134
+
135
+ describe("Graph Management", () => {
136
+ it("should add a new node to the graph", () => {
137
+ const newNode = {
138
+ name: "new-node",
139
+ description: "A new test node",
140
+ execute: async (_params: any, state: SharedState<TestState>) => {
141
+ return graph.updateState({
142
+ ...state.context,
143
+ status: "new",
144
+ step: 4,
145
+ });
146
+ },
147
+ relationships: [{ name: "end" }],
148
+ };
149
+
150
+ graph.addNode(newNode);
151
+
152
+ expect(graph.nodes.has("new-node")).to.be.true;
153
+ const addedNode = graph.nodes.get("new-node");
154
+ expect(addedNode?.relationships).to.have.lengthOf(1);
155
+ });
156
+
157
+ it("should update existing graph with new definition", () => {
158
+ const newDefinition: GraphDefinition<TestState> = {
159
+ name: "updated-workflow",
160
+ entryNode: "start",
161
+ nodes: {
162
+ ...testDefinition.nodes,
163
+ "new-step": {
164
+ name: "new-step",
165
+ description: "New step node",
166
+ execute: async (_params: any, state: SharedState<TestState>) => {
167
+ return graph.updateState({
168
+ ...state.context,
169
+ status: "new-step",
170
+ step: 4,
171
+ });
172
+ },
173
+ relationships: [],
174
+ },
175
+ },
176
+ schema: TestSchema,
177
+ };
178
+
179
+ graph.updateGraph(newDefinition);
180
+ expect(graph.nodes.has("new-step")).to.be.true;
181
+ });
182
+ });
183
+
184
+ describe("State Management", () => {
185
+ it("should properly update and retrieve state", async () => {
186
+ const newState: SharedState<TestState> = {
187
+ context: {
188
+ status: "test",
189
+ step: 5,
190
+ },
191
+ };
192
+
193
+ graph.setState(newState);
194
+ const retrievedState = graph.getState();
195
+
196
+ expect(retrievedState.context).to.deep.equal(newState.context);
197
+ });
198
+
199
+ it("should merge states correctly when updating partially", () => {
200
+ const initialState: SharedState<TestState> = {
201
+ context: {
202
+ status: "initial",
203
+ step: 1,
204
+ },
205
+ };
206
+
207
+ graph.setState(initialState);
208
+
209
+ const partialUpdate = {
210
+ status: "updated",
211
+ };
212
+
213
+ const updatedState = graph.updateState(partialUpdate);
214
+
215
+ expect(updatedState.context).to.deep.equal({
216
+ status: "updated",
217
+ step: 1,
218
+ });
219
+ });
220
+ });
221
+
222
+ describe("Error Handling", () => {
223
+ it("should handle execution errors gracefully", async () => {
224
+ const errorNode = {
225
+ name: "error-node",
226
+ execute: async () => {
227
+ throw new Error("Test error");
228
+ },
229
+ };
230
+
231
+ graph.addNode(errorNode);
232
+
233
+ let errorCaught = false;
234
+ try {
235
+ await graph.execute(
236
+ { context: { status: "test", step: 1 } },
237
+ "error-node",
238
+ undefined,
239
+ (error) => {
240
+ expect(error.message).to.equal("Test error");
241
+ errorCaught = true;
242
+ }
243
+ );
244
+ } catch (error) {
245
+ expect(error).to.be.instanceOf(Error);
246
+ expect((error as Error).message).to.equal("Test error");
247
+ }
248
+
249
+ expect(errorCaught).to.be.true;
250
+ });
251
+
252
+ it("should validate state against schema", async () => {
253
+ // Créer un nouveau graph avec validation stricte
254
+ const strictGraph = new GraphEngine(testDefinition, {
255
+ schema: TestSchema,
256
+ initialState: {
257
+ context: {
258
+ status: "init",
259
+ step: 0,
260
+ },
261
+ },
262
+ });
263
+
264
+ const invalidState: SharedState<any> = {
265
+ context: {
266
+ status: 123, // Should be string
267
+ step: "invalid", // Should be number
268
+ },
269
+ };
270
+
271
+ try {
272
+ await strictGraph.execute(invalidState, "start");
273
+ // Si on arrive ici, le test doit échouer car on s'attend à une erreur
274
+ expect.fail("Expected validation error but none was thrown");
275
+ } catch (error) {
276
+ expect(error).to.be.instanceOf(Error);
277
+ const errorMessage = (error as Error).message;
278
+ expect(
279
+ errorMessage.includes("Expected string") ||
280
+ errorMessage.includes("Expected number") ||
281
+ errorMessage.includes("validation")
282
+ ).to.be.true;
283
+ }
284
+ });
285
+ });
286
+
287
+ describe("Parallel Execution", () => {
288
+ /**
289
+ * Tests concurrent execution of multiple nodes
290
+ * Important: The execution order is not guaranteed due to async nature
291
+ * @param concurrency - Maximum number of nodes that can execute simultaneously
292
+ */
293
+ it("should execute multiple nodes in parallel", async () => {
294
+ const executionOrder: string[] = [];
295
+
296
+ const parallelNodes = ["node1", "node2", "node3"].map((name) => ({
297
+ name,
298
+ execute: async (_params: any, state: SharedState<TestState>) => {
299
+ executionOrder.push(name);
300
+ await new Promise((resolve) => setTimeout(resolve, 10));
301
+ return state;
302
+ },
303
+ }));
304
+
305
+ parallelNodes.forEach((node) => {
306
+ graph.addNode(node);
307
+ });
308
+
309
+ await graph.executeParallel(
310
+ { context: { status: "test", step: 1 } },
311
+ ["node1", "node2", "node3"],
312
+ 2
313
+ );
314
+
315
+ expect(executionOrder).to.have.lengthOf(3);
316
+ expect(executionOrder).to.include.members(["node1", "node2", "node3"]);
317
+ });
318
+ });
319
+
320
+ describe("Event Handling", () => {
321
+ /**
322
+ * Tests the event emission and handling system
323
+ * Events can trigger node execution asynchronously
324
+ * Note: Uses setTimeout to ensure event processing completes
325
+ */
326
+ it("should emit and handle events correctly", async () => {
327
+ const eventNode = {
328
+ name: "event-node",
329
+ execute: async (_params: any, state: SharedState<TestState>) => {
330
+ return graph.updateState({
331
+ ...state.context,
332
+ status: "event-triggered",
333
+ step: 10,
334
+ });
335
+ },
336
+ events: ["test-event"],
337
+ };
338
+
339
+ graph.addNode(eventNode);
340
+
341
+ // Émettre l'événement
342
+ graph.emit("test-event", {
343
+ state: { context: { status: "init", step: 0 } },
344
+ });
345
+
346
+ // Attendre un peu pour que l'événement soit traité
347
+ await new Promise((resolve) => setTimeout(resolve, 50));
348
+
349
+ const state = graph.getState();
350
+ expect(state.context.status).to.equal("event-triggered");
351
+ expect(state.context.status).to.equal("event-triggered");
352
+ });
353
+ });
354
+
355
+ describe("Subgraph Integration", () => {
356
+ /**
357
+ * Tests nested workflow execution through subgraphs
358
+ * Subgraphs allow modular workflow composition
359
+ * The main graph can delegate execution to subgraphs
360
+ */
361
+ it("should execute subgraph as part of main graph", async () => {
362
+ const subGraphDef: GraphDefinition<TestState> = {
363
+ name: "sub-workflow",
364
+ entryNode: "sub-start",
365
+ nodes: {
366
+ "sub-start": {
367
+ name: "sub-start",
368
+ execute: async (_params: any, state: SharedState<TestState>) => {
369
+ return graph.updateState({
370
+ ...state.context,
371
+ status: "sub-completed",
372
+ step: 100,
373
+ });
374
+ },
375
+ relationships: [],
376
+ },
377
+ },
378
+ schema: TestSchema,
379
+ };
380
+
381
+ const subGraph = new GraphEngine(subGraphDef);
382
+ graph.addSubGraph(subGraph, "sub-start", "sub-workflow");
383
+
384
+ const initialState: SharedState<TestState> = {
385
+ context: {
386
+ status: "init",
387
+ step: 0,
388
+ },
389
+ };
390
+
391
+ await graph.execute(initialState, "sub-workflow");
392
+ const state = graph.getState();
393
+
394
+ expect(state.context.status).to.equal("sub-completed");
395
+ expect(state.context.step).to.equal(100);
396
+ });
397
+ });
398
+
399
+ describe("Global Context Management", () => {
400
+ it("should manage global context correctly", () => {
401
+ graph.addToContext("testKey", "testValue");
402
+ expect(graph.getContext("testKey")).to.equal("testValue");
403
+
404
+ graph.removeFromContext("testKey");
405
+ expect(graph.getContext("testKey")).to.be.undefined;
406
+ });
407
+
408
+ it("should handle multiple context values", () => {
409
+ graph.addToContext("key1", "value1");
410
+ graph.addToContext("key2", { nested: "value2" });
411
+
412
+ expect(graph.getContext("key1")).to.equal("value1");
413
+ expect(graph.getContext("key2")).to.deep.equal({ nested: "value2" });
414
+ });
415
+ });
416
+
417
+ describe("Graph Visualization", () => {
418
+ it("should generate valid mermaid diagram", () => {
419
+ const diagram = graph.generateMermaidDiagram("Test Workflow");
420
+ expect(diagram).to.include("flowchart TD");
421
+ expect(diagram).to.include("subgraph Test Workflow");
422
+ expect(diagram).to.include("start");
423
+ expect(diagram).to.include("process");
424
+ expect(diagram).to.include("end");
425
+ });
426
+ });
427
+
428
+ describe("Schema Visualization", () => {
429
+ /**
430
+ * Tests the schema visualization functionality
431
+ * This helps developers understand the workflow structure
432
+ * The visualization includes:
433
+ * - Node relationships
434
+ * - State schema
435
+ * - Validation rules
436
+ */
437
+ it("should generate schema visualization", () => {
438
+ // Créer un nouveau graph avec un schéma pour le test
439
+ const graphWithSchema = new GraphEngine(testDefinition, {
440
+ schema: TestSchema,
441
+ });
442
+
443
+ const schemaVisualization = graphWithSchema.visualizeSchema();
444
+
445
+ // Vérifier les sections attendues dans la visualisation
446
+ expect(schemaVisualization).to.include("📋 Graph:");
447
+ expect(schemaVisualization).to.include("🔷 Nodes:");
448
+
449
+ // Vérifier les détails du schéma
450
+ expect(schemaVisualization).to.satisfy((text: string) => {
451
+ return text.includes("status:") && text.includes("step:");
452
+ });
453
+
454
+ // Vérifier la présence des nœuds
455
+ expect(schemaVisualization).to.include("start");
456
+ expect(schemaVisualization).to.include("process");
457
+ expect(schemaVisualization).to.include("end");
458
+ });
459
+ });
460
+
461
+ describe("Persistence Integration", () => {
462
+ it("should work with persistence layer", async () => {
463
+ const mockPersistence: Persistence<TestState> = {
464
+ saveState: async (graphName, state, currentNode) => {
465
+ expect(graphName).to.equal("simple-workflow");
466
+ expect(state.context).to.exist;
467
+ expect(currentNode).to.exist;
468
+ },
469
+ loadState: async () => null,
470
+ };
471
+
472
+ graph.setPersistence(mockPersistence);
473
+ await graph.execute({ context: { status: "init", step: 0 } }, "start");
474
+ });
475
+ });
476
+
477
+ describe("Real-time Notifications", () => {
478
+ /**
479
+ * Tests the notification system during workflow execution
480
+ * Notifications are sent for:
481
+ * - Node execution start
482
+ * - Node execution completion
483
+ * - State updates
484
+ * - Error events
485
+ */
486
+ it("should send notifications during execution", async () => {
487
+ const notifications: any[] = [];
488
+ const mockNotifier: RealTimeNotifier = {
489
+ notify: (event, data) => {
490
+ notifications.push({ event, data });
491
+ },
492
+ };
493
+
494
+ graph.setNotifier(mockNotifier);
495
+ await graph.execute({ context: { status: "init", step: 0 } }, "start");
496
+
497
+ expect(notifications).to.have.length.greaterThan(0);
498
+ expect(notifications[0].event).to.equal("nodeExecutionStarted");
499
+ expect(notifications).to.deep.include.members([
500
+ {
501
+ event: "nodeExecutionCompleted",
502
+ data: {
503
+ workflow: "simple-workflow",
504
+ node: "start",
505
+ state: {
506
+ context: {
507
+ status: "started",
508
+ step: 1,
509
+ },
510
+ },
511
+ },
512
+ },
513
+ ]);
514
+ });
515
+ });
516
+
517
+ describe("Cycle Detection", () => {
518
+ /**
519
+ * Tests the cycle detection mechanism
520
+ * Cycles in workflow definitions can cause infinite loops
521
+ * The graph constructor should detect and prevent cyclic dependencies
522
+ */
523
+ it("should detect cycles in graph", () => {
524
+ const cyclicDefinition: GraphDefinition<TestState> = {
525
+ name: "cyclic-workflow",
526
+ entryNode: "node1",
527
+ nodes: {
528
+ node1: {
529
+ name: "node1",
530
+ execute: async (state) => state,
531
+ relationships: [{ name: "node2" }],
532
+ },
533
+ node2: {
534
+ name: "node2",
535
+ execute: async (state) => state,
536
+ relationships: [{ name: "node1" }],
537
+ },
538
+ },
539
+ };
540
+
541
+ expect(
542
+ () => new GraphEngine(cyclicDefinition, { autoDetectCycles: true })
543
+ ).to.throw;
544
+ });
545
+ });
546
+ });