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