@ai.ntellect/core 0.6.21 → 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 +61 -85
- package/graph/controller.ts +1 -1
- package/graph/event-manager.ts +288 -0
- package/graph/index.ts +153 -367
- 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/package.json +10 -5
- package/test/graph/index.test.ts +244 -113
- 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/dist/graph/controller.js +0 -72
- package/dist/graph/index.js +0 -501
- 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 -43
- package/dist/utils/header-builder.js +0 -34
- 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/graph/index.ts
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
import { EventEmitter } from "events";
|
2
|
+
import { BehaviorSubject, Subject } from "rxjs";
|
2
3
|
import { ZodSchema } from "zod";
|
3
|
-
import {
|
4
|
-
import {
|
4
|
+
import { GraphObservable, IEventEmitter } from "../interfaces";
|
5
|
+
import { GraphContext, GraphDefinition, GraphEvent, Node } from "../types";
|
6
|
+
import { GraphEventManager } from "./event-manager";
|
7
|
+
import { GraphLogger } from "./logger";
|
8
|
+
import { GraphNode } from "./node";
|
9
|
+
import { GraphObserver } from "./observer";
|
5
10
|
|
6
11
|
/**
|
7
12
|
* @module GraphFlow
|
@@ -23,10 +28,18 @@ export class GraphFlow<T extends ZodSchema> {
|
|
23
28
|
private globalErrorHandler?: (error: Error, context: GraphContext<T>) => void;
|
24
29
|
private graphEvents?: string[];
|
25
30
|
private entryNode?: string;
|
26
|
-
private logs: string[] = [];
|
27
31
|
private verbose: boolean = false;
|
28
32
|
public nodes: Map<string, Node<T, any>>;
|
29
33
|
|
34
|
+
private eventSubject: Subject<GraphEvent<T>> = new Subject();
|
35
|
+
private stateSubject: BehaviorSubject<GraphContext<T>>;
|
36
|
+
private destroySubject: Subject<void> = new Subject();
|
37
|
+
|
38
|
+
public observer: GraphObserver<T>;
|
39
|
+
private logger: GraphLogger;
|
40
|
+
private eventManager: GraphEventManager<T>;
|
41
|
+
private nodeExecutor: GraphNode<T>;
|
42
|
+
|
30
43
|
/**
|
31
44
|
* Creates a new instance of GraphFlow
|
32
45
|
* @param {string} name - The name of the graph flow
|
@@ -49,17 +62,36 @@ export class GraphFlow<T extends ZodSchema> {
|
|
49
62
|
this.graphEvents = config.events;
|
50
63
|
this.verbose = options.verbose ?? false;
|
51
64
|
|
65
|
+
this.stateSubject = new BehaviorSubject<GraphContext<T>>(this.context);
|
66
|
+
|
67
|
+
this.logger = new GraphLogger(name, options.verbose);
|
68
|
+
this.eventManager = new GraphEventManager(
|
69
|
+
this.eventEmitter,
|
70
|
+
this.nodes,
|
71
|
+
name,
|
72
|
+
this.context,
|
73
|
+
config.events,
|
74
|
+
config.entryNode,
|
75
|
+
config.onError
|
76
|
+
);
|
77
|
+
this.nodeExecutor = new GraphNode(
|
78
|
+
this.nodes,
|
79
|
+
this.logger,
|
80
|
+
this.eventManager,
|
81
|
+
this.eventSubject,
|
82
|
+
this.stateSubject
|
83
|
+
);
|
84
|
+
|
85
|
+
this.setupEventStreams();
|
52
86
|
this.setupEventListeners();
|
53
87
|
this.setupGraphEventListeners();
|
54
|
-
}
|
55
88
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
return structuredClone(this.context);
|
89
|
+
this.observer = new GraphObserver(
|
90
|
+
this,
|
91
|
+
this.eventSubject,
|
92
|
+
this.stateSubject,
|
93
|
+
this.destroySubject
|
94
|
+
);
|
63
95
|
}
|
64
96
|
|
65
97
|
/**
|
@@ -67,77 +99,49 @@ export class GraphFlow<T extends ZodSchema> {
|
|
67
99
|
* @private
|
68
100
|
* @description Attaches all node-based event triggers while preserving external listeners
|
69
101
|
*/
|
70
|
-
private
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
102
|
+
private setupEventStreams(): void {
|
103
|
+
this.eventManager.on("nodeStarted", (data) => {
|
104
|
+
this.addLog(`Event: Node "${data.name}" started`);
|
105
|
+
});
|
106
|
+
|
107
|
+
this.eventManager.on("nodeCompleted", (data) => {
|
108
|
+
this.addLog(`Event: Node "${data.name}" completed`);
|
109
|
+
});
|
110
|
+
|
111
|
+
this.eventManager.on("nodeError", (data) => {
|
112
|
+
let errorMessage = "Unknown error";
|
113
|
+
if (data.error) {
|
114
|
+
errorMessage =
|
115
|
+
data.error instanceof Error
|
116
|
+
? data.error.message
|
117
|
+
: data.error.errors?.[0]?.message ||
|
118
|
+
data.error.message ||
|
119
|
+
"Unknown error";
|
88
120
|
}
|
89
|
-
|
90
|
-
|
91
|
-
// remove only those events that are used by nodes
|
92
|
-
this.eventEmitter.removeAllListeners(evt);
|
93
|
-
}
|
121
|
+
this.addLog(`Event: Node "${data.name}" error: ${errorMessage}`);
|
122
|
+
});
|
94
123
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
node.events.forEach((event) => {
|
99
|
-
this.eventEmitter.on(
|
100
|
-
event,
|
101
|
-
async (data?: Partial<GraphContext<T>>) => {
|
102
|
-
const freshContext = this.createNewContext();
|
103
|
-
if (data) Object.assign(freshContext, data);
|
104
|
-
|
105
|
-
// If triggered by an event, we pass "true" so event-driven node will skip `next`.
|
106
|
-
await this.executeNode(
|
107
|
-
node.name,
|
108
|
-
freshContext,
|
109
|
-
undefined,
|
110
|
-
/* triggeredByEvent= */ true
|
111
|
-
);
|
112
|
-
}
|
113
|
-
);
|
114
|
-
});
|
115
|
-
}
|
116
|
-
}
|
117
|
-
}
|
118
|
-
|
119
|
-
private addLog(message: string): void {
|
120
|
-
const logMessage = `[${new Date().toISOString()}] ${message}`;
|
121
|
-
this.logs.push(logMessage);
|
122
|
-
if (this.verbose) {
|
123
|
-
console.log(`[${this.name}] ${message}`);
|
124
|
-
}
|
124
|
+
this.eventManager.on("nodeStateChanged", (data) => {
|
125
|
+
this.addLog(`Event: Node "${data.name}" state changed`);
|
126
|
+
});
|
125
127
|
}
|
126
128
|
|
127
129
|
/**
|
128
|
-
*
|
129
|
-
* @
|
130
|
+
* Sets up event listeners for node-based events
|
131
|
+
* @private
|
132
|
+
* @description Attaches all node-based event triggers while preserving external listeners
|
130
133
|
*/
|
131
|
-
|
132
|
-
this.
|
134
|
+
private setupEventListeners(): void {
|
135
|
+
this.eventManager.setupEventListeners();
|
133
136
|
}
|
134
137
|
|
135
138
|
/**
|
136
|
-
*
|
137
|
-
* @
|
139
|
+
* Sets up event listeners for graph-based events
|
140
|
+
* @private
|
141
|
+
* @description Attaches all graph-based event triggers
|
138
142
|
*/
|
139
|
-
|
140
|
-
|
143
|
+
private setupGraphEventListeners(): void {
|
144
|
+
this.eventManager.setupGraphEventListeners();
|
141
145
|
}
|
142
146
|
|
143
147
|
/**
|
@@ -155,198 +159,58 @@ export class GraphFlow<T extends ZodSchema> {
|
|
155
159
|
inputs: any,
|
156
160
|
triggeredByEvent: boolean = false
|
157
161
|
): Promise<void> {
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
const localContext = structuredClone(context);
|
166
|
-
|
167
|
-
if (node.condition && !node.condition(localContext)) {
|
168
|
-
this.addLog(`⏭️ Skipping node "${nodeName}" - condition not met`);
|
169
|
-
return;
|
170
|
-
}
|
171
|
-
|
172
|
-
// Validate inputs
|
173
|
-
if (node.inputs) {
|
174
|
-
if (!inputs) {
|
175
|
-
this.addLog(`❌ Missing required inputs for node "${nodeName}"`);
|
176
|
-
throw new Error(`Inputs required for node "${nodeName}"`);
|
177
|
-
}
|
178
|
-
this.addLog(`📥 Validating inputs for node "${nodeName}"`);
|
179
|
-
inputs = node.inputs.parse(inputs);
|
180
|
-
}
|
181
|
-
|
182
|
-
// Handle retry logic
|
183
|
-
if (node.retry && node.retry.maxAttempts > 0) {
|
184
|
-
let attempts = 0;
|
185
|
-
let lastError: Error | null = null;
|
186
|
-
|
187
|
-
while (attempts < node.retry.maxAttempts) {
|
188
|
-
try {
|
189
|
-
this.addLog(`🔄 Attempt ${attempts + 1}/${node.retry.maxAttempts}`);
|
190
|
-
await node.execute(localContext, inputs);
|
191
|
-
lastError = null;
|
192
|
-
break;
|
193
|
-
} catch (error: any) {
|
194
|
-
lastError = error as Error;
|
195
|
-
attempts++;
|
196
|
-
this.addLog(`❌ Attempt ${attempts} failed: ${error.message}`);
|
197
|
-
|
198
|
-
if (attempts === node.retry.maxAttempts) {
|
199
|
-
// Si toutes les tentatives ont échoué et qu'il y a un gestionnaire d'échec
|
200
|
-
if (node.retry.onRetryFailed) {
|
201
|
-
this.addLog(
|
202
|
-
`🔄 Executing retry failure handler for node "${nodeName}"`
|
203
|
-
);
|
204
|
-
try {
|
205
|
-
await node.retry.onRetryFailed(lastError, localContext);
|
206
|
-
// Si le gestionnaire d'échec réussit, on continue l'exécution
|
207
|
-
// SEULEMENT si le gestionnaire a explicitement retourné true
|
208
|
-
if (node.retry.continueOnFailed) {
|
209
|
-
this.addLog(
|
210
|
-
`✅ Retry failure handler succeeded for node "${nodeName}" - continuing execution`
|
211
|
-
);
|
212
|
-
break;
|
213
|
-
} else {
|
214
|
-
this.addLog(
|
215
|
-
`⚠️ Retry failure handler executed but node "${nodeName}" will still fail`
|
216
|
-
);
|
217
|
-
throw lastError;
|
218
|
-
}
|
219
|
-
} catch (handlerError: any) {
|
220
|
-
this.addLog(
|
221
|
-
`❌ Retry failure handler failed for node "${nodeName}": ${handlerError.message}`
|
222
|
-
);
|
223
|
-
throw handlerError;
|
224
|
-
}
|
225
|
-
}
|
226
|
-
// Si pas de gestionnaire d'échec ou si le gestionnaire a échoué
|
227
|
-
throw lastError;
|
228
|
-
}
|
229
|
-
|
230
|
-
if (attempts < node.retry.maxAttempts) {
|
231
|
-
this.addLog(
|
232
|
-
`⏳ Waiting ${node.retry.delay}ms before next attempt`
|
233
|
-
);
|
234
|
-
await new Promise((resolve) =>
|
235
|
-
setTimeout(resolve, node.retry?.delay || 0)
|
236
|
-
);
|
237
|
-
}
|
238
|
-
}
|
239
|
-
}
|
240
|
-
} else {
|
241
|
-
await node.execute(localContext, inputs);
|
242
|
-
}
|
243
|
-
|
244
|
-
// Validate outputs
|
245
|
-
if (node.outputs) {
|
246
|
-
this.addLog(`📤 Validating outputs for node "${nodeName}"`);
|
247
|
-
node.outputs.parse(localContext);
|
248
|
-
}
|
249
|
-
|
250
|
-
Object.assign(context, localContext);
|
251
|
-
|
252
|
-
this.addLog(
|
253
|
-
`✅ Node "${nodeName}" executed successfully ${JSON.stringify(context)}`
|
254
|
-
);
|
255
|
-
this.eventEmitter.emit("nodeCompleted", { name: nodeName });
|
256
|
-
|
257
|
-
// Handle waitForEvent
|
258
|
-
if (node.waitForEvent && !triggeredByEvent) {
|
259
|
-
this.addLog(
|
260
|
-
`⏳ Node "${nodeName}" waiting for events: ${node.events?.join(", ")}`
|
261
|
-
);
|
262
|
-
|
263
|
-
await new Promise<void>((resolve) => {
|
264
|
-
const eventHandler = () => {
|
265
|
-
this.addLog(`🚀 Event received for node "${nodeName}"`);
|
266
|
-
resolve();
|
267
|
-
};
|
268
|
-
|
269
|
-
node.events?.forEach((event) => {
|
270
|
-
this.eventEmitter.once(event, eventHandler);
|
271
|
-
});
|
272
|
-
});
|
162
|
+
return this.nodeExecutor.executeNode(
|
163
|
+
nodeName,
|
164
|
+
context,
|
165
|
+
inputs,
|
166
|
+
triggeredByEvent
|
167
|
+
);
|
168
|
+
}
|
273
169
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
: node.next || [];
|
278
|
-
|
279
|
-
if (nextNodes.length > 0) {
|
280
|
-
this.addLog(`➡️ Executing next nodes: ${nextNodes.join(", ")}`);
|
281
|
-
|
282
|
-
// Execute next nodes
|
283
|
-
for (const nextNodeName of nextNodes) {
|
284
|
-
this.addLog(`🔄 Starting branch for node "${nextNodeName}"`);
|
285
|
-
const nextNode = this.nodes.get(nextNodeName);
|
286
|
-
if (nextNode) {
|
287
|
-
await this.executeNode(
|
288
|
-
nextNodeName,
|
289
|
-
context,
|
290
|
-
undefined,
|
291
|
-
nextNode.waitForEvent
|
292
|
-
);
|
293
|
-
}
|
294
|
-
this.addLog(`✅ Branch "${nextNodeName}" completed`);
|
295
|
-
}
|
170
|
+
private addLog(message: string): void {
|
171
|
+
this.logger.addLog(message);
|
172
|
+
}
|
296
173
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
});
|
174
|
+
public getLogs(): string[] {
|
175
|
+
return this.logger.getLogs();
|
176
|
+
}
|
301
177
|
|
302
|
-
|
303
|
-
|
304
|
-
|
178
|
+
public clearLogs(): void {
|
179
|
+
this.logger.clearLogs();
|
180
|
+
}
|
305
181
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
undefined,
|
324
|
-
nextNode.waitForEvent
|
325
|
-
);
|
326
|
-
}
|
327
|
-
this.addLog(`✅ Branch "${nextNodeName}" completed`);
|
328
|
-
}
|
329
|
-
}
|
182
|
+
/**
|
183
|
+
* Get the observer instance for monitoring graph state and events
|
184
|
+
*/
|
185
|
+
public observe(
|
186
|
+
options: {
|
187
|
+
debounce?: number;
|
188
|
+
delay?: number;
|
189
|
+
stream?: boolean;
|
190
|
+
properties?: (keyof GraphContext<T> extends string
|
191
|
+
? keyof GraphContext<T>
|
192
|
+
: never)[];
|
193
|
+
onStreamLetter?: (data: { letter: string; property: string }) => void;
|
194
|
+
onStreamComplete?: () => void;
|
195
|
+
} = {}
|
196
|
+
): GraphObservable<T> {
|
197
|
+
return this.observer.state(options) as GraphObservable<T>;
|
198
|
+
}
|
330
199
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
}
|
200
|
+
/**
|
201
|
+
* Enable or disable verbose logging
|
202
|
+
* @param {boolean} enabled - Whether to enable verbose logging
|
203
|
+
*/
|
204
|
+
public setVerbose(enabled: boolean): void {
|
205
|
+
this.logger.setVerbose(enabled);
|
338
206
|
}
|
339
207
|
|
340
208
|
/**
|
341
|
-
*
|
342
|
-
* @
|
343
|
-
* @param {GraphContext<T>} context - Context to validate
|
344
|
-
* @throws {Error} If validation fails
|
209
|
+
* Get current verbose setting
|
210
|
+
* @returns {boolean} Current verbose setting
|
345
211
|
*/
|
346
|
-
|
347
|
-
|
348
|
-
this.validator.parse(context);
|
349
|
-
}
|
212
|
+
public isVerbose(): boolean {
|
213
|
+
return this.logger.isVerbose();
|
350
214
|
}
|
351
215
|
|
352
216
|
/**
|
@@ -356,13 +220,12 @@ export class GraphFlow<T extends ZodSchema> {
|
|
356
220
|
* @param {any} inputParams - Optional input parameters for the start node
|
357
221
|
* @returns {Promise<GraphContext<T>>} Final context after execution
|
358
222
|
*/
|
359
|
-
async execute(
|
223
|
+
public async execute(
|
360
224
|
startNode: string,
|
361
225
|
inputParams?: any,
|
362
226
|
inputContext?: Partial<GraphContext<T>>
|
363
227
|
): Promise<GraphContext<T>> {
|
364
228
|
if (inputParams) {
|
365
|
-
// Merge inputParams into context
|
366
229
|
Object.assign(this.context, inputParams);
|
367
230
|
}
|
368
231
|
|
@@ -373,7 +236,12 @@ export class GraphFlow<T extends ZodSchema> {
|
|
373
236
|
this.eventEmitter.emit("graphStarted", { name: this.name });
|
374
237
|
|
375
238
|
try {
|
376
|
-
await this.executeNode(
|
239
|
+
await this.nodeExecutor.executeNode(
|
240
|
+
startNode,
|
241
|
+
this.context,
|
242
|
+
inputParams,
|
243
|
+
false
|
244
|
+
);
|
377
245
|
|
378
246
|
this.eventEmitter.emit("graphCompleted", {
|
379
247
|
name: this.name,
|
@@ -392,31 +260,19 @@ export class GraphFlow<T extends ZodSchema> {
|
|
392
260
|
* Emits an event to trigger event-based nodes
|
393
261
|
* @param {string} eventName - Name of the event to emit
|
394
262
|
* @param {Partial<GraphContext<T>>} data - Optional data to merge with context
|
395
|
-
* @returns {Promise<
|
263
|
+
* @returns {Promise<void>}
|
396
264
|
*/
|
397
265
|
public async emit(
|
398
266
|
eventName: string,
|
399
267
|
data?: Partial<GraphContext<T>>
|
400
|
-
): Promise<
|
401
|
-
const
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
}
|
406
|
-
|
407
|
-
|
408
|
-
node.events?.includes(eventName)
|
409
|
-
);
|
410
|
-
|
411
|
-
// Exécuter les nœuds d'événements séquentiellement
|
412
|
-
for (const node of eventNodes) {
|
413
|
-
await this.executeNode(node.name, workingContext, undefined, true);
|
414
|
-
}
|
415
|
-
|
416
|
-
// Mettre à jour le contexte global
|
417
|
-
this.context = workingContext;
|
418
|
-
|
419
|
-
return this.getContext();
|
268
|
+
): Promise<void> {
|
269
|
+
const event: GraphEvent<T> = {
|
270
|
+
type: eventName,
|
271
|
+
payload: data,
|
272
|
+
timestamp: Date.now(),
|
273
|
+
};
|
274
|
+
this.eventSubject.next(event);
|
275
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
420
276
|
}
|
421
277
|
|
422
278
|
/**
|
@@ -424,15 +280,15 @@ export class GraphFlow<T extends ZodSchema> {
|
|
424
280
|
* @param {string} eventName - Name of the event to listen for
|
425
281
|
* @param {Function} handler - Handler function to execute when event is emitted
|
426
282
|
*/
|
427
|
-
on(eventName: string, handler: (...args: any[]) => void): void {
|
428
|
-
this.
|
283
|
+
public on(eventName: string, handler: (...args: any[]) => void): void {
|
284
|
+
this.eventManager.on(eventName, handler);
|
429
285
|
}
|
430
286
|
|
431
287
|
/**
|
432
288
|
* Updates the graph definition with new configuration
|
433
289
|
* @param {GraphDefinition<T>} definition - New graph definition
|
434
290
|
*/
|
435
|
-
load(definition: GraphDefinition<T>): void {
|
291
|
+
public load(definition: GraphDefinition<T>): void {
|
436
292
|
// Clear all existing nodes
|
437
293
|
this.nodes.clear();
|
438
294
|
// Wipe out old node-based event listeners
|
@@ -494,8 +350,8 @@ export class GraphFlow<T extends ZodSchema> {
|
|
494
350
|
* @param {string} message - Message to log
|
495
351
|
* @param {any} data - Optional data to log
|
496
352
|
*/
|
497
|
-
log(message: string, data?: any): void {
|
498
|
-
|
353
|
+
public log(message: string, data?: any): void {
|
354
|
+
this.logger.log(message, data);
|
499
355
|
}
|
500
356
|
|
501
357
|
/**
|
@@ -503,105 +359,35 @@ export class GraphFlow<T extends ZodSchema> {
|
|
503
359
|
* @param {Node<T>} node - Node to add
|
504
360
|
* @throws {Error} If node with same name already exists
|
505
361
|
*/
|
506
|
-
addNode(node: Node<T, any>): void {
|
362
|
+
public addNode(node: Node<T, any>): void {
|
507
363
|
this.nodes.set(node.name, node);
|
508
|
-
|
509
|
-
for (const evt of node.events) {
|
510
|
-
this.eventEmitter.on(evt, async (data?: Partial<GraphContext<T>>) => {
|
511
|
-
const freshContext = this.createNewContext();
|
512
|
-
if (data) Object.assign(freshContext, data);
|
513
|
-
await this.executeNode(node.name, freshContext, undefined, true);
|
514
|
-
});
|
515
|
-
}
|
516
|
-
}
|
364
|
+
this.eventManager.setupEventListeners();
|
517
365
|
}
|
518
366
|
|
519
367
|
/**
|
520
368
|
* Removes a node from the graph
|
521
369
|
* @param {string} nodeName - Name of the node to remove
|
522
370
|
*/
|
523
|
-
removeNode(nodeName: string): void {
|
524
|
-
const node = this.nodes.get(nodeName);
|
525
|
-
if (!node) return;
|
526
|
-
|
527
|
-
// remove the node from the map
|
371
|
+
public removeNode(nodeName: string): void {
|
528
372
|
this.nodes.delete(nodeName);
|
529
|
-
|
530
|
-
// remove any of its event-based listeners
|
531
|
-
if (node.events && node.events.length > 0) {
|
532
|
-
for (const evt of node.events) {
|
533
|
-
// removeAllListeners(evt) would also remove other node listeners,
|
534
|
-
// so we need a more fine-grained approach. Ideally, we should keep a reference
|
535
|
-
// to the exact listener function we attached. For brevity, let's remove all for that event:
|
536
|
-
this.eventEmitter.removeAllListeners(evt);
|
537
|
-
}
|
538
|
-
// Then reattach the others that remain in the graph
|
539
|
-
for (const n of this.nodes.values()) {
|
540
|
-
if (n.events && n.events.length > 0) {
|
541
|
-
n.events.forEach((e) => {
|
542
|
-
this.eventEmitter.on(e, async (data?: Partial<GraphContext<T>>) => {
|
543
|
-
const freshContext = this.createNewContext();
|
544
|
-
if (data) Object.assign(freshContext, data);
|
545
|
-
await this.executeNode(n.name, freshContext, undefined, true);
|
546
|
-
});
|
547
|
-
});
|
548
|
-
}
|
549
|
-
}
|
550
|
-
}
|
373
|
+
this.eventManager.setupEventListeners();
|
551
374
|
}
|
552
375
|
|
553
376
|
/**
|
554
377
|
* Returns all nodes in the graph
|
555
378
|
* @returns {Node<T>[]} Array of all nodes
|
556
379
|
*/
|
557
|
-
getNodes(): Node<T, any>[] {
|
380
|
+
public getNodes(): Node<T, any>[] {
|
558
381
|
return Array.from(this.nodes.values());
|
559
382
|
}
|
560
383
|
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
this.eventEmitter.emit("graphStarted", { name: this.name });
|
570
|
-
|
571
|
-
try {
|
572
|
-
// Execute the graph starting from the entry node
|
573
|
-
if (!this.entryNode) {
|
574
|
-
throw new Error("No entry node defined for graph event handling");
|
575
|
-
}
|
576
|
-
|
577
|
-
await this.executeNode(
|
578
|
-
this.entryNode,
|
579
|
-
freshContext,
|
580
|
-
undefined,
|
581
|
-
false
|
582
|
-
);
|
583
|
-
|
584
|
-
// Emit "graphCompleted"
|
585
|
-
this.eventEmitter.emit("graphCompleted", {
|
586
|
-
name: this.name,
|
587
|
-
context: this.context,
|
588
|
-
});
|
589
|
-
} catch (error) {
|
590
|
-
// Emit "graphError"
|
591
|
-
this.eventEmitter.emit("graphError", { name: this.name, error });
|
592
|
-
this.globalErrorHandler?.(error as Error, freshContext);
|
593
|
-
throw error;
|
594
|
-
}
|
595
|
-
});
|
596
|
-
});
|
597
|
-
}
|
598
|
-
}
|
599
|
-
|
600
|
-
getLogs(): string[] {
|
601
|
-
return [...this.logs];
|
602
|
-
}
|
603
|
-
|
604
|
-
clearLogs(): void {
|
605
|
-
this.logs = [];
|
384
|
+
/**
|
385
|
+
* Cleanup resources
|
386
|
+
*/
|
387
|
+
public destroy(): void {
|
388
|
+
this.destroySubject.next();
|
389
|
+
this.destroySubject.complete();
|
390
|
+
this.eventSubject.complete();
|
391
|
+
this.stateSubject.complete();
|
606
392
|
}
|
607
393
|
}
|