@ai.ntellect/core 0.6.19 → 0.6.20
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/graph/controller.ts +19 -32
- package/graph/index.ts +258 -101
- package/graph.ts +74 -0
- package/index.ts +1 -1
- package/interfaces/index.ts +7 -0
- package/package.json +1 -1
- package/test/graph/index.test.ts +54 -122
- package/types/index.ts +20 -8
package/graph/controller.ts
CHANGED
@@ -15,17 +15,17 @@ export class GraphController {
|
|
15
15
|
* @returns Map containing results of each graph execution, keyed by graph name and index
|
16
16
|
* @template T - Zod schema type for graph context validation
|
17
17
|
*/
|
18
|
-
static async executeSequential<T extends ZodSchema>(
|
19
|
-
graphs: GraphFlow<T
|
18
|
+
static async executeSequential<T extends ZodSchema[]>(
|
19
|
+
graphs: { [K in keyof T]: GraphFlow<T[K]> },
|
20
20
|
startNodes: string[],
|
21
|
-
|
22
|
-
): Promise<
|
23
|
-
const results = new Map<string, GraphContext<T>>();
|
21
|
+
inputs: any[]
|
22
|
+
): Promise<any[]> {
|
23
|
+
const results = new Map<string, GraphContext<T[keyof T]>>();
|
24
24
|
for (let i = 0; i < graphs.length; i++) {
|
25
|
-
const result = await graphs[i].execute(startNodes[i],
|
25
|
+
const result = await graphs[i].execute(startNodes[i], inputs[i]);
|
26
26
|
results.set(`${graphs[i].name}-${i}`, result);
|
27
27
|
}
|
28
|
-
return results;
|
28
|
+
return Array.from(results.values());
|
29
29
|
}
|
30
30
|
|
31
31
|
/**
|
@@ -38,34 +38,25 @@ export class GraphController {
|
|
38
38
|
* @returns Map containing results of each graph execution, keyed by graph name
|
39
39
|
* @template T - Zod schema type for graph context validation
|
40
40
|
*/
|
41
|
-
static async executeParallel<T extends ZodSchema>(
|
42
|
-
graphs: GraphFlow<T
|
41
|
+
static async executeParallel<T extends ZodSchema[]>(
|
42
|
+
graphs: { [K in keyof T]: GraphFlow<T[K]> },
|
43
43
|
startNodes: string[],
|
44
|
-
|
45
|
-
inputs
|
46
|
-
|
47
|
-
|
48
|
-
const results = new Map<string, GraphContext<T>>();
|
49
|
-
|
50
|
-
if (inputContexts) {
|
51
|
-
inputContexts = inputContexts.map((ctx) => ctx || {});
|
52
|
-
}
|
44
|
+
concurrency: number,
|
45
|
+
inputs: any[]
|
46
|
+
): Promise<any[]> {
|
47
|
+
const results = new Map<string, GraphContext<T[keyof T]>>();
|
53
48
|
|
54
49
|
if (inputs) {
|
55
50
|
inputs = inputs.map((input) => input || {});
|
56
51
|
}
|
57
52
|
|
58
|
-
if (
|
59
|
-
for (let i = 0; i < graphs.length; i +=
|
53
|
+
if (concurrency) {
|
54
|
+
for (let i = 0; i < graphs.length; i += concurrency) {
|
60
55
|
const batchResults = await Promise.all(
|
61
56
|
graphs
|
62
|
-
.slice(i, i +
|
57
|
+
.slice(i, i + concurrency)
|
63
58
|
.map((graph, index) =>
|
64
|
-
graph.execute(
|
65
|
-
startNodes[i + index],
|
66
|
-
inputContexts?.[i + index] || {},
|
67
|
-
inputs?.[i + index]
|
68
|
-
)
|
59
|
+
graph.execute(startNodes[i + index], inputs?.[i + index])
|
69
60
|
)
|
70
61
|
);
|
71
62
|
batchResults.forEach((result, index) => {
|
@@ -75,17 +66,13 @@ export class GraphController {
|
|
75
66
|
} else {
|
76
67
|
const allResults = await Promise.all(
|
77
68
|
graphs.map((graph, index) =>
|
78
|
-
graph.execute(
|
79
|
-
startNodes[index],
|
80
|
-
inputContexts?.[index] || {},
|
81
|
-
inputs?.[index] || {}
|
82
|
-
)
|
69
|
+
graph.execute(startNodes[index], inputs?.[index] || {})
|
83
70
|
)
|
84
71
|
);
|
85
72
|
allResults.forEach((result, index) => {
|
86
73
|
results.set(`${graphs[index].name}`, result);
|
87
74
|
});
|
88
75
|
}
|
89
|
-
return results;
|
76
|
+
return Array.from(results.values());
|
90
77
|
}
|
91
78
|
}
|
package/graph/index.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { EventEmitter } from "events";
|
2
|
+
import { IEventEmitter } from "interfaces";
|
2
3
|
import { ZodSchema } from "zod";
|
3
|
-
import { IEventEmitter } from "../interfaces";
|
4
4
|
import { GraphContext, GraphDefinition, Node } from "../types";
|
5
5
|
|
6
6
|
/**
|
@@ -17,26 +17,37 @@ import { GraphContext, GraphDefinition, Node } from "../types";
|
|
17
17
|
* @template T - Extends ZodSchema for type validation
|
18
18
|
*/
|
19
19
|
export class GraphFlow<T extends ZodSchema> {
|
20
|
-
private nodes: Map<string, Node<T>>;
|
20
|
+
private nodes: Map<string, Node<T, any>>;
|
21
21
|
private context: GraphContext<T>;
|
22
22
|
public validator?: T;
|
23
23
|
private eventEmitter: IEventEmitter;
|
24
24
|
private globalErrorHandler?: (error: Error, context: GraphContext<T>) => void;
|
25
25
|
private graphEvents?: string[];
|
26
26
|
private entryNode?: string;
|
27
|
+
private logs: string[] = [];
|
28
|
+
private verbose: boolean = false;
|
27
29
|
|
28
30
|
/**
|
29
31
|
* Creates a new instance of GraphFlow
|
30
32
|
* @param {string} name - The name of the graph flow
|
31
33
|
* @param {GraphDefinition<T>} config - Configuration object containing nodes, schema, context, and error handlers
|
34
|
+
* @param {Object} options - Optional options for the graph flow
|
32
35
|
*/
|
33
|
-
constructor(
|
34
|
-
|
36
|
+
constructor(
|
37
|
+
public name: string,
|
38
|
+
config: GraphDefinition<T>,
|
39
|
+
options: { verbose?: boolean } = {}
|
40
|
+
) {
|
41
|
+
this.nodes = new Map(
|
42
|
+
config.nodes.map((node: Node<T, any>) => [node.name, node])
|
43
|
+
);
|
35
44
|
this.validator = config.schema;
|
36
45
|
this.context = config.schema.parse(config.context) as GraphContext<T>;
|
37
46
|
this.globalErrorHandler = config.onError;
|
38
|
-
this.eventEmitter =
|
47
|
+
this.eventEmitter =
|
48
|
+
config.eventEmitter || (new EventEmitter() as IEventEmitter);
|
39
49
|
this.graphEvents = config.events;
|
50
|
+
this.verbose = options.verbose ?? false;
|
40
51
|
|
41
52
|
this.setupEventListeners();
|
42
53
|
this.setupGraphEventListeners();
|
@@ -105,6 +116,30 @@ export class GraphFlow<T extends ZodSchema> {
|
|
105
116
|
}
|
106
117
|
}
|
107
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
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
/**
|
128
|
+
* Enable or disable verbose logging
|
129
|
+
* @param {boolean} enabled - Whether to enable verbose logging
|
130
|
+
*/
|
131
|
+
public setVerbose(enabled: boolean): void {
|
132
|
+
this.verbose = enabled;
|
133
|
+
}
|
134
|
+
|
135
|
+
/**
|
136
|
+
* Get current verbose setting
|
137
|
+
* @returns {boolean} Current verbose setting
|
138
|
+
*/
|
139
|
+
public isVerbose(): boolean {
|
140
|
+
return this.verbose;
|
141
|
+
}
|
142
|
+
|
108
143
|
/**
|
109
144
|
* Executes a specific node in the graph
|
110
145
|
* @private
|
@@ -117,96 +152,201 @@ export class GraphFlow<T extends ZodSchema> {
|
|
117
152
|
private async executeNode(
|
118
153
|
nodeName: string,
|
119
154
|
context: GraphContext<T>,
|
120
|
-
inputs
|
155
|
+
inputs: any,
|
121
156
|
triggeredByEvent: boolean = false
|
122
157
|
): Promise<void> {
|
123
158
|
const node = this.nodes.get(nodeName);
|
124
|
-
if (!node) throw new Error(
|
159
|
+
if (!node) throw new Error(`Node "${nodeName}" not found.`);
|
125
160
|
|
126
|
-
|
127
|
-
|
128
|
-
}
|
161
|
+
this.addLog(`🚀 Starting node "${nodeName}"`);
|
162
|
+
this.eventEmitter.emit("nodeStarted", { name: nodeName });
|
129
163
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
}
|
143
|
-
validatedInputs = node.inputs.parse(inputs);
|
164
|
+
try {
|
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}"`);
|
144
177
|
}
|
178
|
+
this.addLog(`📥 Validating inputs for node "${nodeName}"`);
|
179
|
+
inputs = node.inputs.parse(inputs);
|
180
|
+
}
|
145
181
|
|
146
|
-
|
182
|
+
// Handle retry logic
|
183
|
+
if (node.retry && node.retry.maxAttempts > 0) {
|
184
|
+
let attempts = 0;
|
185
|
+
let lastError: Error | null = null;
|
147
186
|
|
148
|
-
|
149
|
-
|
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
|
+
}
|
150
229
|
|
151
|
-
|
152
|
-
|
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
|
+
}
|
153
239
|
}
|
240
|
+
} else {
|
241
|
+
await node.execute(localContext, inputs);
|
242
|
+
}
|
154
243
|
|
155
|
-
|
156
|
-
|
244
|
+
// Validate outputs
|
245
|
+
if (node.outputs) {
|
246
|
+
this.addLog(`📤 Validating outputs for node "${nodeName}"`);
|
247
|
+
node.outputs.parse(localContext);
|
248
|
+
}
|
157
249
|
|
158
|
-
|
159
|
-
// on arrête ici et on ne suit pas la chaîne next
|
160
|
-
if (triggeredByEvent && node.events && node.events.length > 0) {
|
161
|
-
this.context = structuredClone(context);
|
162
|
-
return;
|
163
|
-
}
|
250
|
+
Object.assign(context, localContext);
|
164
251
|
|
165
|
-
|
166
|
-
|
167
|
-
|
252
|
+
this.addLog(
|
253
|
+
`✅ Node "${nodeName}" executed successfully ${JSON.stringify(context)}`
|
254
|
+
);
|
255
|
+
this.eventEmitter.emit("nodeCompleted", { name: nodeName });
|
168
256
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
257
|
+
// Handle waitForEvent
|
258
|
+
if (node.waitForEvent && !triggeredByEvent) {
|
259
|
+
this.addLog(
|
260
|
+
`⏳ Node "${nodeName}" waiting for events: ${node.events?.join(", ")}`
|
261
|
+
);
|
173
262
|
|
174
|
-
|
263
|
+
await new Promise<void>((resolve) => {
|
264
|
+
const eventHandler = () => {
|
265
|
+
this.addLog(`🚀 Event received for node "${nodeName}"`);
|
266
|
+
resolve();
|
267
|
+
};
|
175
268
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
269
|
+
node.events?.forEach((event) => {
|
270
|
+
this.eventEmitter.once(event, eventHandler);
|
271
|
+
});
|
272
|
+
});
|
180
273
|
|
181
|
-
|
182
|
-
|
183
|
-
|
274
|
+
const nextNodes =
|
275
|
+
typeof node.next === "function"
|
276
|
+
? node.next(context)
|
277
|
+
: node.next || [];
|
278
|
+
|
279
|
+
if (nextNodes.length > 0) {
|
280
|
+
this.addLog(`➡️ Executing next nodes: ${nextNodes.join(", ")}`);
|
184
281
|
|
185
|
-
//
|
186
|
-
|
187
|
-
|
188
|
-
|
282
|
+
// Créer un contexte unique pour toutes les branches
|
283
|
+
const branchContext = structuredClone(context);
|
284
|
+
|
285
|
+
// Exécuter les branches séquentiellement avec le même contexte
|
286
|
+
for (const nextNodeName of nextNodes) {
|
287
|
+
this.addLog(`🔄 Starting branch for node "${nextNodeName}"`);
|
288
|
+
const nextNode = this.nodes.get(nextNodeName);
|
289
|
+
if (nextNode) {
|
290
|
+
// Utiliser le même contexte pour toutes les branches
|
291
|
+
await this.executeNode(
|
292
|
+
nextNodeName,
|
293
|
+
branchContext,
|
294
|
+
undefined,
|
295
|
+
nextNode.waitForEvent
|
296
|
+
);
|
297
|
+
}
|
298
|
+
this.addLog(`✅ Branch "${nextNodeName}" completed`);
|
189
299
|
}
|
300
|
+
|
301
|
+
// Mettre à jour le contexte global avec le résultat final des branches
|
302
|
+
Object.assign(context, branchContext);
|
303
|
+
this.context = structuredClone(context);
|
304
|
+
|
305
|
+
this.eventEmitter.emit("graphCompleted", {
|
306
|
+
name: this.name,
|
307
|
+
context: this.context,
|
308
|
+
});
|
309
|
+
|
310
|
+
return;
|
190
311
|
}
|
312
|
+
}
|
191
313
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
314
|
+
// Execute next nodes
|
315
|
+
const nextNodes =
|
316
|
+
typeof node.next === "function"
|
317
|
+
? node.next(localContext)
|
318
|
+
: node.next || [];
|
319
|
+
|
320
|
+
if (nextNodes.length > 0) {
|
321
|
+
this.addLog(`➡️ Executing next nodes: ${nextNodes.join(", ")}`);
|
322
|
+
|
323
|
+
// Créer un contexte unique pour toutes les branches
|
324
|
+
const branchContext = structuredClone(context);
|
325
|
+
|
326
|
+
// Exécuter les branches séquentiellement avec le même contexte
|
327
|
+
for (const nextNodeName of nextNodes) {
|
328
|
+
this.addLog(`🔄 Starting branch for node "${nextNodeName}"`);
|
329
|
+
const nextNode = this.nodes.get(nextNodeName);
|
330
|
+
if (nextNode) {
|
331
|
+
// Utiliser le même contexte pour toutes les branches
|
332
|
+
await this.executeNode(
|
333
|
+
nextNodeName,
|
334
|
+
branchContext,
|
335
|
+
undefined,
|
336
|
+
nextNode.waitForEvent
|
337
|
+
);
|
338
|
+
}
|
339
|
+
this.addLog(`✅ Branch "${nextNodeName}" completed`);
|
202
340
|
}
|
203
341
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
);
|
208
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
342
|
+
// Mettre à jour le contexte global avec le résultat final des branches
|
343
|
+
Object.assign(context, branchContext);
|
344
|
+
this.context = structuredClone(context);
|
209
345
|
}
|
346
|
+
} catch (error: any) {
|
347
|
+
this.addLog(`❌ Error in node "${nodeName}": ${error.message}`);
|
348
|
+
this.eventEmitter.emit("nodeError", { name: nodeName, error });
|
349
|
+
throw error;
|
210
350
|
}
|
211
351
|
}
|
212
352
|
|
@@ -231,37 +371,32 @@ export class GraphFlow<T extends ZodSchema> {
|
|
231
371
|
*/
|
232
372
|
async execute(
|
233
373
|
startNode: string,
|
234
|
-
|
235
|
-
|
374
|
+
inputParams?: any,
|
375
|
+
inputContext?: Partial<GraphContext<T>>
|
236
376
|
): Promise<GraphContext<T>> {
|
237
|
-
|
238
|
-
|
239
|
-
|
377
|
+
if (inputParams) {
|
378
|
+
// Merge inputParams into context
|
379
|
+
Object.assign(this.context, inputParams);
|
380
|
+
}
|
381
|
+
|
382
|
+
if (inputContext) {
|
383
|
+
Object.assign(this.context, inputContext);
|
384
|
+
}
|
240
385
|
|
241
|
-
// Emit "graphStarted"
|
242
386
|
this.eventEmitter.emit("graphStarted", { name: this.name });
|
243
387
|
|
244
388
|
try {
|
245
|
-
|
246
|
-
await this.executeNode(
|
247
|
-
startNode,
|
248
|
-
context,
|
249
|
-
inputParams,
|
250
|
-
/* triggeredByEvent= */ false
|
251
|
-
);
|
389
|
+
await this.executeNode(startNode, this.context, inputParams, false);
|
252
390
|
|
253
|
-
// Emit "graphCompleted"
|
254
391
|
this.eventEmitter.emit("graphCompleted", {
|
255
392
|
name: this.name,
|
256
393
|
context: this.context,
|
257
394
|
});
|
258
395
|
|
259
|
-
|
260
|
-
return structuredClone(this.context);
|
396
|
+
return this.getContext();
|
261
397
|
} catch (error) {
|
262
|
-
// Emit "graphError"
|
263
398
|
this.eventEmitter.emit("graphError", { name: this.name, error });
|
264
|
-
this.globalErrorHandler?.(error as Error, context);
|
399
|
+
this.globalErrorHandler?.(error as Error, this.context);
|
265
400
|
throw error;
|
266
401
|
}
|
267
402
|
}
|
@@ -276,15 +411,29 @@ export class GraphFlow<T extends ZodSchema> {
|
|
276
411
|
eventName: string,
|
277
412
|
data?: Partial<GraphContext<T>>
|
278
413
|
): Promise<GraphContext<T>> {
|
279
|
-
|
280
|
-
const context = this.createNewContext();
|
281
|
-
if (data) Object.assign(context, data);
|
414
|
+
const workingContext = structuredClone(this.context);
|
282
415
|
|
283
|
-
|
284
|
-
|
285
|
-
|
416
|
+
if (data) {
|
417
|
+
Object.assign(workingContext, data);
|
418
|
+
}
|
419
|
+
|
420
|
+
const eventNodes = Array.from(this.nodes.values()).filter((node) =>
|
421
|
+
node.events?.includes(eventName)
|
422
|
+
);
|
423
|
+
|
424
|
+
// Execute event nodes sequentially with shared context
|
425
|
+
for (const node of eventNodes) {
|
426
|
+
await this.executeNode(node.name, workingContext, undefined, true);
|
427
|
+
}
|
428
|
+
|
429
|
+
// Update global context after all event nodes are executed
|
430
|
+
this.context = structuredClone(workingContext);
|
431
|
+
|
432
|
+
this.eventEmitter.emit("graphCompleted", {
|
433
|
+
name: this.name,
|
434
|
+
context: this.context,
|
435
|
+
});
|
286
436
|
|
287
|
-
// Return the updated global context
|
288
437
|
return this.getContext();
|
289
438
|
}
|
290
439
|
|
@@ -351,10 +500,10 @@ export class GraphFlow<T extends ZodSchema> {
|
|
351
500
|
}
|
352
501
|
|
353
502
|
/**
|
354
|
-
*
|
355
|
-
* @returns {GraphContext<T>}
|
503
|
+
* Gets a copy of the current context
|
504
|
+
* @returns {GraphContext<T>} A deep copy of the current context
|
356
505
|
*/
|
357
|
-
getContext(): GraphContext<T> {
|
506
|
+
public getContext(): GraphContext<T> {
|
358
507
|
return structuredClone(this.context);
|
359
508
|
}
|
360
509
|
|
@@ -372,7 +521,7 @@ export class GraphFlow<T extends ZodSchema> {
|
|
372
521
|
* @param {Node<T>} node - Node to add
|
373
522
|
* @throws {Error} If node with same name already exists
|
374
523
|
*/
|
375
|
-
addNode(node: Node<T>): void {
|
524
|
+
addNode(node: Node<T, any>): void {
|
376
525
|
this.nodes.set(node.name, node);
|
377
526
|
if (node.events && node.events.length > 0) {
|
378
527
|
for (const evt of node.events) {
|
@@ -423,7 +572,7 @@ export class GraphFlow<T extends ZodSchema> {
|
|
423
572
|
* Returns all nodes in the graph
|
424
573
|
* @returns {Node<T>[]} Array of all nodes
|
425
574
|
*/
|
426
|
-
getNodes(): Node<T>[] {
|
575
|
+
getNodes(): Node<T, any>[] {
|
427
576
|
return Array.from(this.nodes.values());
|
428
577
|
}
|
429
578
|
|
@@ -465,4 +614,12 @@ export class GraphFlow<T extends ZodSchema> {
|
|
465
614
|
});
|
466
615
|
}
|
467
616
|
}
|
617
|
+
|
618
|
+
getLogs(): string[] {
|
619
|
+
return [...this.logs];
|
620
|
+
}
|
621
|
+
|
622
|
+
clearLogs(): void {
|
623
|
+
this.logs = [];
|
624
|
+
}
|
468
625
|
}
|
package/graph.ts
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
import { Node } from "types";
|
2
|
+
import { z } from "zod";
|
3
|
+
import { GraphController, GraphFlow } from "./index";
|
4
|
+
|
5
|
+
// 🏗 Définition des schémas pour chaque graphe
|
6
|
+
const schemaA = z.object({
|
7
|
+
input: z.string(),
|
8
|
+
result: z.string().optional(),
|
9
|
+
});
|
10
|
+
|
11
|
+
const schemaB = z.object({
|
12
|
+
number: z.number(),
|
13
|
+
result: z.number().optional(),
|
14
|
+
});
|
15
|
+
|
16
|
+
// 🔹 **Graph A** : Convertit une chaîne en majuscules
|
17
|
+
const processText: Node<typeof schemaA> = {
|
18
|
+
name: "processText",
|
19
|
+
execute: async (context) => {
|
20
|
+
context.result = context.input.toUpperCase();
|
21
|
+
console.log("📢 Graphe A : Texte transformé →", context.result);
|
22
|
+
},
|
23
|
+
};
|
24
|
+
|
25
|
+
// 🔹 **Graph B** : Multiplie un nombre par 10
|
26
|
+
const multiplyNumber: Node<typeof schemaB> = {
|
27
|
+
name: "multiplyNumber",
|
28
|
+
execute: async (context) => {
|
29
|
+
context.result = context.number * 10;
|
30
|
+
console.log("🔢 Graphe B : Nombre multiplié →", context.result);
|
31
|
+
},
|
32
|
+
};
|
33
|
+
|
34
|
+
// 🔗 **Création des graphes**
|
35
|
+
const graphA = new GraphFlow("GraphA", {
|
36
|
+
name: "GraphA",
|
37
|
+
nodes: [processText],
|
38
|
+
context: { input: "" },
|
39
|
+
schema: schemaA,
|
40
|
+
});
|
41
|
+
|
42
|
+
const graphB = new GraphFlow("GraphB", {
|
43
|
+
name: "GraphB",
|
44
|
+
nodes: [multiplyNumber],
|
45
|
+
context: { number: 0 },
|
46
|
+
schema: schemaB,
|
47
|
+
});
|
48
|
+
|
49
|
+
(async () => {
|
50
|
+
try {
|
51
|
+
console.log("🚀 **Exécution Séquentielle** des graphes...");
|
52
|
+
const sequentialResults = await GraphController.executeSequential(
|
53
|
+
[graphA, graphB],
|
54
|
+
["processText", "multiplyNumber"],
|
55
|
+
[{ input: "hello world" }, { number: 5 }]
|
56
|
+
);
|
57
|
+
|
58
|
+
console.log("🟢 **Résultats Séquentiels :**", sequentialResults);
|
59
|
+
|
60
|
+
console.log(
|
61
|
+
"\n⚡ **Exécution Parallèle** avec limitation de concurrence..."
|
62
|
+
);
|
63
|
+
const parallelResults = await GraphController.executeParallel(
|
64
|
+
[graphA, graphB],
|
65
|
+
["processText", "multiplyNumber"],
|
66
|
+
1, // ⚠️ Limite de concurrence (1 à la fois)
|
67
|
+
[{ input: "parallel execution" }, { number: 7 }]
|
68
|
+
);
|
69
|
+
|
70
|
+
console.log("🟢 **Résultats Parallèles :**", parallelResults);
|
71
|
+
} catch (error) {
|
72
|
+
console.error("❌ Erreur lors de l’exécution :", error);
|
73
|
+
}
|
74
|
+
})();
|
package/index.ts
CHANGED
@@ -11,8 +11,8 @@
|
|
11
11
|
* - Utility functions for action schema generation and header building
|
12
12
|
*/
|
13
13
|
|
14
|
-
export * from "./graph";
|
15
14
|
export * from "./graph/controller";
|
15
|
+
export * from "./graph/index";
|
16
16
|
export * from "./modules/memory";
|
17
17
|
export * from "./modules/memory/adapters/meilisearch";
|
18
18
|
export * from "./modules/memory/adapters/redis";
|
package/interfaces/index.ts
CHANGED
@@ -331,6 +331,13 @@ export interface IEventEmitter {
|
|
331
331
|
* @returns {Function[]} Array of listener functions
|
332
332
|
*/
|
333
333
|
rawListeners(event: string): Function[];
|
334
|
+
|
335
|
+
/**
|
336
|
+
* Registers an event listener that will be called only once
|
337
|
+
* @param {string} event - Event name
|
338
|
+
* @param {Function} listener - Event handler
|
339
|
+
*/
|
340
|
+
once(event: string, listener: (...args: any[]) => void): void;
|
334
341
|
}
|
335
342
|
|
336
343
|
/**
|
package/package.json
CHANGED
package/test/graph/index.test.ts
CHANGED
@@ -2,9 +2,8 @@ import { expect } from "chai";
|
|
2
2
|
import EventEmitter from "events";
|
3
3
|
import sinon from "sinon";
|
4
4
|
import { z } from "zod";
|
5
|
-
import { GraphFlow } from "../../graph";
|
5
|
+
import { GraphFlow } from "../../graph/index";
|
6
6
|
import { GraphDefinition, Node } from "../../types";
|
7
|
-
|
8
7
|
/**
|
9
8
|
* ✅ Define a valid schema using Zod.
|
10
9
|
*/
|
@@ -23,13 +22,17 @@ describe("Graph", function () {
|
|
23
22
|
|
24
23
|
beforeEach(() => {
|
25
24
|
eventEmitter = new EventEmitter();
|
26
|
-
graph = new GraphFlow(
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
25
|
+
graph = new GraphFlow(
|
26
|
+
"TestGraph",
|
27
|
+
{
|
28
|
+
name: "TestGraph",
|
29
|
+
nodes: [],
|
30
|
+
context: { value: 0 },
|
31
|
+
schema: TestSchema,
|
32
|
+
eventEmitter: eventEmitter,
|
33
|
+
},
|
34
|
+
{ verbose: true }
|
35
|
+
);
|
33
36
|
});
|
34
37
|
|
35
38
|
/**
|
@@ -39,7 +42,7 @@ describe("Graph", function () {
|
|
39
42
|
const simpleNode: Node<TestSchema> = {
|
40
43
|
name: "simpleNode",
|
41
44
|
execute: async (context) => {
|
42
|
-
context.value
|
45
|
+
context.value = (context.value ?? 0) + 1;
|
43
46
|
},
|
44
47
|
next: [],
|
45
48
|
};
|
@@ -64,7 +67,7 @@ describe("Graph", function () {
|
|
64
67
|
const testNode: Node<TestSchema> = {
|
65
68
|
name: "testNode",
|
66
69
|
execute: async (context) => {
|
67
|
-
context.value
|
70
|
+
context.value = (context.value ?? 0) + 1;
|
68
71
|
},
|
69
72
|
next: [],
|
70
73
|
};
|
@@ -101,35 +104,6 @@ describe("Graph", function () {
|
|
101
104
|
expect(nodeErrorSpy.calledOnce).to.be.true;
|
102
105
|
});
|
103
106
|
|
104
|
-
/**
|
105
|
-
* ✅ Ensure a node requiring user confirmation waits before execution.
|
106
|
-
*/
|
107
|
-
it("should execute a node requiring user confirmation", async function () {
|
108
|
-
const confirmationNode: Node<TestSchema> = {
|
109
|
-
name: "waitUserConfirmation",
|
110
|
-
execute: async (context) => {
|
111
|
-
return new Promise<void>((resolve) => {
|
112
|
-
graph.on("userConfirmed", () => {
|
113
|
-
context.value += 1;
|
114
|
-
resolve();
|
115
|
-
});
|
116
|
-
});
|
117
|
-
},
|
118
|
-
next: [],
|
119
|
-
};
|
120
|
-
|
121
|
-
graph.addNode(confirmationNode);
|
122
|
-
const executionPromise = graph.execute("waitUserConfirmation");
|
123
|
-
|
124
|
-
setTimeout(() => {
|
125
|
-
graph.emit("userConfirmed");
|
126
|
-
}, 100);
|
127
|
-
|
128
|
-
await executionPromise;
|
129
|
-
const context = graph.getContext();
|
130
|
-
expect(context.value).to.equal(1);
|
131
|
-
});
|
132
|
-
|
133
107
|
/**
|
134
108
|
* ✅ Ensure that context validation using Zod works correctly.
|
135
109
|
*/
|
@@ -140,7 +114,7 @@ describe("Graph", function () {
|
|
140
114
|
const simpleNode: Node<TestSchema> = {
|
141
115
|
name: "simpleNode",
|
142
116
|
execute: async (context) => {
|
143
|
-
context.value
|
117
|
+
context.value = (context.value ?? 0) + 1;
|
144
118
|
},
|
145
119
|
next: [],
|
146
120
|
};
|
@@ -158,7 +132,7 @@ describe("Graph", function () {
|
|
158
132
|
* ✅ Ensure a node with validated inputs and outputs executes correctly.
|
159
133
|
*/
|
160
134
|
it("should execute a node with validated inputs and outputs", async function () {
|
161
|
-
const paramNode: Node<TestSchema> = {
|
135
|
+
const paramNode: Node<TestSchema, { increment: number }> = {
|
162
136
|
name: "paramNode",
|
163
137
|
inputs: z.object({
|
164
138
|
increment: z.number(),
|
@@ -167,13 +141,13 @@ describe("Graph", function () {
|
|
167
141
|
value: z.number().min(5),
|
168
142
|
}),
|
169
143
|
execute: async (context, inputs: { increment: number }) => {
|
170
|
-
context.value
|
144
|
+
context.value = (context.value ?? 0) + inputs.increment;
|
171
145
|
},
|
172
146
|
next: [],
|
173
147
|
};
|
174
148
|
|
175
149
|
graph.addNode(paramNode);
|
176
|
-
await graph.execute("paramNode", {
|
150
|
+
await graph.execute("paramNode", { increment: 5 });
|
177
151
|
|
178
152
|
const context = graph.getContext();
|
179
153
|
expect(context.value).to.equal(5);
|
@@ -185,9 +159,9 @@ describe("Graph", function () {
|
|
185
159
|
it("should not execute a node when condition is false", async function () {
|
186
160
|
const conditionalNode: Node<TestSchema> = {
|
187
161
|
name: "conditionalNode",
|
188
|
-
condition: (context) => context.value > 0,
|
162
|
+
condition: (context) => (context.value ?? 0) > 0,
|
189
163
|
execute: async (context) => {
|
190
|
-
context.value
|
164
|
+
context.value = (context.value ?? 0) + 10;
|
191
165
|
},
|
192
166
|
next: [],
|
193
167
|
};
|
@@ -212,7 +186,7 @@ describe("Graph", function () {
|
|
212
186
|
if (attemptCount < 3) {
|
213
187
|
throw new Error("Temporary failure");
|
214
188
|
}
|
215
|
-
context.value
|
189
|
+
context.value = (context.value ?? 0) + 1;
|
216
190
|
},
|
217
191
|
next: [],
|
218
192
|
};
|
@@ -235,7 +209,7 @@ describe("Graph", function () {
|
|
235
209
|
name: "eventNode",
|
236
210
|
events: ["customEvent"],
|
237
211
|
execute: async (context) => {
|
238
|
-
context.value
|
212
|
+
context.value = (context.value ?? 0) + 1;
|
239
213
|
},
|
240
214
|
next: [],
|
241
215
|
};
|
@@ -304,13 +278,13 @@ describe("Graph", function () {
|
|
304
278
|
* ✅ Test input validation failure
|
305
279
|
*/
|
306
280
|
it("should throw error when node input validation fails", async function () {
|
307
|
-
const nodeWithInput: Node<TestSchema> = {
|
281
|
+
const nodeWithInput: Node<TestSchema, { amount: number }> = {
|
308
282
|
name: "inputNode",
|
309
283
|
inputs: z.object({
|
310
284
|
amount: z.number().min(0),
|
311
285
|
}),
|
312
286
|
execute: async (context, inputs: { amount: number }) => {
|
313
|
-
context.value
|
287
|
+
context.value = (context.value ?? 0) + inputs.amount;
|
314
288
|
},
|
315
289
|
next: [],
|
316
290
|
};
|
@@ -318,7 +292,7 @@ describe("Graph", function () {
|
|
318
292
|
graph.addNode(nodeWithInput);
|
319
293
|
|
320
294
|
try {
|
321
|
-
await graph.execute("inputNode", {
|
295
|
+
await graph.execute("inputNode", { amount: -1 });
|
322
296
|
expect.fail("Should have thrown an error");
|
323
297
|
} catch (error) {
|
324
298
|
expect((error as Error).message).to.include(
|
@@ -358,7 +332,7 @@ describe("Graph", function () {
|
|
358
332
|
* ✅ Test successful input and output validation
|
359
333
|
*/
|
360
334
|
it("should successfully validate both inputs and outputs", async function () {
|
361
|
-
const validatedNode: Node<TestSchema> = {
|
335
|
+
const validatedNode: Node<TestSchema, { increment: number }> = {
|
362
336
|
name: "validatedNode",
|
363
337
|
inputs: z.object({
|
364
338
|
increment: z.number().min(0).max(5),
|
@@ -367,7 +341,7 @@ describe("Graph", function () {
|
|
367
341
|
value: z.number().min(0).max(10),
|
368
342
|
}),
|
369
343
|
execute: async (context, inputs: { increment: number }) => {
|
370
|
-
context.value
|
344
|
+
context.value = (context.value ?? 0) + inputs.increment;
|
371
345
|
},
|
372
346
|
next: [],
|
373
347
|
};
|
@@ -375,12 +349,12 @@ describe("Graph", function () {
|
|
375
349
|
graph.addNode(validatedNode);
|
376
350
|
|
377
351
|
// Test with valid input that produces valid output
|
378
|
-
await graph.execute("validatedNode", {
|
352
|
+
await graph.execute("validatedNode", { increment: 3 });
|
379
353
|
expect(graph.getContext().value).to.equal(3);
|
380
354
|
|
381
355
|
// Test with valid input that would produce invalid output
|
382
356
|
try {
|
383
|
-
await graph.execute("validatedNode", {
|
357
|
+
await graph.execute("validatedNode", { increment: 5 }, { value: 7 });
|
384
358
|
expect.fail("Should have thrown an error");
|
385
359
|
} catch (error) {
|
386
360
|
expect((error as Error).message).to.include(
|
@@ -393,7 +367,7 @@ describe("Graph", function () {
|
|
393
367
|
* ✅ Test missing required inputs
|
394
368
|
*/
|
395
369
|
it("should throw error when required inputs are missing", async function () {
|
396
|
-
const nodeWithRequiredInput: Node<TestSchema> = {
|
370
|
+
const nodeWithRequiredInput: Node<TestSchema, { required: string }> = {
|
397
371
|
name: "requiredInputNode",
|
398
372
|
inputs: z.object({
|
399
373
|
required: z.string(),
|
@@ -419,7 +393,7 @@ describe("Graph", function () {
|
|
419
393
|
const nodeA: Node<TestSchema> = {
|
420
394
|
name: "nodeA",
|
421
395
|
execute: async (context) => {
|
422
|
-
context.value
|
396
|
+
context.value = (context.value ?? 0) + 1;
|
423
397
|
},
|
424
398
|
next: ["nodeB1", "nodeB2"],
|
425
399
|
};
|
@@ -427,7 +401,7 @@ describe("Graph", function () {
|
|
427
401
|
const nodeB1: Node<TestSchema> = {
|
428
402
|
name: "nodeB1",
|
429
403
|
execute: async (context) => {
|
430
|
-
context.value
|
404
|
+
context.value = (context.value ?? 0) * 2;
|
431
405
|
},
|
432
406
|
next: ["nodeC"],
|
433
407
|
};
|
@@ -435,7 +409,7 @@ describe("Graph", function () {
|
|
435
409
|
const nodeB2: Node<TestSchema> = {
|
436
410
|
name: "nodeB2",
|
437
411
|
execute: async (context) => {
|
438
|
-
context.value
|
412
|
+
context.value = (context.value ?? 0) + 3;
|
439
413
|
},
|
440
414
|
next: ["nodeC"],
|
441
415
|
};
|
@@ -444,7 +418,7 @@ describe("Graph", function () {
|
|
444
418
|
name: "nodeC",
|
445
419
|
execute: async (context) => {
|
446
420
|
// Créer une copie du contexte pour éviter les modifications concurrentes
|
447
|
-
const newValue = context.value + 5;
|
421
|
+
const newValue = (context.value ?? 0) + 5;
|
448
422
|
context.value = newValue;
|
449
423
|
},
|
450
424
|
};
|
@@ -462,25 +436,25 @@ describe("Graph", function () {
|
|
462
436
|
const startNode: Node<TestSchema> = {
|
463
437
|
name: "start",
|
464
438
|
execute: async (context) => {
|
465
|
-
context.value = 5;
|
439
|
+
context.value = (context.value ?? 0) + 5;
|
466
440
|
},
|
467
441
|
next: ["branchA", "branchB"],
|
468
442
|
};
|
469
443
|
|
470
444
|
const branchA: Node<TestSchema> = {
|
471
445
|
name: "branchA",
|
472
|
-
condition: (context) => context.value < 10,
|
446
|
+
condition: (context) => (context.value ?? 0) < 10,
|
473
447
|
execute: async (context) => {
|
474
|
-
context.value
|
448
|
+
context.value = (context.value ?? 0) * 2;
|
475
449
|
},
|
476
450
|
next: ["end"],
|
477
451
|
};
|
478
452
|
|
479
453
|
const branchB: Node<TestSchema> = {
|
480
454
|
name: "branchB",
|
481
|
-
condition: (context) => context.value >= 10,
|
455
|
+
condition: (context) => (context.value ?? 0) >= 10,
|
482
456
|
execute: async (context) => {
|
483
|
-
context.value
|
457
|
+
context.value = (context.value ?? 0) + 10;
|
484
458
|
},
|
485
459
|
next: ["end"],
|
486
460
|
};
|
@@ -488,7 +462,7 @@ describe("Graph", function () {
|
|
488
462
|
const endNode: Node<TestSchema> = {
|
489
463
|
name: "end",
|
490
464
|
execute: async (context) => {
|
491
|
-
context.value = context.value + 1;
|
465
|
+
context.value = (context.value ?? 0) + 1;
|
492
466
|
},
|
493
467
|
};
|
494
468
|
|
@@ -496,6 +470,13 @@ describe("Graph", function () {
|
|
496
470
|
graph.addNode(node)
|
497
471
|
);
|
498
472
|
|
473
|
+
await graph.load({
|
474
|
+
name: "TestGraph",
|
475
|
+
nodes: [startNode, branchA, branchB, endNode],
|
476
|
+
context: { value: 0 },
|
477
|
+
schema: TestSchema,
|
478
|
+
});
|
479
|
+
|
499
480
|
await graph.execute("start");
|
500
481
|
expect(graph.getContext().value).to.equal(11);
|
501
482
|
});
|
@@ -520,7 +501,7 @@ describe("Graph", function () {
|
|
520
501
|
name: "process",
|
521
502
|
events: ["processData"],
|
522
503
|
execute: async (context) => {
|
523
|
-
context.value
|
504
|
+
context.value = (context.value ?? 0) * 2;
|
524
505
|
},
|
525
506
|
next: ["finalize"],
|
526
507
|
};
|
@@ -529,7 +510,7 @@ describe("Graph", function () {
|
|
529
510
|
name: "finalize",
|
530
511
|
events: ["complete"],
|
531
512
|
execute: async (context) => {
|
532
|
-
context.value
|
513
|
+
context.value = (context.value ?? 0) + 3;
|
533
514
|
eventCounter.count++;
|
534
515
|
},
|
535
516
|
};
|
@@ -573,7 +554,7 @@ describe("Graph", function () {
|
|
573
554
|
const cycleNode: Node<TestSchema> = {
|
574
555
|
name: "cycle",
|
575
556
|
execute: async (context) => {
|
576
|
-
context.value
|
557
|
+
context.value = (context.value ?? 0) + 1;
|
577
558
|
iterationCount.count++;
|
578
559
|
},
|
579
560
|
next: ["checkExit"],
|
@@ -581,9 +562,10 @@ describe("Graph", function () {
|
|
581
562
|
|
582
563
|
const checkExitNode: Node<TestSchema> = {
|
583
564
|
name: "checkExit",
|
584
|
-
execute: async (
|
585
|
-
|
586
|
-
|
565
|
+
execute: async () => {},
|
566
|
+
next: (context) => {
|
567
|
+
return (context.value ?? 0) < 5 ? ["cycle"] : [];
|
568
|
+
},
|
587
569
|
};
|
588
570
|
|
589
571
|
[cycleNode, checkExitNode].forEach((node) => graph.addNode(node));
|
@@ -593,54 +575,4 @@ describe("Graph", function () {
|
|
593
575
|
expect(graph.getContext().value).to.equal(5);
|
594
576
|
expect(iterationCount.count).to.equal(5);
|
595
577
|
});
|
596
|
-
|
597
|
-
/**
|
598
|
-
* ✅ Test executing entire graph when triggered by event
|
599
|
-
*/
|
600
|
-
it("should execute entire graph when triggered by event", async function () {
|
601
|
-
const nodeA: Node<TestSchema> = {
|
602
|
-
name: "nodeA",
|
603
|
-
execute: async (context) => {
|
604
|
-
context.value += 1;
|
605
|
-
},
|
606
|
-
next: ["nodeB"],
|
607
|
-
};
|
608
|
-
|
609
|
-
const nodeB: Node<TestSchema> = {
|
610
|
-
name: "nodeB",
|
611
|
-
execute: async (context) => {
|
612
|
-
context.value *= 2;
|
613
|
-
},
|
614
|
-
next: [],
|
615
|
-
};
|
616
|
-
|
617
|
-
const graphDefinition: GraphDefinition<TestSchema> = {
|
618
|
-
name: "EventGraph",
|
619
|
-
nodes: [nodeA, nodeB],
|
620
|
-
context: { value: 0 },
|
621
|
-
schema: TestSchema,
|
622
|
-
entryNode: "nodeA",
|
623
|
-
events: ["startGraph"],
|
624
|
-
};
|
625
|
-
|
626
|
-
graph.load(graphDefinition);
|
627
|
-
|
628
|
-
// Use a promise to ensure the event is properly handled
|
629
|
-
await new Promise<void>((resolve, reject) => {
|
630
|
-
const timeout = setTimeout(
|
631
|
-
() => reject(new Error("Graph event did not complete")),
|
632
|
-
1500
|
633
|
-
);
|
634
|
-
|
635
|
-
graph.on("graphCompleted", () => {
|
636
|
-
clearTimeout(timeout);
|
637
|
-
resolve();
|
638
|
-
});
|
639
|
-
|
640
|
-
graph.emit("startGraph").catch(reject);
|
641
|
-
});
|
642
|
-
|
643
|
-
const context = graph.getContext();
|
644
|
-
expect(context.value).to.equal(2); // (0 + 1) * 2
|
645
|
-
});
|
646
578
|
});
|
package/types/index.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import { EventEmitter } from "events";
|
1
2
|
import { IEventEmitter } from "interfaces";
|
2
3
|
import { ZodSchema } from "zod";
|
3
4
|
|
@@ -62,7 +63,7 @@ export type ScheduledRequest = {
|
|
62
63
|
* Utility type for extracting schema type from Zod schema
|
63
64
|
* @template T - Zod schema type
|
64
65
|
*/
|
65
|
-
export type SchemaType<T> = T extends ZodSchema<infer U> ?
|
66
|
+
export type SchemaType<T> = T extends ZodSchema<infer U> ? U : never;
|
66
67
|
|
67
68
|
/**
|
68
69
|
* Type for graph context based on schema
|
@@ -74,28 +75,39 @@ export type GraphContext<T> = SchemaType<T>;
|
|
74
75
|
* Interface representing a node in the graph
|
75
76
|
* @interface
|
76
77
|
* @template T - Schema type
|
78
|
+
* @template I - Input schema type
|
79
|
+
* @template O - Output schema type
|
77
80
|
*/
|
78
|
-
export interface Node<T> {
|
81
|
+
export interface Node<T extends ZodSchema, I = any> {
|
79
82
|
/** Name of the node */
|
80
83
|
name: string;
|
81
84
|
/** Schema for node inputs */
|
82
|
-
inputs?: ZodSchema
|
85
|
+
inputs?: I extends void ? never : ZodSchema<I>;
|
83
86
|
/** Schema for node outputs */
|
84
87
|
outputs?: ZodSchema;
|
85
88
|
/** Execute function for the node */
|
86
|
-
execute: (
|
89
|
+
execute: (
|
90
|
+
context: GraphContext<T>,
|
91
|
+
inputs: I extends void ? never : I
|
92
|
+
) => Promise<void>;
|
87
93
|
/** Optional condition for node execution */
|
88
94
|
condition?: (context: GraphContext<T>) => boolean;
|
89
95
|
/** Array of next node names */
|
90
|
-
next?: string[];
|
96
|
+
next?: string[] | ((context: GraphContext<T>) => string[]);
|
91
97
|
/** Array of event names */
|
92
98
|
events?: string[];
|
99
|
+
/** Wait for event */
|
100
|
+
waitForEvent?: boolean;
|
93
101
|
/** Retry configuration */
|
94
102
|
retry?: {
|
95
103
|
/** Maximum number of retry attempts */
|
96
104
|
maxAttempts: number;
|
97
105
|
/** Delay between retries in milliseconds */
|
98
106
|
delay: number;
|
107
|
+
/** Error handler function */
|
108
|
+
onRetryFailed?: (error: Error, context: GraphContext<T>) => Promise<void>;
|
109
|
+
/** Continue execution on failed retry */
|
110
|
+
continueOnFailed?: boolean;
|
99
111
|
};
|
100
112
|
/** Error handler function */
|
101
113
|
onError?: (error: Error) => void;
|
@@ -106,11 +118,11 @@ export interface Node<T> {
|
|
106
118
|
* @interface
|
107
119
|
* @template T - Schema type
|
108
120
|
*/
|
109
|
-
export interface GraphDefinition<T> {
|
121
|
+
export interface GraphDefinition<T extends ZodSchema> {
|
110
122
|
/** Name of the graph */
|
111
123
|
name: string;
|
112
124
|
/** Array of nodes in the graph */
|
113
|
-
nodes: Node<T>[];
|
125
|
+
nodes: Node<T, any>[];
|
114
126
|
/** Initial context */
|
115
127
|
context: SchemaType<T>;
|
116
128
|
/** Schema for validation */
|
@@ -120,7 +132,7 @@ export interface GraphDefinition<T> {
|
|
120
132
|
/** Entry node name */
|
121
133
|
entryNode?: string;
|
122
134
|
/** Event emitter instance */
|
123
|
-
eventEmitter?: IEventEmitter;
|
135
|
+
eventEmitter?: IEventEmitter | EventEmitter;
|
124
136
|
/** Array of events */
|
125
137
|
events?: string[];
|
126
138
|
}
|