@ai.ntellect/core 0.7.6 → 0.7.8
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 +2 -3
- package/dist/graph/controller.d.ts.map +1 -1
- package/dist/graph/controller.js +5 -6
- package/dist/graph/controller.js.map +1 -1
- package/dist/graph/event-manager.d.ts +2 -1
- package/dist/graph/event-manager.d.ts.map +1 -1
- package/dist/graph/event-manager.js +55 -3
- package/dist/graph/event-manager.js.map +1 -1
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/index.js +2 -2
- package/dist/graph/index.js.map +1 -1
- package/dist/graph/node.d.ts +10 -39
- package/dist/graph/node.d.ts.map +1 -1
- package/dist/graph/node.js +41 -64
- package/dist/graph/node.js.map +1 -1
- package/dist/graph/observer.d.ts +13 -11
- package/dist/graph/observer.d.ts.map +1 -1
- package/dist/graph/observer.js.map +1 -1
- package/dist/interfaces/index.d.ts +2 -2
- package/dist/interfaces/index.d.ts.map +1 -1
- package/dist/interfaces/index.js.map +1 -1
- package/dist/types/index.d.ts +13 -9
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/generate-action-schema.js +2 -2
- package/graph/controller.ts +5 -10
- package/graph/event-manager.ts +74 -6
- package/graph/index.ts +4 -6
- package/graph/node.ts +53 -95
- package/graph/observer.ts +10 -10
- package/interfaces/index.ts +4 -1
- package/package.json +1 -1
- package/test/graph/event-manager.test.ts +46 -0
- package/test/graph/index.test.ts +119 -173
- package/test/graph/node.test.ts +323 -0
- package/types/index.ts +15 -9
- package/utils/generate-action-schema.ts +2 -2
package/graph/node.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
import { IEventEmitter } from "interfaces";
|
1
2
|
import { BehaviorSubject, Subject } from "rxjs";
|
2
3
|
import { ZodSchema } from "zod";
|
3
|
-
import { GraphContext, GraphEvent } from "../types";
|
4
|
+
import { GraphContext, GraphEvent, Node } from "../types";
|
4
5
|
import { GraphEventManager } from "./event-manager";
|
5
6
|
|
6
7
|
/**
|
@@ -11,39 +12,13 @@ export interface NodeParams<T = any> {
|
|
11
12
|
[key: string]: T;
|
12
13
|
}
|
13
14
|
|
14
|
-
export interface Node<T extends ZodSchema, I = any> {
|
15
|
-
condition?: (context: GraphContext<T>, params?: NodeParams) => boolean;
|
16
|
-
execute: (
|
17
|
-
context: GraphContext<T>,
|
18
|
-
inputs: I,
|
19
|
-
params?: NodeParams
|
20
|
-
) => Promise<void>;
|
21
|
-
next?: string[] | ((context: GraphContext<T>) => string[]);
|
22
|
-
inputs?: ZodSchema;
|
23
|
-
outputs?: ZodSchema;
|
24
|
-
retry?: {
|
25
|
-
maxAttempts: number;
|
26
|
-
delay?: number;
|
27
|
-
onRetryFailed?: (error: Error, context: GraphContext<T>) => Promise<void>;
|
28
|
-
continueOnFailed?: boolean;
|
29
|
-
};
|
30
|
-
correlateEvents?: {
|
31
|
-
events: string[];
|
32
|
-
timeout?: number;
|
33
|
-
correlation: (events: GraphEvent<T>[]) => boolean;
|
34
|
-
};
|
35
|
-
waitForEvents?: {
|
36
|
-
events: string[];
|
37
|
-
timeout: number;
|
38
|
-
};
|
39
|
-
}
|
40
|
-
|
41
15
|
export interface GraphLogger {
|
42
16
|
addLog: (message: string, data?: any) => void;
|
43
17
|
}
|
44
18
|
|
45
19
|
export class GraphNode<T extends ZodSchema> {
|
46
20
|
private lastStateEvent: GraphEvent<T> | null = null;
|
21
|
+
private eventEmitter: IEventEmitter;
|
47
22
|
|
48
23
|
/**
|
49
24
|
* Creates a new GraphNode instance
|
@@ -59,7 +34,9 @@ export class GraphNode<T extends ZodSchema> {
|
|
59
34
|
private eventManager: GraphEventManager<T>,
|
60
35
|
private eventSubject: Subject<GraphEvent<T>>,
|
61
36
|
private stateSubject: BehaviorSubject<GraphContext<T>>
|
62
|
-
) {
|
37
|
+
) {
|
38
|
+
this.eventEmitter = eventManager["eventEmitter"];
|
39
|
+
}
|
63
40
|
|
64
41
|
/**
|
65
42
|
* Emits an event with the specified type and payload
|
@@ -102,32 +79,49 @@ export class GraphNode<T extends ZodSchema> {
|
|
102
79
|
* Executes a node with the given name and context
|
103
80
|
* @param nodeName - The name of the node to execute
|
104
81
|
* @param context - The current graph context
|
105
|
-
* @param
|
82
|
+
* @param params - Input data for the node
|
106
83
|
* @param triggeredByEvent - Whether the execution was triggered by an event
|
107
84
|
* @throws Error if the node is not found or execution fails
|
108
85
|
*/
|
109
86
|
public async executeNode(
|
110
87
|
nodeName: string,
|
111
88
|
context: GraphContext<T>,
|
112
|
-
|
89
|
+
params: any,
|
113
90
|
triggeredByEvent: boolean = false
|
114
91
|
): Promise<void> {
|
115
92
|
const node = this.nodes.get(nodeName);
|
116
93
|
if (!node) throw new Error(`Node "${nodeName}" not found.`);
|
117
94
|
|
118
|
-
// Créer une copie du contexte pour ce nœud
|
119
95
|
const nodeContext = { ...context };
|
120
96
|
this.emitEvent("nodeStarted", { name: nodeName, context: nodeContext });
|
121
97
|
|
122
98
|
try {
|
99
|
+
if (node.correlateEvents) {
|
100
|
+
await this.eventManager.waitForCorrelatedEvents(
|
101
|
+
node.correlateEvents.events,
|
102
|
+
node.correlateEvents.timeout || 30000,
|
103
|
+
(events) => {
|
104
|
+
return node.correlateEvents!.correlation(events);
|
105
|
+
}
|
106
|
+
);
|
107
|
+
}
|
108
|
+
|
109
|
+
// Ensuite, attendre les événements si waitForEvents est défini
|
110
|
+
if (node.waitForEvents) {
|
111
|
+
await this.eventManager.waitForEvents(
|
112
|
+
node.waitForEvents.events,
|
113
|
+
node.waitForEvents.timeout
|
114
|
+
);
|
115
|
+
}
|
116
|
+
|
123
117
|
const contextProxy = new Proxy(nodeContext, {
|
124
|
-
set: (target, prop, value) => {
|
125
|
-
const oldValue = target[prop];
|
118
|
+
set: (target: any, prop: string | symbol, value: any) => {
|
119
|
+
const oldValue = target[prop.toString()];
|
126
120
|
if (oldValue === value) return true;
|
127
121
|
|
128
|
-
target[prop] = value;
|
122
|
+
target[prop.toString()] = value;
|
129
123
|
// Mettre à jour le contexte global
|
130
|
-
context[prop as keyof typeof context] = value;
|
124
|
+
context[prop.toString() as keyof typeof context] = value;
|
131
125
|
|
132
126
|
this.emitEvent("nodeStateChanged", {
|
133
127
|
nodeName,
|
@@ -140,11 +134,11 @@ export class GraphNode<T extends ZodSchema> {
|
|
140
134
|
},
|
141
135
|
});
|
142
136
|
|
143
|
-
if (node.condition && !node.condition(contextProxy,
|
137
|
+
if (node.condition && !node.condition(contextProxy, params)) {
|
144
138
|
return;
|
145
139
|
}
|
146
140
|
|
147
|
-
await this.executeWithRetry(node, contextProxy,
|
141
|
+
await this.executeWithRetry(node, contextProxy, nodeName, params);
|
148
142
|
this.emitEvent("nodeCompleted", { name: nodeName, context: nodeContext });
|
149
143
|
|
150
144
|
if (!triggeredByEvent && node.next) {
|
@@ -165,52 +159,30 @@ export class GraphNode<T extends ZodSchema> {
|
|
165
159
|
}
|
166
160
|
|
167
161
|
/**
|
168
|
-
* Validates the
|
169
|
-
* @param node - The node whose
|
170
|
-
* @param
|
162
|
+
* Validates the params for a node using its schema
|
163
|
+
* @param node - The node whose params need validation
|
164
|
+
* @param params - The input data to validate
|
171
165
|
* @param nodeName - The name of the node (for error messages)
|
172
166
|
* @throws Error if validation fails
|
173
167
|
* @private
|
174
168
|
*/
|
175
|
-
private async
|
169
|
+
private async validateParams(
|
176
170
|
node: Node<T, any>,
|
177
|
-
|
171
|
+
params: any,
|
178
172
|
nodeName: string
|
179
173
|
): Promise<void> {
|
180
|
-
|
181
|
-
|
182
|
-
}
|
174
|
+
// Si pas de schéma de validation ou si le schéma est optionnel, accepter n'importe quels params
|
175
|
+
if (!node.params || node.params.isOptional?.()) return;
|
183
176
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
throw new Error(
|
188
|
-
error.errors?.[0]?.message || error.message || "Input validation failed"
|
189
|
-
);
|
177
|
+
// Vérifier les params uniquement si un schéma est défini et non optionnel
|
178
|
+
if (!params) {
|
179
|
+
throw new Error(`Params required for node "${nodeName}"`);
|
190
180
|
}
|
191
|
-
}
|
192
181
|
|
193
|
-
/**
|
194
|
-
* Validates the outputs of a node against its schema
|
195
|
-
* @param node - The node whose outputs need validation
|
196
|
-
* @param context - The current graph context
|
197
|
-
* @param nodeName - The name of the node (for error messages)
|
198
|
-
* @throws Error if validation fails
|
199
|
-
* @private
|
200
|
-
*/
|
201
|
-
private async validateOutputs(
|
202
|
-
node: Node<T, any>,
|
203
|
-
context: GraphContext<T>,
|
204
|
-
nodeName: string
|
205
|
-
): Promise<void> {
|
206
182
|
try {
|
207
|
-
node.
|
183
|
+
return node.params.parse(params);
|
208
184
|
} catch (error: any) {
|
209
|
-
throw
|
210
|
-
error.errors?.[0]?.message ||
|
211
|
-
error.message ||
|
212
|
-
"Output validation failed"
|
213
|
-
);
|
185
|
+
throw error;
|
214
186
|
}
|
215
187
|
}
|
216
188
|
|
@@ -239,44 +211,30 @@ export class GraphNode<T extends ZodSchema> {
|
|
239
211
|
* Executes a node with retry logic
|
240
212
|
* @param node - The node to execute
|
241
213
|
* @param contextProxy - The proxied graph context
|
242
|
-
* @param
|
214
|
+
* @param params - Input data for the node
|
243
215
|
* @param nodeName - The name of the node
|
216
|
+
* @param params - Parameters for the node
|
244
217
|
* @throws Error if all retry attempts fail
|
245
218
|
* @private
|
246
219
|
*/
|
247
220
|
private async executeWithRetry(
|
248
221
|
node: Node<T, any>,
|
249
222
|
contextProxy: GraphContext<T>,
|
250
|
-
|
251
|
-
|
223
|
+
nodeName: string,
|
224
|
+
params?: NodeParams
|
252
225
|
): Promise<void> {
|
253
226
|
let attempts = 0;
|
254
227
|
let lastError: Error = new Error("Unknown error");
|
255
228
|
|
256
229
|
while (attempts < (node.retry?.maxAttempts || 1)) {
|
257
230
|
try {
|
258
|
-
|
259
|
-
|
260
|
-
try {
|
261
|
-
node.inputs.parse(inputs);
|
262
|
-
} catch (error: any) {
|
263
|
-
const message = error.errors?.[0]?.message || error.message;
|
264
|
-
throw new Error(`Input validation failed: ${message}`);
|
265
|
-
}
|
231
|
+
if (node.params) {
|
232
|
+
await this.validateParams(node, params, nodeName);
|
266
233
|
}
|
267
234
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
// Validation des outputs
|
272
|
-
if (node.outputs) {
|
273
|
-
try {
|
274
|
-
node.outputs.parse(contextProxy);
|
275
|
-
} catch (error: any) {
|
276
|
-
const message = error.errors?.[0]?.message || error.message;
|
277
|
-
throw new Error(`Output validation failed: ${message}`);
|
278
|
-
}
|
279
|
-
}
|
235
|
+
await node.execute(contextProxy, params, {
|
236
|
+
eventEmitter: this.eventEmitter,
|
237
|
+
});
|
280
238
|
return;
|
281
239
|
} catch (error: any) {
|
282
240
|
lastError =
|
package/graph/observer.ts
CHANGED
@@ -21,6 +21,15 @@ import { GraphObservable } from "../interfaces";
|
|
21
21
|
import { GraphContext, GraphEvent } from "../types";
|
22
22
|
import { GraphFlow } from "./index";
|
23
23
|
|
24
|
+
interface ObserverOptions {
|
25
|
+
debounce?: number;
|
26
|
+
delay?: number;
|
27
|
+
stream?: boolean;
|
28
|
+
properties?: (string | number)[]; // Accepte uniquement string ou number comme clés
|
29
|
+
onStreamLetter?: (data: { letter: string; property: string }) => void;
|
30
|
+
onStreamComplete?: () => void;
|
31
|
+
}
|
32
|
+
|
24
33
|
/**
|
25
34
|
* GraphObserver class provides reactive observation capabilities for a GraphFlow instance
|
26
35
|
* It allows monitoring state changes, node updates, and specific events in the graph
|
@@ -45,16 +54,7 @@ export class GraphObserver<T extends ZodSchema> {
|
|
45
54
|
* @param options.onStreamComplete Callback when streaming is complete
|
46
55
|
* @returns An Observable that emits the complete graph context whenever it changes
|
47
56
|
*/
|
48
|
-
state(
|
49
|
-
options: {
|
50
|
-
debounce?: number;
|
51
|
-
delay?: number;
|
52
|
-
stream?: boolean;
|
53
|
-
properties?: (keyof GraphContext<T>)[];
|
54
|
-
onStreamLetter?: (data: { letter: string; property: string }) => void;
|
55
|
-
onStreamComplete?: () => void;
|
56
|
-
} = {}
|
57
|
-
): GraphObservable<T> {
|
57
|
+
state(options: ObserverOptions = {}): GraphObservable<T> {
|
58
58
|
const baseObservable = new Observable<any>((subscriber) => {
|
59
59
|
const subscription = combineLatest([
|
60
60
|
this.eventSubject.pipe(
|
package/interfaces/index.ts
CHANGED
@@ -6,6 +6,7 @@ import {
|
|
6
6
|
GraphContext,
|
7
7
|
GraphEvent,
|
8
8
|
ScheduledRequest,
|
9
|
+
SchemaType,
|
9
10
|
} from "../types";
|
10
11
|
|
11
12
|
/* ======================== EMBEDDING SERVICE ======================== */
|
@@ -525,7 +526,9 @@ export interface GraphObservable<T extends ZodSchema> extends Observable<any> {
|
|
525
526
|
* Observes specific properties of the graph context
|
526
527
|
* @param prop - Property or array of properties to observe
|
527
528
|
*/
|
528
|
-
property(
|
529
|
+
property(
|
530
|
+
prop: keyof SchemaType<T> | Array<keyof SchemaType<T>>
|
531
|
+
): Observable<any>;
|
529
532
|
|
530
533
|
/**
|
531
534
|
* Observes specific events in the graph
|
package/package.json
CHANGED
@@ -69,4 +69,50 @@ describe("GraphEventManager", () => {
|
|
69
69
|
eventManager.setupEventListeners();
|
70
70
|
expect(eventEmitter.listenerCount("customEvent")).to.equal(1);
|
71
71
|
});
|
72
|
+
|
73
|
+
it("should handle timeout for correlated events", async () => {
|
74
|
+
const timeout = 100;
|
75
|
+
|
76
|
+
try {
|
77
|
+
// N'émettre qu'un seul événement pour provoquer le timeout
|
78
|
+
eventManager.emitEvent("event1", { data: "test1" });
|
79
|
+
|
80
|
+
await eventManager.waitForCorrelatedEvents(
|
81
|
+
["event1", "event2"],
|
82
|
+
timeout,
|
83
|
+
() => false // La fonction de corrélation retourne toujours false pour forcer le timeout
|
84
|
+
);
|
85
|
+
throw new Error("Should have timed out");
|
86
|
+
} catch (error: any) {
|
87
|
+
expect(error.message).to.include("Timeout waiting for correlated events");
|
88
|
+
}
|
89
|
+
});
|
90
|
+
|
91
|
+
it("should wait for multiple events before continuing", async () => {
|
92
|
+
let receivedEvents = 0;
|
93
|
+
let resolvePromise: (value: unknown) => void;
|
94
|
+
const eventPromise = new Promise((resolve) => {
|
95
|
+
resolvePromise = resolve;
|
96
|
+
});
|
97
|
+
|
98
|
+
// Configurer les écouteurs d'événements AVANT d'émettre
|
99
|
+
eventEmitter.on("event1", () => {
|
100
|
+
receivedEvents++;
|
101
|
+
if (receivedEvents === 2) resolvePromise(true);
|
102
|
+
});
|
103
|
+
eventEmitter.on("event2", () => {
|
104
|
+
receivedEvents++;
|
105
|
+
if (receivedEvents === 2) resolvePromise(true);
|
106
|
+
});
|
107
|
+
|
108
|
+
// Émettre les événements AVANT de démarrer l'attente
|
109
|
+
eventManager.emitEvent("event1", { data: "test1" });
|
110
|
+
eventManager.emitEvent("event2", { data: "test2" });
|
111
|
+
|
112
|
+
// Démarrer l'attente
|
113
|
+
const waitPromise = eventManager.waitForEvents(["event1", "event2"], 1000);
|
114
|
+
|
115
|
+
await Promise.race([waitPromise, eventPromise]);
|
116
|
+
expect(receivedEvents).to.equal(2);
|
117
|
+
});
|
72
118
|
});
|