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