@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/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 inputs - Input data for the node
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
- inputs: any,
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, inputs)) {
137
+ if (node.condition && !node.condition(contextProxy, params)) {
144
138
  return;
145
139
  }
146
140
 
147
- await this.executeWithRetry(node, contextProxy, inputs, nodeName);
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 inputs for a node using its schema
169
- * @param node - The node whose inputs need validation
170
- * @param inputs - The input data to validate
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 validateInputs(
169
+ private async validateParams(
176
170
  node: Node<T, any>,
177
- inputs: any,
171
+ params: any,
178
172
  nodeName: string
179
173
  ): Promise<void> {
180
- if (!inputs) {
181
- throw new Error(`Inputs required for node "${nodeName}"`);
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
- try {
185
- return node.inputs!.parse(inputs);
186
- } catch (error: any) {
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.outputs!.parse(context);
183
+ return node.params.parse(params);
208
184
  } catch (error: any) {
209
- throw new Error(
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 inputs - Input data for the node
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
- inputs: any,
251
- nodeName: string
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
- // Validation des inputs
259
- if (node.inputs) {
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
- // Exécution du node
269
- await node.execute(contextProxy, inputs);
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(
@@ -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(prop: string | string[]): Observable<any>;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai.ntellect/core",
3
- "version": "0.7.6",
3
+ "version": "0.7.8",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -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
  });