@ai.ntellect/core 0.6.0 → 0.6.2
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/create-llm-to-select-multiple-graph copy.ts +243 -0
- package/create-llm-to-select-multiple-graph.ts +148 -0
- package/dist/create-llm-to-select-multiple-graph copy.js +201 -0
- package/dist/create-llm-to-select-multiple-graph.js +142 -0
- package/dist/graph/controller.js +6 -6
- package/dist/graph/engine.js +198 -135
- package/dist/index copy.js +76 -0
- package/dist/utils/setup-graphs.js +28 -0
- package/dist/utils/stringifiy-zod-schema.js +41 -0
- package/graph/controller.ts +11 -9
- package/graph/engine.ts +244 -166
- package/index copy.ts +81 -0
- package/index.ts +1 -1
- package/package.json +1 -1
- package/test/graph/engine.test.ts +27 -44
- package/tsconfig.json +1 -1
- package/types/index.ts +11 -3
- package/utils/setup-graphs.ts +45 -0
- package/utils/stringifiy-zod-schema.ts +45 -0
- package/dist/test/graph/controller.test.js +0 -170
- package/dist/test/graph/engine.test.js +0 -465
- package/dist/test/memory/adapters/meilisearch.test.js +0 -250
- package/dist/test/memory/adapters/redis.test.js +0 -143
- package/dist/test/memory/base.test.js +0 -209
- package/dist/test/services/agenda.test.js +0 -230
- package/dist/test/services/queue.test.js +0 -258
- package/dist/utils/schema-generator.js +0 -46
- package/dist/utils/state-manager.js +0 -20
- package/utils/generate-object.js +0 -111
- package/utils/header-builder.js +0 -34
- package/utils/inject-actions.js +0 -16
- package/utils/queue-item-transformer.js +0 -24
- package/utils/sanitize-results.js +0 -60
- package/utils/schema-generator.js +0 -46
- package/utils/schema-generator.ts +0 -73
- package/utils/state-manager.js +0 -20
- package/utils/state-manager.ts +0 -30
package/graph/engine.ts
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
import { Persistence, RealTimeNotifier } from "@/interfaces";
|
2
|
-
import { GraphDefinition, Node,
|
3
|
-
import { configDotenv } from "dotenv";
|
2
|
+
import { GraphDefinition, Node, SharedState } from "@/types";
|
4
3
|
import EventEmitter from "events";
|
5
4
|
import { z } from "zod";
|
6
5
|
|
7
|
-
configDotenv();
|
8
|
-
|
9
6
|
interface GraphOptions<T> {
|
10
7
|
initialState?: SharedState<T>;
|
11
8
|
schema?: z.ZodSchema<T>;
|
@@ -13,44 +10,43 @@ interface GraphOptions<T> {
|
|
13
10
|
}
|
14
11
|
|
15
12
|
/**
|
16
|
-
*
|
17
|
-
* The worflow can handle state management, event emissions, and conditional execution paths.
|
13
|
+
* Représente un workflow dirigé capable d’exécuter des noeuds en séquence ou en parallèle.
|
18
14
|
*
|
19
|
-
* @template T -
|
15
|
+
* @template T - Le type de données stockées dans le contexte du workflow
|
20
16
|
*/
|
21
17
|
export class GraphEngine<T> {
|
22
|
-
/**
|
18
|
+
/** Données globales accessibles à tous les nœuds */
|
23
19
|
public globalContext: Map<string, any>;
|
24
20
|
|
25
|
-
/** Event emitter
|
21
|
+
/** Event emitter pour gérer les événements du workflow */
|
26
22
|
private eventEmitter: EventEmitter;
|
27
23
|
|
28
|
-
/** Map
|
24
|
+
/** Map de tous les nœuds du workflow */
|
29
25
|
public nodes: Map<string, Node<T>>;
|
30
26
|
|
31
|
-
/**
|
27
|
+
/** Ensemble des nœuds déjà exécutés */
|
32
28
|
public executedNodes: Set<string>;
|
33
29
|
|
34
|
-
/**
|
30
|
+
/** Nom du workflow */
|
35
31
|
public name: string;
|
36
32
|
|
37
|
-
/**
|
33
|
+
/** Couche de persistance optionnelle pour sauvegarder l'état du workflow */
|
38
34
|
private persistence: Persistence<T> | null;
|
39
35
|
|
40
|
-
/**
|
36
|
+
/** Notifier en temps réel optionnel */
|
41
37
|
private notifier: RealTimeNotifier | null;
|
42
38
|
|
39
|
+
/** Schéma global Zod pour valider l’état ou le contexte du workflow */
|
43
40
|
private schema?: z.ZodSchema<T>;
|
44
41
|
|
42
|
+
/** État interne actuel du workflow */
|
45
43
|
private currentState: SharedState<T>;
|
46
44
|
|
47
45
|
/**
|
48
|
-
*
|
46
|
+
* Crée une nouvelle instance de GraphEngine.
|
49
47
|
*
|
50
|
-
* @param {GraphDefinition<T>} [definition] -
|
51
|
-
* @param {
|
52
|
-
* @param {boolean} [config.autoDetectCycles] - Whether to check for cycles during initialization
|
53
|
-
* @throws {Error} If cycles are detected when autoDetectCycles is true
|
48
|
+
* @param {GraphDefinition<T>} [definition] - La définition initiale du workflow
|
49
|
+
* @param {GraphOptions<T>} [options] - Options de configuration
|
54
50
|
*/
|
55
51
|
constructor(definition?: GraphDefinition<T>, options?: GraphOptions<T>) {
|
56
52
|
this.name = definition?.name || "anonymous";
|
@@ -61,14 +57,14 @@ export class GraphEngine<T> {
|
|
61
57
|
this.persistence = null;
|
62
58
|
this.notifier = null;
|
63
59
|
this.schema = options?.schema;
|
64
|
-
this.currentState = {
|
60
|
+
this.currentState = {} as SharedState<T>;
|
65
61
|
|
66
62
|
if (definition) {
|
67
63
|
this.loadFromDefinition(definition);
|
68
64
|
}
|
69
65
|
|
70
66
|
if (options?.autoDetectCycles && this.checkForCycles()) {
|
71
|
-
throw new Error("Cycle
|
67
|
+
throw new Error("Cycle détecté dans le workflow");
|
72
68
|
}
|
73
69
|
|
74
70
|
if (options?.initialState) {
|
@@ -77,68 +73,63 @@ export class GraphEngine<T> {
|
|
77
73
|
}
|
78
74
|
|
79
75
|
/**
|
80
|
-
*
|
81
|
-
* @param {string} key -
|
82
|
-
* @param {any} value -
|
76
|
+
* Ajoute un élément au contexte global.
|
77
|
+
* @param {string} key - La clé
|
78
|
+
* @param {any} value - La valeur
|
83
79
|
*/
|
84
80
|
addToContext(key: string, value: any): void {
|
85
81
|
this.globalContext.set(key, value);
|
86
82
|
}
|
87
83
|
|
88
84
|
/**
|
89
|
-
*
|
90
|
-
* @param {string} key -
|
91
|
-
* @returns {any} The stored value, or undefined if not found
|
85
|
+
* Récupère un élément du contexte global.
|
86
|
+
* @param {string} key - La clé
|
92
87
|
*/
|
93
88
|
getContext(key: string): any {
|
94
89
|
return this.globalContext.get(key);
|
95
90
|
}
|
96
91
|
|
97
92
|
/**
|
98
|
-
*
|
99
|
-
* @param {string} key -
|
93
|
+
* Supprime un élément du contexte global.
|
94
|
+
* @param {string} key - La clé
|
100
95
|
*/
|
101
96
|
removeFromContext(key: string): void {
|
102
97
|
this.globalContext.delete(key);
|
103
98
|
}
|
104
99
|
|
105
100
|
/**
|
106
|
-
*
|
107
|
-
* @param {Persistence<T>} persistence
|
101
|
+
* Définit la couche de persistance.
|
102
|
+
* @param {Persistence<T>} persistence
|
108
103
|
*/
|
109
104
|
setPersistence(persistence: Persistence<T>): void {
|
110
105
|
this.persistence = persistence;
|
111
106
|
}
|
112
107
|
|
113
108
|
/**
|
114
|
-
*
|
115
|
-
* @param {RealTimeNotifier} notifier
|
109
|
+
* Définit le notifier en temps réel.
|
110
|
+
* @param {RealTimeNotifier} notifier
|
116
111
|
*/
|
117
112
|
setNotifier(notifier: RealTimeNotifier): void {
|
118
113
|
this.notifier = notifier;
|
119
114
|
}
|
120
115
|
|
121
116
|
/**
|
122
|
-
*
|
117
|
+
* Charge un workflow à partir d'une définition.
|
123
118
|
* @private
|
124
|
-
* @param {GraphDefinition<T>} definition
|
119
|
+
* @param {GraphDefinition<T>} definition
|
125
120
|
*/
|
126
121
|
private loadFromDefinition(definition: GraphDefinition<T>): void {
|
127
122
|
Object.entries(definition.nodes).forEach(([_, nodeConfig]) => {
|
128
|
-
this.addNode(nodeConfig
|
129
|
-
condition: nodeConfig.condition,
|
130
|
-
relationships: nodeConfig.relationships,
|
131
|
-
});
|
123
|
+
this.addNode(nodeConfig);
|
132
124
|
});
|
133
125
|
}
|
134
126
|
|
135
127
|
/**
|
136
|
-
*
|
137
|
-
* @
|
138
|
-
* @param {string}
|
139
|
-
* @param {Set<string>}
|
140
|
-
* @
|
141
|
-
* @returns {boolean} True if a cycle is detected, false otherwise
|
128
|
+
* Vérifie récursivement s’il existe un cycle dans le workflow.
|
129
|
+
* @param {string} nodeName
|
130
|
+
* @param {Set<string>} visited
|
131
|
+
* @param {Set<string>} recStack
|
132
|
+
* @returns {boolean}
|
142
133
|
*/
|
143
134
|
private isCyclic(
|
144
135
|
nodeName: string,
|
@@ -169,8 +160,8 @@ export class GraphEngine<T> {
|
|
169
160
|
}
|
170
161
|
|
171
162
|
/**
|
172
|
-
*
|
173
|
-
* @returns {boolean}
|
163
|
+
* Vérifie si le workflow contient des cycles.
|
164
|
+
* @returns {boolean}
|
174
165
|
*/
|
175
166
|
public checkForCycles(): boolean {
|
176
167
|
const visited = new Set<string>();
|
@@ -185,30 +176,18 @@ export class GraphEngine<T> {
|
|
185
176
|
}
|
186
177
|
|
187
178
|
/**
|
188
|
-
*
|
189
|
-
* @param {Node<T>} node
|
190
|
-
* @param {Object} options - Node configuration options
|
191
|
-
* @param {Function} [options.condition] - Condition function for node execution
|
192
|
-
* @param {string[]} [options.relations] - Array of relations node names
|
193
|
-
* @param {string[]} [options.events] - Array of event names to listen for
|
179
|
+
* Ajoute un nouveau nœud au workflow.
|
180
|
+
* @param {Node<T>} node
|
194
181
|
*/
|
195
|
-
addNode(
|
196
|
-
node
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
events,
|
201
|
-
}: {
|
202
|
-
condition?: (state: SharedState<T>) => boolean;
|
203
|
-
relationships?: NodeRelationship[];
|
204
|
-
events?: string[];
|
182
|
+
addNode(node: Node<T>): void {
|
183
|
+
if (node.relationships) {
|
184
|
+
node.relationships.forEach((relationship) => {
|
185
|
+
this.nodes.get(relationship.name)?.relationships?.push(relationship);
|
186
|
+
});
|
205
187
|
}
|
206
|
-
): void {
|
207
|
-
node.relationships = relationships;
|
208
|
-
node.condition = condition;
|
209
188
|
|
210
|
-
if (events) {
|
211
|
-
events.forEach((event) => {
|
189
|
+
if (node.events) {
|
190
|
+
node.events.forEach((event) => {
|
212
191
|
this.eventEmitter.on(event, async (data) => {
|
213
192
|
const state = data.state || {};
|
214
193
|
await this.execute(state, node.name);
|
@@ -220,19 +199,19 @@ export class GraphEngine<T> {
|
|
220
199
|
}
|
221
200
|
|
222
201
|
/**
|
223
|
-
*
|
224
|
-
* @param {string} eventName
|
225
|
-
* @param {any} data
|
202
|
+
* Émet un événement sur l'event emitter du workflow.
|
203
|
+
* @param {string} eventName
|
204
|
+
* @param {any} data
|
226
205
|
*/
|
227
206
|
public emit(eventName: string, data: any): void {
|
228
207
|
this.eventEmitter.emit(eventName, data);
|
229
208
|
}
|
230
209
|
|
231
210
|
/**
|
232
|
-
*
|
233
|
-
* @param {
|
234
|
-
* @param {string} entryNode -
|
235
|
-
* @param {string} name -
|
211
|
+
* Ajoute un sous-graph (GraphEngine) comme un nœud dans le workflow courant.
|
212
|
+
* @param {GraphEngine<T>} subGraph
|
213
|
+
* @param {string} entryNode - Le nom du nœud de démarrage dans le sous-graph
|
214
|
+
* @param {string} name - Le nom symbolique à donner au sous-graph
|
236
215
|
*/
|
237
216
|
addSubGraph(subGraph: GraphEngine<T>, entryNode: string, name: string): void {
|
238
217
|
const subGraphNode: Node<T> = {
|
@@ -246,25 +225,26 @@ export class GraphEngine<T> {
|
|
246
225
|
}
|
247
226
|
|
248
227
|
/**
|
249
|
-
*
|
250
|
-
* @param {SharedState<T>} state
|
251
|
-
* @param {string} startNode
|
252
|
-
* @param {
|
253
|
-
* @param {
|
228
|
+
* Exécute le workflow à partir d’un nœud donné.
|
229
|
+
* @param {SharedState<T>} state
|
230
|
+
* @param {string} startNode
|
231
|
+
* @param {(state: SharedState<T>) => void} [onStream] - Callback sur l’évolution de l’état
|
232
|
+
* @param {(error: Error, nodeName: string, state: SharedState<T>) => void} [onError] - Callback sur erreur
|
254
233
|
*/
|
255
234
|
async execute(
|
256
235
|
state: SharedState<T>,
|
257
236
|
startNode: string,
|
258
|
-
onStream?: (
|
237
|
+
onStream?: (graph: GraphEngine<T>) => void,
|
259
238
|
onError?: (error: Error, nodeName: string, state: SharedState<T>) => void
|
260
239
|
): Promise<SharedState<T>> {
|
261
240
|
try {
|
241
|
+
// Valide l'état initial via le schéma global (si défini)
|
262
242
|
if (this.schema) {
|
263
243
|
try {
|
264
|
-
this.schema.parse(state
|
244
|
+
this.schema.parse(state);
|
265
245
|
} catch (error) {
|
266
246
|
const validationError = new Error(
|
267
|
-
|
247
|
+
`Échec de la validation de l'état initial: ${
|
268
248
|
error instanceof Error ? error.message : error
|
269
249
|
}`
|
270
250
|
);
|
@@ -279,8 +259,11 @@ export class GraphEngine<T> {
|
|
279
259
|
while (currentNodeName) {
|
280
260
|
this.executedNodes.add(currentNodeName);
|
281
261
|
const currentNode = this.nodes.get(currentNodeName);
|
282
|
-
if (!currentNode)
|
262
|
+
if (!currentNode) {
|
263
|
+
throw new Error(`Nœud ${currentNodeName} introuvable.`);
|
264
|
+
}
|
283
265
|
|
266
|
+
// Vérification de condition (si présente)
|
284
267
|
if (
|
285
268
|
currentNode.condition &&
|
286
269
|
!currentNode.condition(this.currentState)
|
@@ -289,6 +272,7 @@ export class GraphEngine<T> {
|
|
289
272
|
}
|
290
273
|
|
291
274
|
try {
|
275
|
+
// Notifier : début d'exécution du nœud
|
292
276
|
if (this.notifier) {
|
293
277
|
this.notifier.notify("nodeExecutionStarted", {
|
294
278
|
workflow: this.name,
|
@@ -304,9 +288,10 @@ export class GraphEngine<T> {
|
|
304
288
|
|
305
289
|
if (newState) {
|
306
290
|
this.setState(newState);
|
307
|
-
if (onStream) onStream(this
|
291
|
+
if (onStream) onStream(this);
|
308
292
|
}
|
309
293
|
|
294
|
+
// Sauvegarde via la persistence (optionnel)
|
310
295
|
if (this.persistence) {
|
311
296
|
await this.persistence.saveState(
|
312
297
|
this.name,
|
@@ -315,6 +300,7 @@ export class GraphEngine<T> {
|
|
315
300
|
);
|
316
301
|
}
|
317
302
|
|
303
|
+
// Notifier : fin d'exécution du nœud
|
318
304
|
if (this.notifier) {
|
319
305
|
await this.notifier.notify("nodeExecutionCompleted", {
|
320
306
|
workflow: this.name,
|
@@ -323,8 +309,9 @@ export class GraphEngine<T> {
|
|
323
309
|
});
|
324
310
|
}
|
325
311
|
} catch (error) {
|
326
|
-
if (onError)
|
312
|
+
if (onError) {
|
327
313
|
onError(error as Error, currentNodeName, this.currentState);
|
314
|
+
}
|
328
315
|
if (this.notifier) {
|
329
316
|
this.notifier.notify("nodeExecutionFailed", {
|
330
317
|
workflow: this.name,
|
@@ -336,15 +323,19 @@ export class GraphEngine<T> {
|
|
336
323
|
break;
|
337
324
|
}
|
338
325
|
|
326
|
+
// Gestion des relations (branchements)
|
339
327
|
const relationsNodes = currentNode.relationships || [];
|
340
328
|
if (relationsNodes.length > 1) {
|
329
|
+
// Exécution parallèle des branches
|
341
330
|
await Promise.all(
|
342
331
|
relationsNodes.map((relation) =>
|
343
332
|
this.execute(this.currentState, relation.name, onStream, onError)
|
344
333
|
)
|
345
334
|
);
|
335
|
+
// Après exécution en parallèle, on arrête la boucle
|
346
336
|
break;
|
347
337
|
} else {
|
338
|
+
// Cas normal : un seul chemin
|
348
339
|
currentNodeName = relationsNodes[0]?.name || "";
|
349
340
|
}
|
350
341
|
}
|
@@ -359,18 +350,16 @@ export class GraphEngine<T> {
|
|
359
350
|
}
|
360
351
|
|
361
352
|
/**
|
362
|
-
*
|
363
|
-
* @param {SharedState<T>} state
|
364
|
-
* @param {string[]} nodeNames
|
365
|
-
* @param {number} [concurrencyLimit=5]
|
366
|
-
* @param {Function} [onStream] - Callback for streaming state updates
|
367
|
-
* @param {Function} [onError] - Callback for handling errors
|
353
|
+
* Exécute plusieurs nœuds en parallèle au sein du même workflow, avec une limite de concurrence.
|
354
|
+
* @param {SharedState<T>} state
|
355
|
+
* @param {string[]} nodeNames
|
356
|
+
* @param {number} [concurrencyLimit=5]
|
368
357
|
*/
|
369
358
|
async executeParallel(
|
370
359
|
state: SharedState<T>,
|
371
360
|
nodeNames: string[],
|
372
361
|
concurrencyLimit: number = 5,
|
373
|
-
onStream?: (
|
362
|
+
onStream?: (graph: GraphEngine<T>) => void,
|
374
363
|
onError?: (error: Error, nodeName: string, state: SharedState<T>) => void
|
375
364
|
): Promise<void> {
|
376
365
|
const executeWithLimit = async (nodeName: string) => {
|
@@ -388,8 +377,8 @@ export class GraphEngine<T> {
|
|
388
377
|
}
|
389
378
|
|
390
379
|
/**
|
391
|
-
*
|
392
|
-
* @param {GraphDefinition<T>} definition
|
380
|
+
* Met à jour le workflow avec une nouvelle définition (mise à jour des nœuds existants ou ajout de nouveaux).
|
381
|
+
* @param {GraphDefinition<T>} definition
|
393
382
|
*/
|
394
383
|
updateGraph(definition: GraphDefinition<T>): void {
|
395
384
|
Object.entries(definition.nodes).forEach(([_, nodeConfig]) => {
|
@@ -398,18 +387,16 @@ export class GraphEngine<T> {
|
|
398
387
|
existingNode.relationships =
|
399
388
|
nodeConfig.relationships || existingNode.relationships;
|
400
389
|
existingNode.condition = nodeConfig.condition || existingNode.condition;
|
390
|
+
existingNode.events = nodeConfig.events || existingNode.events;
|
401
391
|
} else {
|
402
|
-
this.addNode(nodeConfig
|
403
|
-
condition: nodeConfig.condition,
|
404
|
-
relationships: nodeConfig.relationships,
|
405
|
-
});
|
392
|
+
this.addNode(nodeConfig);
|
406
393
|
}
|
407
394
|
});
|
408
395
|
}
|
409
396
|
|
410
397
|
/**
|
411
|
-
*
|
412
|
-
* @param {GraphDefinition<T>} definition
|
398
|
+
* Remplace complètement le workflow par une nouvelle définition.
|
399
|
+
* @param {GraphDefinition<T>} definition
|
413
400
|
*/
|
414
401
|
replaceGraph(definition: GraphDefinition<T>): void {
|
415
402
|
this.nodes.clear();
|
@@ -417,14 +404,9 @@ export class GraphEngine<T> {
|
|
417
404
|
}
|
418
405
|
|
419
406
|
/**
|
420
|
-
*
|
421
|
-
*
|
422
|
-
*
|
423
|
-
* - Event nodes (yellow)
|
424
|
-
* - Conditional nodes (orange)
|
425
|
-
*
|
426
|
-
* @param {string} [title] - Optional title for the diagram
|
427
|
-
* @returns {string} Mermaid diagram syntax representing the worflow
|
407
|
+
* Génère un diagramme Mermaid pour visualiser le workflow.
|
408
|
+
* @param {string} [title]
|
409
|
+
* @returns {string}
|
428
410
|
*/
|
429
411
|
generateMermaidDiagram(title?: string): string {
|
430
412
|
const lines: string[] = ["flowchart TD"];
|
@@ -433,48 +415,46 @@ export class GraphEngine<T> {
|
|
433
415
|
lines.push(` subgraph ${title}`);
|
434
416
|
}
|
435
417
|
|
436
|
-
//
|
418
|
+
// Ajout des nœuds
|
437
419
|
this.nodes.forEach((node, nodeName) => {
|
438
420
|
const hasEvents = node.events && node.events.length > 0;
|
439
421
|
const hasCondition = !!node.condition;
|
440
422
|
|
441
|
-
// Style
|
423
|
+
// Style selon les propriétés
|
442
424
|
let style = "";
|
443
425
|
if (hasEvents) {
|
444
|
-
style = "style " + nodeName + " fill:#FFD700,stroke:#DAA520"; //
|
426
|
+
style = "style " + nodeName + " fill:#FFD700,stroke:#DAA520"; // Jaune pour event
|
445
427
|
} else if (hasCondition) {
|
446
|
-
style = "style " + nodeName + " fill:#FFA500,stroke:#FF8C00"; // Orange
|
428
|
+
style = "style " + nodeName + " fill:#FFA500,stroke:#FF8C00"; // Orange pour condition
|
447
429
|
}
|
448
430
|
|
449
|
-
// Add node definition
|
450
431
|
lines.push(` ${nodeName}[${nodeName}]`);
|
451
432
|
if (style) {
|
452
433
|
lines.push(` ${style}`);
|
453
434
|
}
|
454
435
|
});
|
455
436
|
|
456
|
-
//
|
437
|
+
// Ajout des connexions
|
457
438
|
this.nodes.forEach((node, nodeName) => {
|
458
439
|
if (node.relationships) {
|
459
440
|
node.relationships.forEach((relationsNode) => {
|
460
441
|
let connectionStyle = "";
|
461
442
|
if (node.condition) {
|
462
|
-
connectionStyle = "---|condition|";
|
443
|
+
connectionStyle = "---|condition|";
|
463
444
|
} else {
|
464
|
-
connectionStyle = "-->";
|
445
|
+
connectionStyle = "-->";
|
465
446
|
}
|
466
447
|
lines.push(` ${nodeName} ${connectionStyle} ${relationsNode}`);
|
467
448
|
});
|
468
449
|
}
|
469
450
|
|
470
|
-
//
|
451
|
+
// Gestion des events
|
471
452
|
if (node.events && node.events.length > 0) {
|
472
453
|
node.events.forEach((event: string) => {
|
473
454
|
const eventNodeId = `${event}_event`;
|
474
455
|
lines.push(` ${eventNodeId}((${event})):::event`);
|
475
456
|
lines.push(` ${eventNodeId} -.->|trigger| ${nodeName}`);
|
476
457
|
});
|
477
|
-
// Add style class for event nodes
|
478
458
|
lines.push(" classDef event fill:#FFD700,stroke:#DAA520");
|
479
459
|
}
|
480
460
|
});
|
@@ -487,26 +467,29 @@ export class GraphEngine<T> {
|
|
487
467
|
}
|
488
468
|
|
489
469
|
/**
|
490
|
-
*
|
491
|
-
*
|
492
|
-
*
|
493
|
-
* @param {string} [title] - Optional title for the visualization
|
470
|
+
* Affiche le diagramme Mermaid dans la console.
|
471
|
+
* @param {string} [title]
|
494
472
|
*/
|
495
473
|
visualize(title?: string): void {
|
496
474
|
const diagram = this.generateMermaidDiagram(title);
|
497
475
|
console.log(
|
498
|
-
"
|
476
|
+
"Pour visualiser ce workflow, utilisez un rendu compatible Mermaid avec la syntaxe suivante :"
|
499
477
|
);
|
500
478
|
console.log("\n```mermaid");
|
501
479
|
console.log(diagram);
|
502
480
|
console.log("```\n");
|
503
481
|
}
|
504
482
|
|
505
|
-
|
483
|
+
/**
|
484
|
+
* Exporte la définition du workflow au format JSON (pour debug ou documentation).
|
485
|
+
* @param {GraphDefinition<T>} workflow
|
486
|
+
* @returns {string} JSON string
|
487
|
+
*/
|
488
|
+
exportGraphToJson(workflow: GraphDefinition<T>): string {
|
506
489
|
const result = {
|
507
|
-
|
508
|
-
entryNode:
|
509
|
-
nodes: Object.entries(
|
490
|
+
workflowName: workflow.name,
|
491
|
+
entryNode: workflow.entryNode,
|
492
|
+
nodes: Object.entries(workflow.nodes).reduce((acc, [key, node]) => {
|
510
493
|
acc[key] = {
|
511
494
|
name: node.name,
|
512
495
|
description: node.description || "No description provided",
|
@@ -521,10 +504,8 @@ export class GraphEngine<T> {
|
|
521
504
|
}
|
522
505
|
|
523
506
|
/**
|
524
|
-
*
|
525
|
-
*
|
526
|
-
*
|
527
|
-
* @returns {string} A formatted string describing the workflow schema
|
507
|
+
* Génère une représentation textuelle (console) du schéma du workflow.
|
508
|
+
* @returns {string}
|
528
509
|
*/
|
529
510
|
visualizeSchema(): string {
|
530
511
|
const output: string[] = [];
|
@@ -532,6 +513,7 @@ export class GraphEngine<T> {
|
|
532
513
|
output.push(`📋 Graph: ${this.name}`);
|
533
514
|
output.push("=".repeat(50));
|
534
515
|
|
516
|
+
// Schéma global
|
535
517
|
if (this.schema) {
|
536
518
|
output.push("🔷 Global Schema:");
|
537
519
|
output.push("-".repeat(30));
|
@@ -547,6 +529,7 @@ export class GraphEngine<T> {
|
|
547
529
|
output.push("");
|
548
530
|
}
|
549
531
|
|
532
|
+
// Détails des nœuds
|
550
533
|
output.push("🔷 Nodes:");
|
551
534
|
output.push("-".repeat(30));
|
552
535
|
|
@@ -557,7 +540,8 @@ export class GraphEngine<T> {
|
|
557
540
|
);
|
558
541
|
|
559
542
|
if (node.relationships && node.relationships.length > 0) {
|
560
|
-
|
543
|
+
const rels = node.relationships.map((r) => r.name).join(", ");
|
544
|
+
output.push(`Next nodes: ${rels}`);
|
561
545
|
}
|
562
546
|
|
563
547
|
output.push("");
|
@@ -567,7 +551,10 @@ export class GraphEngine<T> {
|
|
567
551
|
}
|
568
552
|
|
569
553
|
/**
|
570
|
-
*
|
554
|
+
* Décrit récursivement un type Zod pour l'affichage.
|
555
|
+
* @param {z.ZodType} type
|
556
|
+
* @param {number} indent
|
557
|
+
* @returns {string}
|
571
558
|
*/
|
572
559
|
public describeZodType(type: z.ZodType, indent: number = 0): string {
|
573
560
|
const padding = " ".repeat(indent);
|
@@ -623,38 +610,35 @@ export class GraphEngine<T> {
|
|
623
610
|
}
|
624
611
|
|
625
612
|
/**
|
626
|
-
*
|
627
|
-
* @param {SharedState<T>} state
|
628
|
-
* @param {Partial<T>} updates
|
629
|
-
* @returns {SharedState<T>}
|
613
|
+
* Met à jour le contexte du workflow pour un nœud, en renvoyant un nouvel état.
|
614
|
+
* @param {SharedState<T>} state
|
615
|
+
* @param {Partial<T>} updates
|
616
|
+
* @returns {SharedState<T>}
|
630
617
|
*/
|
631
618
|
protected updateNodeState(state: SharedState<T>, updates: Partial<T>) {
|
632
619
|
return {
|
633
620
|
...state,
|
634
|
-
|
635
|
-
...(state.context || {}),
|
636
|
-
...updates,
|
637
|
-
},
|
621
|
+
...updates,
|
638
622
|
};
|
639
623
|
}
|
640
624
|
|
641
625
|
/**
|
642
|
-
*
|
643
|
-
* @returns {SharedState<T>}
|
626
|
+
* Récupère l'état courant du workflow.
|
627
|
+
* @returns {SharedState<T>}
|
644
628
|
*/
|
645
629
|
public getState(): SharedState<T> {
|
646
630
|
return this.currentState;
|
647
631
|
}
|
648
632
|
|
649
633
|
/**
|
650
|
-
*
|
651
|
-
* @param {Partial<SharedState<T>>} state
|
634
|
+
* Définit le nouvel état courant du workflow et met à jour le contexte global.
|
635
|
+
* @param {Partial<SharedState<T>>} state
|
652
636
|
*/
|
653
637
|
public setState(state: Partial<SharedState<T>>): void {
|
654
638
|
this.currentState = this.mergeStates(this.currentState, state);
|
655
639
|
|
656
|
-
if (state
|
657
|
-
Object.entries(state
|
640
|
+
if (state) {
|
641
|
+
Object.entries(state).forEach(([key, value]) => {
|
658
642
|
this.globalContext.set(key, value);
|
659
643
|
});
|
660
644
|
}
|
@@ -664,17 +648,17 @@ export class GraphEngine<T> {
|
|
664
648
|
if (node) {
|
665
649
|
node.state = {
|
666
650
|
...(node.state || {}),
|
667
|
-
...(state
|
651
|
+
...(state || {}),
|
668
652
|
};
|
669
653
|
}
|
670
654
|
}
|
671
655
|
}
|
672
656
|
|
673
657
|
/**
|
674
|
-
*
|
675
|
-
* @param {SharedState<T>} currentState
|
676
|
-
* @param {Partial<SharedState<T>>} newState
|
677
|
-
* @returns {SharedState<T>}
|
658
|
+
* Fusionne deux états.
|
659
|
+
* @param {SharedState<T>} currentState
|
660
|
+
* @param {Partial<SharedState<T>>} newState
|
661
|
+
* @returns {SharedState<T>}
|
678
662
|
*/
|
679
663
|
private mergeStates(
|
680
664
|
currentState: SharedState<T>,
|
@@ -682,28 +666,122 @@ export class GraphEngine<T> {
|
|
682
666
|
): SharedState<T> {
|
683
667
|
return {
|
684
668
|
...currentState,
|
685
|
-
|
686
|
-
...(currentState.context || {}),
|
687
|
-
...(newState.context || {}),
|
688
|
-
},
|
669
|
+
...(newState || {}),
|
689
670
|
};
|
690
671
|
}
|
691
672
|
|
692
673
|
/**
|
693
|
-
*
|
694
|
-
* @param {Partial<
|
695
|
-
* @returns {SharedState<T>}
|
674
|
+
* Met à jour l'état courant et le renvoie.
|
675
|
+
* @param {Partial<T>} updates
|
676
|
+
* @returns {SharedState<T>}
|
696
677
|
*/
|
697
|
-
public updateState(updates: Partial<
|
678
|
+
public updateState(updates: Partial<T>): SharedState<T> {
|
698
679
|
const currentState = this.getState();
|
699
|
-
const newState = {
|
680
|
+
const newState: SharedState<T> = {
|
700
681
|
...currentState,
|
701
|
-
|
702
|
-
...currentState.context,
|
703
|
-
...(updates.context || {}),
|
704
|
-
},
|
682
|
+
...updates,
|
705
683
|
};
|
706
684
|
this.setState(newState);
|
707
685
|
return newState;
|
708
686
|
}
|
687
|
+
|
688
|
+
/* =============================================
|
689
|
+
= MÉTHODES STATIQUES POUR PLUSIEURS GRAPHES =
|
690
|
+
============================================= */
|
691
|
+
|
692
|
+
/**
|
693
|
+
* Exécute plusieurs GraphEngine en **séquence** (l'un après l'autre).
|
694
|
+
* @param graphs Liste des graphes à exécuter
|
695
|
+
* @param startNodes Noms des nœuds de départ correspondants
|
696
|
+
* @param initialStates États initiaux correspondants
|
697
|
+
* @param onStream Callback d'avancement
|
698
|
+
* @param onError Callback d'erreur
|
699
|
+
* @returns Tableau des états finaux de chaque graphe
|
700
|
+
*/
|
701
|
+
public static async executeGraphsInSequence<U>(
|
702
|
+
graphs: GraphEngine<U>[],
|
703
|
+
startNodes: string[],
|
704
|
+
initialStates: SharedState<U>[],
|
705
|
+
onStream?: (graph: GraphEngine<U>) => void,
|
706
|
+
onError?: (error: Error, nodeName: string, state: SharedState<U>) => void
|
707
|
+
): Promise<SharedState<U>[]> {
|
708
|
+
const finalStates: SharedState<U>[] = [];
|
709
|
+
|
710
|
+
for (let i = 0; i < graphs.length; i++) {
|
711
|
+
const graph = graphs[i];
|
712
|
+
const startNode = startNodes[i];
|
713
|
+
const initialState = initialStates[i];
|
714
|
+
const result = await graph.execute(
|
715
|
+
initialState,
|
716
|
+
startNode,
|
717
|
+
onStream,
|
718
|
+
onError
|
719
|
+
);
|
720
|
+
finalStates.push(result);
|
721
|
+
}
|
722
|
+
|
723
|
+
return finalStates;
|
724
|
+
}
|
725
|
+
|
726
|
+
/**
|
727
|
+
* Exécute plusieurs GraphEngine en **parallèle** (sans limite de concurrence).
|
728
|
+
* @param graphs Liste des graphes
|
729
|
+
* @param startNodes Noms des nœuds de départ
|
730
|
+
* @param initialStates États initiaux
|
731
|
+
* @param onStream Callback d'avancement
|
732
|
+
* @param onError Callback d'erreur
|
733
|
+
* @returns Tableau des états finaux de chaque graphe
|
734
|
+
*/
|
735
|
+
public static async executeGraphsInParallel<U>(
|
736
|
+
graphs: GraphEngine<U>[],
|
737
|
+
startNodes: string[],
|
738
|
+
initialStates: SharedState<U>[],
|
739
|
+
onStream?: (graph: GraphEngine<U>) => void,
|
740
|
+
onError?: (error: Error, nodeName: string, state: SharedState<U>) => void
|
741
|
+
): Promise<SharedState<U>[]> {
|
742
|
+
const promises = graphs.map((graph, index) =>
|
743
|
+
graph.execute(initialStates[index], startNodes[index], onStream, onError)
|
744
|
+
);
|
745
|
+
return Promise.all(promises);
|
746
|
+
}
|
747
|
+
|
748
|
+
/**
|
749
|
+
* Exécute plusieurs GraphEngine en parallèle **avec une limite de concurrence**.
|
750
|
+
* @param graphs Liste des graphes
|
751
|
+
* @param startNodes Noms des nœuds de départ
|
752
|
+
* @param initialStates États initiaux
|
753
|
+
* @param concurrencyLimit Limite de concurrence
|
754
|
+
* @param onStream Callback d'avancement
|
755
|
+
* @param onError Callback d'erreur
|
756
|
+
* @returns Tableau des états finaux de chaque graphe
|
757
|
+
*/
|
758
|
+
public static async executeGraphsWithConcurrencyLimit<U>(
|
759
|
+
graphs: GraphEngine<U>[],
|
760
|
+
startNodes: string[],
|
761
|
+
initialStates: SharedState<U>[],
|
762
|
+
concurrencyLimit: number,
|
763
|
+
onStream?: (graph: GraphEngine<U>) => void,
|
764
|
+
onError?: (error: Error, nodeName: string, state: SharedState<U>) => void
|
765
|
+
): Promise<SharedState<U>[]> {
|
766
|
+
const results: SharedState<U>[] = [];
|
767
|
+
|
768
|
+
for (let i = 0; i < graphs.length; i += concurrencyLimit) {
|
769
|
+
const chunkGraphs = graphs.slice(i, i + concurrencyLimit);
|
770
|
+
const chunkStartNodes = startNodes.slice(i, i + concurrencyLimit);
|
771
|
+
const chunkInitialStates = initialStates.slice(i, i + concurrencyLimit);
|
772
|
+
|
773
|
+
const chunkPromises = chunkGraphs.map((graph, index) => {
|
774
|
+
return graph.execute(
|
775
|
+
chunkInitialStates[index],
|
776
|
+
chunkStartNodes[index],
|
777
|
+
onStream,
|
778
|
+
onError
|
779
|
+
);
|
780
|
+
});
|
781
|
+
const chunkResults = await Promise.all(chunkPromises);
|
782
|
+
results.push(...chunkResults);
|
783
|
+
}
|
784
|
+
|
785
|
+
return results;
|
786
|
+
}
|
709
787
|
}
|