@ai.ntellect/core 0.7.5 → 0.7.7
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/dist/graph/controller.d.ts +8 -10
- package/dist/graph/controller.d.ts.map +1 -1
- package/dist/graph/controller.js +25 -27
- package/dist/graph/controller.js.map +1 -1
- package/dist/graph/index.d.ts +5 -3
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/index.js +13 -9
- package/dist/graph/index.js.map +1 -1
- package/dist/graph/node.d.ts +15 -26
- package/dist/graph/node.d.ts.map +1 -1
- package/dist/graph/node.js +66 -101
- package/dist/graph/node.js.map +1 -1
- package/dist/graph/observer.d.ts.map +1 -1
- package/dist/graph/observer.js +0 -1
- package/dist/graph/observer.js.map +1 -1
- package/dist/types/index.d.ts +5 -5
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/generate-action-schema.js +2 -2
- package/graph/controller.ts +36 -40
- package/graph/index.ts +17 -13
- package/graph/node.ts +79 -134
- package/graph/observer.ts +0 -1
- package/package.json +1 -1
- package/test/graph/controller.test.ts +187 -0
- package/test/graph/index.test.ts +138 -97
- package/test/graph/node.test.ts +319 -6
- package/types/index.ts +5 -8
- package/utils/generate-action-schema.ts +2 -2
package/graph/node.ts
CHANGED
|
@@ -2,13 +2,22 @@ import { BehaviorSubject, Subject } from "rxjs";
|
|
|
2
2
|
import { ZodSchema } from "zod";
|
|
3
3
|
import { GraphContext, GraphEvent, Node } from "../types";
|
|
4
4
|
import { GraphEventManager } from "./event-manager";
|
|
5
|
-
import { GraphLogger } from "./logger";
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Represents a node in the graph that can execute operations and manage state
|
|
9
8
|
* @template T - The Zod schema type for validation
|
|
10
9
|
*/
|
|
10
|
+
export interface NodeParams<T = any> {
|
|
11
|
+
[key: string]: T;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface GraphLogger {
|
|
15
|
+
addLog: (message: string, data?: any) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
export class GraphNode<T extends ZodSchema> {
|
|
19
|
+
private lastStateEvent: GraphEvent<T> | null = null;
|
|
20
|
+
|
|
12
21
|
/**
|
|
13
22
|
* Creates a new GraphNode instance
|
|
14
23
|
* @param nodes - Map of all nodes in the graph
|
|
@@ -32,15 +41,22 @@ export class GraphNode<T extends ZodSchema> {
|
|
|
32
41
|
* @private
|
|
33
42
|
*/
|
|
34
43
|
private emitEvent(type: string, payload: any) {
|
|
35
|
-
|
|
44
|
+
if (type === "nodeStateChanged") {
|
|
45
|
+
if (
|
|
46
|
+
this.lastStateEvent?.type === type &&
|
|
47
|
+
this.lastStateEvent.payload.property === payload.property &&
|
|
48
|
+
this.lastStateEvent.payload.newValue === payload.newValue &&
|
|
49
|
+
this.lastStateEvent.payload.nodeName === payload.nodeName
|
|
50
|
+
) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
36
55
|
const event = {
|
|
37
56
|
type,
|
|
38
57
|
payload: {
|
|
39
58
|
...payload,
|
|
40
|
-
name:
|
|
41
|
-
type === "nodeStateChanged"
|
|
42
|
-
? payload.name || payload.nodeName
|
|
43
|
-
: payload.name,
|
|
59
|
+
name: type === "nodeStateChanged" ? payload.nodeName : payload.name,
|
|
44
60
|
context: { ...payload.context },
|
|
45
61
|
},
|
|
46
62
|
timestamp: Date.now(),
|
|
@@ -49,8 +65,8 @@ export class GraphNode<T extends ZodSchema> {
|
|
|
49
65
|
this.eventSubject.next(event);
|
|
50
66
|
this.eventManager.emitEvent(type, event);
|
|
51
67
|
|
|
52
|
-
// Update state subject only for state changes
|
|
53
68
|
if (type === "nodeStateChanged") {
|
|
69
|
+
this.lastStateEvent = event;
|
|
54
70
|
this.stateSubject.next({ ...payload.context });
|
|
55
71
|
}
|
|
56
72
|
}
|
|
@@ -59,125 +75,93 @@ export class GraphNode<T extends ZodSchema> {
|
|
|
59
75
|
* Executes a node with the given name and context
|
|
60
76
|
* @param nodeName - The name of the node to execute
|
|
61
77
|
* @param context - The current graph context
|
|
62
|
-
* @param
|
|
78
|
+
* @param params - Input data for the node
|
|
63
79
|
* @param triggeredByEvent - Whether the execution was triggered by an event
|
|
64
80
|
* @throws Error if the node is not found or execution fails
|
|
65
81
|
*/
|
|
66
|
-
async executeNode(
|
|
82
|
+
public async executeNode(
|
|
67
83
|
nodeName: string,
|
|
68
84
|
context: GraphContext<T>,
|
|
69
|
-
|
|
85
|
+
params: any,
|
|
70
86
|
triggeredByEvent: boolean = false
|
|
71
87
|
): Promise<void> {
|
|
72
88
|
const node = this.nodes.get(nodeName);
|
|
73
89
|
if (!node) throw new Error(`Node "${nodeName}" not found.`);
|
|
74
90
|
|
|
75
|
-
|
|
76
|
-
|
|
91
|
+
// Créer une copie du contexte pour ce nœud
|
|
92
|
+
const nodeContext = { ...context };
|
|
93
|
+
this.emitEvent("nodeStarted", { name: nodeName, context: nodeContext });
|
|
77
94
|
|
|
78
95
|
try {
|
|
79
|
-
|
|
80
|
-
if (node.condition && !node.condition(context)) {
|
|
81
|
-
this.logger.addLog(
|
|
82
|
-
`⏭️ Skipping node "${nodeName}" - condition not met`
|
|
83
|
-
);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const contextProxy = new Proxy(context, {
|
|
96
|
+
const contextProxy = new Proxy(nodeContext, {
|
|
88
97
|
set: (target, prop, value) => {
|
|
89
98
|
const oldValue = target[prop];
|
|
90
99
|
if (oldValue === value) return true;
|
|
91
100
|
|
|
92
101
|
target[prop] = value;
|
|
102
|
+
// Mettre à jour le contexte global
|
|
103
|
+
context[prop as keyof typeof context] = value;
|
|
104
|
+
|
|
93
105
|
this.emitEvent("nodeStateChanged", {
|
|
94
106
|
nodeName,
|
|
95
107
|
property: prop.toString(),
|
|
96
108
|
oldValue,
|
|
97
109
|
newValue: value,
|
|
98
|
-
context: target,
|
|
110
|
+
context: { ...target },
|
|
99
111
|
});
|
|
100
|
-
|
|
101
112
|
return true;
|
|
102
113
|
},
|
|
103
|
-
get: (target, prop) => target[prop],
|
|
104
114
|
});
|
|
105
115
|
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
if (node.condition && !node.condition(contextProxy, params)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await this.executeWithRetry(node, contextProxy, nodeName, params);
|
|
121
|
+
this.emitEvent("nodeCompleted", { name: nodeName, context: nodeContext });
|
|
108
122
|
|
|
109
|
-
|
|
110
|
-
if (!triggeredByEvent) {
|
|
123
|
+
if (!triggeredByEvent && node.next) {
|
|
111
124
|
const nextNodes =
|
|
112
|
-
typeof node.next === "function"
|
|
113
|
-
? node.next(contextProxy)
|
|
114
|
-
: node.next || [];
|
|
115
|
-
|
|
125
|
+
typeof node.next === "function" ? node.next(contextProxy) : node.next;
|
|
116
126
|
for (const nextNodeName of nextNodes) {
|
|
117
127
|
await this.executeNode(nextNodeName, context, undefined, false);
|
|
118
128
|
}
|
|
119
129
|
}
|
|
120
|
-
|
|
121
|
-
this.logger.addLog(`✅ Node "${nodeName}" executed successfully`);
|
|
122
|
-
this.emitEvent("nodeCompleted", { name: nodeName, context });
|
|
123
130
|
} catch (error) {
|
|
124
|
-
this.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
);
|
|
129
|
-
this.emitEvent("nodeError", { name: nodeName, error, context });
|
|
131
|
+
this.emitEvent("nodeError", {
|
|
132
|
+
name: nodeName,
|
|
133
|
+
error,
|
|
134
|
+
context: nodeContext,
|
|
135
|
+
});
|
|
130
136
|
throw error;
|
|
131
137
|
}
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
/**
|
|
135
|
-
* Validates the
|
|
136
|
-
* @param node - The node whose
|
|
137
|
-
* @param
|
|
141
|
+
* Validates the params for a node using its schema
|
|
142
|
+
* @param node - The node whose params need validation
|
|
143
|
+
* @param params - The input data to validate
|
|
138
144
|
* @param nodeName - The name of the node (for error messages)
|
|
139
145
|
* @throws Error if validation fails
|
|
140
146
|
* @private
|
|
141
147
|
*/
|
|
142
|
-
private async
|
|
148
|
+
private async validateParams(
|
|
143
149
|
node: Node<T, any>,
|
|
144
|
-
|
|
150
|
+
params: any,
|
|
145
151
|
nodeName: string
|
|
146
152
|
): Promise<void> {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
153
|
+
// Si pas de schéma de validation ou si le schéma est optionnel, accepter n'importe quels params
|
|
154
|
+
if (!node.params || node.params.isOptional?.()) return;
|
|
150
155
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
throw new Error(
|
|
155
|
-
error.errors?.[0]?.message || error.message || "Input validation failed"
|
|
156
|
-
);
|
|
156
|
+
// Vérifier les params uniquement si un schéma est défini et non optionnel
|
|
157
|
+
if (!params) {
|
|
158
|
+
throw new Error(`Params required for node "${nodeName}"`);
|
|
157
159
|
}
|
|
158
|
-
}
|
|
159
160
|
|
|
160
|
-
/**
|
|
161
|
-
* Validates the outputs of a node against its schema
|
|
162
|
-
* @param node - The node whose outputs need validation
|
|
163
|
-
* @param context - The current graph context
|
|
164
|
-
* @param nodeName - The name of the node (for error messages)
|
|
165
|
-
* @throws Error if validation fails
|
|
166
|
-
* @private
|
|
167
|
-
*/
|
|
168
|
-
private async validateOutputs(
|
|
169
|
-
node: Node<T, any>,
|
|
170
|
-
context: GraphContext<T>,
|
|
171
|
-
nodeName: string
|
|
172
|
-
): Promise<void> {
|
|
173
161
|
try {
|
|
174
|
-
node.
|
|
162
|
+
return node.params.parse(params);
|
|
175
163
|
} catch (error: any) {
|
|
176
|
-
throw
|
|
177
|
-
error.errors?.[0]?.message ||
|
|
178
|
-
error.message ||
|
|
179
|
-
"Output validation failed"
|
|
180
|
-
);
|
|
164
|
+
throw error;
|
|
181
165
|
}
|
|
182
166
|
}
|
|
183
167
|
|
|
@@ -206,42 +190,41 @@ export class GraphNode<T extends ZodSchema> {
|
|
|
206
190
|
* Executes a node with retry logic
|
|
207
191
|
* @param node - The node to execute
|
|
208
192
|
* @param contextProxy - The proxied graph context
|
|
209
|
-
* @param
|
|
193
|
+
* @param params - Input data for the node
|
|
210
194
|
* @param nodeName - The name of the node
|
|
195
|
+
* @param params - Parameters for the node
|
|
211
196
|
* @throws Error if all retry attempts fail
|
|
212
197
|
* @private
|
|
213
198
|
*/
|
|
214
199
|
private async executeWithRetry(
|
|
215
200
|
node: Node<T, any>,
|
|
216
201
|
contextProxy: GraphContext<T>,
|
|
217
|
-
|
|
218
|
-
|
|
202
|
+
nodeName: string,
|
|
203
|
+
params?: NodeParams
|
|
219
204
|
): Promise<void> {
|
|
220
205
|
let attempts = 0;
|
|
221
|
-
let lastError: Error
|
|
206
|
+
let lastError: Error = new Error("Unknown error");
|
|
222
207
|
|
|
223
|
-
while (attempts < node.retry
|
|
208
|
+
while (attempts < (node.retry?.maxAttempts || 1)) {
|
|
224
209
|
try {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
210
|
+
// Valider les params uniquement si un schéma est défini
|
|
211
|
+
if (node.params) {
|
|
212
|
+
await this.validateParams(node, params, nodeName);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
await node.execute(contextProxy, params);
|
|
229
216
|
return;
|
|
230
217
|
} catch (error: any) {
|
|
231
|
-
lastError =
|
|
218
|
+
lastError =
|
|
219
|
+
error instanceof Error
|
|
220
|
+
? error
|
|
221
|
+
: new Error(error?.message || "Unknown error");
|
|
232
222
|
attempts++;
|
|
233
|
-
this.logger.addLog(
|
|
234
|
-
`❌ Attempt ${attempts} failed: ${lastError.message}`
|
|
235
|
-
);
|
|
236
223
|
|
|
237
|
-
if (attempts === node.retry
|
|
238
|
-
if (node.retry
|
|
239
|
-
await
|
|
240
|
-
|
|
241
|
-
lastError,
|
|
242
|
-
contextProxy,
|
|
243
|
-
nodeName
|
|
244
|
-
);
|
|
224
|
+
if (attempts === (node.retry?.maxAttempts || 1)) {
|
|
225
|
+
if (node.retry?.onRetryFailed) {
|
|
226
|
+
await node.retry.onRetryFailed(lastError, contextProxy);
|
|
227
|
+
if (node.retry.continueOnFailed) return;
|
|
245
228
|
}
|
|
246
229
|
throw lastError;
|
|
247
230
|
}
|
|
@@ -253,44 +236,6 @@ export class GraphNode<T extends ZodSchema> {
|
|
|
253
236
|
}
|
|
254
237
|
}
|
|
255
238
|
|
|
256
|
-
/**
|
|
257
|
-
* Handles the failure of retry attempts
|
|
258
|
-
* @param node - The node that failed
|
|
259
|
-
* @param error - The error that caused the failure
|
|
260
|
-
* @param context - The current graph context
|
|
261
|
-
* @param nodeName - The name of the node
|
|
262
|
-
* @private
|
|
263
|
-
*/
|
|
264
|
-
private async handleRetryFailure(
|
|
265
|
-
node: Node<T, any>,
|
|
266
|
-
error: Error,
|
|
267
|
-
context: GraphContext<T>,
|
|
268
|
-
nodeName: string
|
|
269
|
-
): Promise<void> {
|
|
270
|
-
this.logger.addLog(
|
|
271
|
-
`🔄 Executing retry failure handler for node "${nodeName}"`
|
|
272
|
-
);
|
|
273
|
-
try {
|
|
274
|
-
if (node.retry?.onRetryFailed) {
|
|
275
|
-
await node.retry.onRetryFailed(error, context);
|
|
276
|
-
if (node.retry.continueOnFailed) {
|
|
277
|
-
this.logger.addLog(
|
|
278
|
-
`✅ Retry failure handler succeeded - continuing execution`
|
|
279
|
-
);
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
this.logger.addLog(
|
|
283
|
-
`⚠️ Retry failure handler executed but node will still fail`
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
} catch (handlerError: any) {
|
|
287
|
-
this.logger.addLog(
|
|
288
|
-
`❌ Retry failure handler failed: ${handlerError.message}`
|
|
289
|
-
);
|
|
290
|
-
throw handlerError;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
239
|
/**
|
|
295
240
|
* Handles correlated events for a node
|
|
296
241
|
* @param node - The node with correlated events
|
package/graph/observer.ts
CHANGED
|
@@ -56,7 +56,6 @@ export class GraphObserver<T extends ZodSchema> {
|
|
|
56
56
|
} = {}
|
|
57
57
|
): GraphObservable<T> {
|
|
58
58
|
const baseObservable = new Observable<any>((subscriber) => {
|
|
59
|
-
// Combine les événements avec l'état actuel
|
|
60
59
|
const subscription = combineLatest([
|
|
61
60
|
this.eventSubject.pipe(
|
|
62
61
|
filter((event) => event.type === "nodeStateChanged"),
|
package/package.json
CHANGED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { GraphController } from "../../graph/controller";
|
|
4
|
+
import { GraphFlow } from "../../graph/index";
|
|
5
|
+
import { Node } from "../../types";
|
|
6
|
+
|
|
7
|
+
describe("GraphController", () => {
|
|
8
|
+
const TestSchema = z.object({
|
|
9
|
+
counter: z.number(),
|
|
10
|
+
message: z.string(),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const createTestGraph = (name: string): GraphFlow<typeof TestSchema> => {
|
|
14
|
+
const nodes: Node<typeof TestSchema>[] = [
|
|
15
|
+
{
|
|
16
|
+
name: "start",
|
|
17
|
+
execute: async (context, params) => {
|
|
18
|
+
context.counter = params?.value ?? 0;
|
|
19
|
+
context.message = params?.prefix ? `${params.prefix}-${name}` : name;
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "increment",
|
|
24
|
+
execute: async (context) => {
|
|
25
|
+
context.counter += 1;
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
return new GraphFlow(name, {
|
|
31
|
+
name,
|
|
32
|
+
nodes,
|
|
33
|
+
schema: TestSchema,
|
|
34
|
+
context: { counter: 0, message: "" },
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
describe("Sequential Execution", () => {
|
|
39
|
+
it("should execute graphs sequentially with different params and params", async () => {
|
|
40
|
+
const graph1 = createTestGraph("graph1");
|
|
41
|
+
const graph2 = createTestGraph("graph2");
|
|
42
|
+
const graph3 = createTestGraph("graph3");
|
|
43
|
+
|
|
44
|
+
const params = [{ value: 10 }, { value: 20 }, { value: 30 }];
|
|
45
|
+
|
|
46
|
+
const params2 = [
|
|
47
|
+
{ prefix: "test1" },
|
|
48
|
+
{ prefix: "test2" },
|
|
49
|
+
{ prefix: "test3" },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const results = await GraphController.executeSequential(
|
|
53
|
+
[graph1, graph2, graph3],
|
|
54
|
+
["start", "start", "start"],
|
|
55
|
+
params.map((value, i) => ({ ...value, prefix: params2[i].prefix }))
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(results).to.have.length(3);
|
|
59
|
+
expect(results[0].counter).to.equal(10);
|
|
60
|
+
expect(results[1].counter).to.equal(20);
|
|
61
|
+
expect(results[2].counter).to.equal(30);
|
|
62
|
+
expect(results[0].message).to.equal("test1-graph1");
|
|
63
|
+
expect(results[1].message).to.equal("test2-graph2");
|
|
64
|
+
expect(results[2].message).to.equal("test3-graph3");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should handle missing params and params gracefully", async () => {
|
|
68
|
+
const graph1 = createTestGraph("graph1");
|
|
69
|
+
const graph2 = createTestGraph("graph2");
|
|
70
|
+
|
|
71
|
+
const results = await GraphController.executeSequential(
|
|
72
|
+
[graph1, graph2],
|
|
73
|
+
["start", "start"]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(results).to.have.length(2);
|
|
77
|
+
expect(results[0].counter).to.equal(0);
|
|
78
|
+
expect(results[1].counter).to.equal(0);
|
|
79
|
+
expect(results[0].message).to.equal("graph1");
|
|
80
|
+
expect(results[1].message).to.equal("graph2");
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("Parallel Execution", () => {
|
|
85
|
+
it("should execute graphs in parallel with concurrency limit", async () => {
|
|
86
|
+
const graphs = Array.from({ length: 5 }, (_, i) =>
|
|
87
|
+
createTestGraph(`graph${i + 1}`)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const params = Array.from({ length: 5 }, (_, i) => ({
|
|
91
|
+
value: (i + 1) * 10,
|
|
92
|
+
prefix: `test${i + 1}`,
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
// Ajouter un délai dans l'exécution
|
|
96
|
+
const originalExecute = graphs[0].execute;
|
|
97
|
+
graphs[0].execute = async (...args) => {
|
|
98
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
99
|
+
return originalExecute.apply(graphs[0], args);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const startTime = Date.now();
|
|
103
|
+
const results = await GraphController.executeParallel(
|
|
104
|
+
graphs,
|
|
105
|
+
Array(5).fill("start"),
|
|
106
|
+
2,
|
|
107
|
+
params
|
|
108
|
+
);
|
|
109
|
+
const executionTime = Date.now() - startTime;
|
|
110
|
+
|
|
111
|
+
expect(executionTime).to.be.greaterThan(0);
|
|
112
|
+
expect(results).to.have.length(5);
|
|
113
|
+
results.forEach((result, i) => {
|
|
114
|
+
expect(result.counter).to.equal((i + 1) * 10);
|
|
115
|
+
expect(result.message).to.equal(`test${i + 1}-graph${i + 1}`);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should handle errors in parallel execution", async () => {
|
|
120
|
+
const errorGraph = new GraphFlow("errorGraph", {
|
|
121
|
+
name: "errorGraph",
|
|
122
|
+
nodes: [
|
|
123
|
+
{
|
|
124
|
+
name: "start",
|
|
125
|
+
execute: async () => {
|
|
126
|
+
throw new Error("Test error");
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
schema: TestSchema,
|
|
131
|
+
context: { counter: 0, message: "" },
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const successGraph = createTestGraph("successGraph");
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
await GraphController.executeParallel(
|
|
138
|
+
[errorGraph, successGraph],
|
|
139
|
+
["start", "start"],
|
|
140
|
+
2
|
|
141
|
+
);
|
|
142
|
+
expect.fail("Should have thrown an error");
|
|
143
|
+
} catch (error: any) {
|
|
144
|
+
expect(error.message).to.equal("Test error");
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe("Complex Workflows", () => {
|
|
150
|
+
it("should handle mixed sequential and parallel execution", async () => {
|
|
151
|
+
const graphs = Array.from({ length: 4 }, (_, i) =>
|
|
152
|
+
createTestGraph(`graph${i + 1}`)
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// Exécuter les deux premiers graphes en parallèle
|
|
156
|
+
const parallelResults = await GraphController.executeParallel(
|
|
157
|
+
graphs.slice(0, 2),
|
|
158
|
+
["start", "start"],
|
|
159
|
+
2,
|
|
160
|
+
[
|
|
161
|
+
{ value: 10, prefix: "parallel1" },
|
|
162
|
+
{ value: 20, prefix: "parallel2" },
|
|
163
|
+
]
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Puis exécuter les deux suivants séquentiellement
|
|
167
|
+
const sequentialResults = await GraphController.executeSequential(
|
|
168
|
+
graphs.slice(2),
|
|
169
|
+
["start", "start"],
|
|
170
|
+
[
|
|
171
|
+
{ value: 30, prefix: "seq1" },
|
|
172
|
+
{ value: 40, prefix: "seq2" },
|
|
173
|
+
]
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const allResults = [...parallelResults, ...sequentialResults];
|
|
177
|
+
expect(allResults).to.have.length(4);
|
|
178
|
+
expect(allResults.map((r) => r.counter)).to.deep.equal([10, 20, 30, 40]);
|
|
179
|
+
expect(allResults.map((r) => r.message)).to.deep.equal([
|
|
180
|
+
"parallel1-graph1",
|
|
181
|
+
"parallel2-graph2",
|
|
182
|
+
"seq1-graph3",
|
|
183
|
+
"seq2-graph4",
|
|
184
|
+
]);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|