@ai.ntellect/core 0.7.7 → 1.0.0

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.
Files changed (111) hide show
  1. package/README.md +158 -81
  2. package/index.ts +462 -22
  3. package/package copy.json +21 -0
  4. package/package.json +9 -44
  5. package/tsconfig.json +108 -22
  6. package/types.ts +62 -0
  7. package/utils/executor.ts +42 -0
  8. package/.mocharc.json +0 -5
  9. package/dist/graph/controller.d.ts +0 -31
  10. package/dist/graph/controller.d.ts.map +0 -1
  11. package/dist/graph/controller.js +0 -71
  12. package/dist/graph/controller.js.map +0 -1
  13. package/dist/graph/event-manager.d.ts +0 -92
  14. package/dist/graph/event-manager.d.ts.map +0 -1
  15. package/dist/graph/event-manager.js +0 -244
  16. package/dist/graph/event-manager.js.map +0 -1
  17. package/dist/graph/index.d.ts +0 -159
  18. package/dist/graph/index.d.ts.map +0 -1
  19. package/dist/graph/index.js +0 -303
  20. package/dist/graph/index.js.map +0 -1
  21. package/dist/graph/logger.d.ts +0 -46
  22. package/dist/graph/logger.d.ts.map +0 -1
  23. package/dist/graph/logger.js +0 -69
  24. package/dist/graph/logger.js.map +0 -1
  25. package/dist/graph/node.d.ts +0 -92
  26. package/dist/graph/node.d.ts.map +0 -1
  27. package/dist/graph/node.js +0 -249
  28. package/dist/graph/node.js.map +0 -1
  29. package/dist/graph/observer.d.ts +0 -113
  30. package/dist/graph/observer.d.ts.map +0 -1
  31. package/dist/graph/observer.js +0 -198
  32. package/dist/graph/observer.js.map +0 -1
  33. package/dist/index.d.ts +0 -26
  34. package/dist/index.d.ts.map +0 -1
  35. package/dist/index.js +0 -42
  36. package/dist/index.js.map +0 -1
  37. package/dist/interfaces/index.d.ts +0 -447
  38. package/dist/interfaces/index.d.ts.map +0 -1
  39. package/dist/interfaces/index.js +0 -75
  40. package/dist/interfaces/index.js.map +0 -1
  41. package/dist/modules/agenda/adapters/node-cron/index.d.ts +0 -17
  42. package/dist/modules/agenda/adapters/node-cron/index.d.ts.map +0 -1
  43. package/dist/modules/agenda/adapters/node-cron/index.js +0 -30
  44. package/dist/modules/agenda/adapters/node-cron/index.js.map +0 -1
  45. package/dist/modules/agenda/index.d.ts +0 -63
  46. package/dist/modules/agenda/index.d.ts.map +0 -1
  47. package/dist/modules/agenda/index.js +0 -141
  48. package/dist/modules/agenda/index.js.map +0 -1
  49. package/dist/modules/embedding/adapters/ai/index.d.ts +0 -29
  50. package/dist/modules/embedding/adapters/ai/index.d.ts.map +0 -1
  51. package/dist/modules/embedding/adapters/ai/index.js +0 -58
  52. package/dist/modules/embedding/adapters/ai/index.js.map +0 -1
  53. package/dist/modules/embedding/index.d.ts +0 -36
  54. package/dist/modules/embedding/index.d.ts.map +0 -1
  55. package/dist/modules/embedding/index.js +0 -60
  56. package/dist/modules/embedding/index.js.map +0 -1
  57. package/dist/modules/memory/adapters/in-memory/index.d.ts +0 -120
  58. package/dist/modules/memory/adapters/in-memory/index.d.ts.map +0 -1
  59. package/dist/modules/memory/adapters/in-memory/index.js +0 -211
  60. package/dist/modules/memory/adapters/in-memory/index.js.map +0 -1
  61. package/dist/modules/memory/adapters/meilisearch/index.d.ts +0 -110
  62. package/dist/modules/memory/adapters/meilisearch/index.d.ts.map +0 -1
  63. package/dist/modules/memory/adapters/meilisearch/index.js +0 -321
  64. package/dist/modules/memory/adapters/meilisearch/index.js.map +0 -1
  65. package/dist/modules/memory/adapters/redis/index.d.ts +0 -82
  66. package/dist/modules/memory/adapters/redis/index.d.ts.map +0 -1
  67. package/dist/modules/memory/adapters/redis/index.js +0 -159
  68. package/dist/modules/memory/adapters/redis/index.js.map +0 -1
  69. package/dist/modules/memory/index.d.ts +0 -67
  70. package/dist/modules/memory/index.d.ts.map +0 -1
  71. package/dist/modules/memory/index.js +0 -104
  72. package/dist/modules/memory/index.js.map +0 -1
  73. package/dist/types/index.d.ts +0 -166
  74. package/dist/types/index.d.ts.map +0 -1
  75. package/dist/types/index.js +0 -3
  76. package/dist/types/index.js.map +0 -1
  77. package/dist/utils/generate-action-schema.d.ts +0 -5
  78. package/dist/utils/generate-action-schema.d.ts.map +0 -1
  79. package/dist/utils/generate-action-schema.js +0 -44
  80. package/dist/utils/generate-action-schema.js.map +0 -1
  81. package/dist/utils/header-builder.d.ts +0 -12
  82. package/dist/utils/header-builder.d.ts.map +0 -1
  83. package/dist/utils/header-builder.js +0 -35
  84. package/dist/utils/header-builder.js.map +0 -1
  85. package/graph/controller.ts +0 -74
  86. package/graph/event-manager.ts +0 -295
  87. package/graph/index.ts +0 -397
  88. package/graph/logger.ts +0 -70
  89. package/graph/node.ts +0 -305
  90. package/graph/observer.ts +0 -368
  91. package/interfaces/index.ts +0 -545
  92. package/modules/agenda/adapters/node-cron/index.ts +0 -25
  93. package/modules/agenda/index.ts +0 -146
  94. package/modules/embedding/adapters/ai/index.ts +0 -42
  95. package/modules/embedding/index.ts +0 -45
  96. package/modules/memory/adapters/in-memory/index.ts +0 -207
  97. package/modules/memory/adapters/meilisearch/index.ts +0 -361
  98. package/modules/memory/adapters/redis/index.ts +0 -164
  99. package/modules/memory/index.ts +0 -93
  100. package/test/graph/controller.test.ts +0 -187
  101. package/test/graph/event-manager.test.ts +0 -72
  102. package/test/graph/index.test.ts +0 -768
  103. package/test/graph/node.test.ts +0 -510
  104. package/test/graph/observer.test.ts +0 -398
  105. package/test/modules/agenda/node-cron.test.ts +0 -307
  106. package/test/modules/memory/adapters/in-memory.test.ts +0 -153
  107. package/test/modules/memory/adapters/meilisearch.test.ts +0 -287
  108. package/test/modules/memory/base.test.ts +0 -230
  109. package/types/index.ts +0 -184
  110. package/utils/generate-action-schema.ts +0 -46
  111. package/utils/header-builder.ts +0 -40
package/graph/node.ts DELETED
@@ -1,305 +0,0 @@
1
- import { BehaviorSubject, Subject } from "rxjs";
2
- import { ZodSchema } from "zod";
3
- import { GraphContext, GraphEvent, Node } from "../types";
4
- import { GraphEventManager } from "./event-manager";
5
-
6
- /**
7
- * Represents a node in the graph that can execute operations and manage state
8
- * @template T - The Zod schema type for validation
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
-
18
- export class GraphNode<T extends ZodSchema> {
19
- private lastStateEvent: GraphEvent<T> | null = null;
20
-
21
- /**
22
- * Creates a new GraphNode instance
23
- * @param nodes - Map of all nodes in the graph
24
- * @param logger - Logger instance for tracking node operations
25
- * @param eventManager - Manager for handling graph events
26
- * @param eventSubject - Subject for emitting events
27
- * @param stateSubject - Subject for managing graph state
28
- */
29
- constructor(
30
- private nodes: Map<string, Node<T, any>>,
31
- private logger: GraphLogger,
32
- private eventManager: GraphEventManager<T>,
33
- private eventSubject: Subject<GraphEvent<T>>,
34
- private stateSubject: BehaviorSubject<GraphContext<T>>
35
- ) {}
36
-
37
- /**
38
- * Emits an event with the specified type and payload
39
- * @param type - The type of event to emit
40
- * @param payload - The data associated with the event
41
- * @private
42
- */
43
- private emitEvent(type: string, payload: any) {
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
-
55
- const event = {
56
- type,
57
- payload: {
58
- ...payload,
59
- name: type === "nodeStateChanged" ? payload.nodeName : payload.name,
60
- context: { ...payload.context },
61
- },
62
- timestamp: Date.now(),
63
- };
64
-
65
- this.eventSubject.next(event);
66
- this.eventManager.emitEvent(type, event);
67
-
68
- if (type === "nodeStateChanged") {
69
- this.lastStateEvent = event;
70
- this.stateSubject.next({ ...payload.context });
71
- }
72
- }
73
-
74
- /**
75
- * Executes a node with the given name and context
76
- * @param nodeName - The name of the node to execute
77
- * @param context - The current graph context
78
- * @param params - Input data for the node
79
- * @param triggeredByEvent - Whether the execution was triggered by an event
80
- * @throws Error if the node is not found or execution fails
81
- */
82
- public async executeNode(
83
- nodeName: string,
84
- context: GraphContext<T>,
85
- params: any,
86
- triggeredByEvent: boolean = false
87
- ): Promise<void> {
88
- const node = this.nodes.get(nodeName);
89
- if (!node) throw new Error(`Node "${nodeName}" not found.`);
90
-
91
- // Créer une copie du contexte pour ce nœud
92
- const nodeContext = { ...context };
93
- this.emitEvent("nodeStarted", { name: nodeName, context: nodeContext });
94
-
95
- try {
96
- const contextProxy = new Proxy(nodeContext, {
97
- set: (target, prop, value) => {
98
- const oldValue = target[prop];
99
- if (oldValue === value) return true;
100
-
101
- target[prop] = value;
102
- // Mettre à jour le contexte global
103
- context[prop as keyof typeof context] = value;
104
-
105
- this.emitEvent("nodeStateChanged", {
106
- nodeName,
107
- property: prop.toString(),
108
- oldValue,
109
- newValue: value,
110
- context: { ...target },
111
- });
112
- return true;
113
- },
114
- });
115
-
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 });
122
-
123
- if (!triggeredByEvent && node.next) {
124
- const nextNodes =
125
- typeof node.next === "function" ? node.next(contextProxy) : node.next;
126
- for (const nextNodeName of nextNodes) {
127
- await this.executeNode(nextNodeName, context, undefined, false);
128
- }
129
- }
130
- } catch (error) {
131
- this.emitEvent("nodeError", {
132
- name: nodeName,
133
- error,
134
- context: nodeContext,
135
- });
136
- throw error;
137
- }
138
- }
139
-
140
- /**
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
144
- * @param nodeName - The name of the node (for error messages)
145
- * @throws Error if validation fails
146
- * @private
147
- */
148
- private async validateParams(
149
- node: Node<T, any>,
150
- params: any,
151
- nodeName: string
152
- ): Promise<void> {
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;
155
-
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}"`);
159
- }
160
-
161
- try {
162
- return node.params.parse(params);
163
- } catch (error: any) {
164
- throw error;
165
- }
166
- }
167
-
168
- /**
169
- * Handles event-related operations for a node
170
- * @param node - The node whose events need handling
171
- * @param nodeName - The name of the node
172
- * @param context - The current graph context
173
- * @private
174
- */
175
- private async handleEvents(
176
- node: Node<T, any>,
177
- nodeName: string,
178
- context: GraphContext<T>
179
- ): Promise<void> {
180
- if (node.correlateEvents) {
181
- await this.handleCorrelatedEvents(node, nodeName);
182
- }
183
-
184
- if (node.waitForEvents) {
185
- await this.handleWaitForEvents(node, nodeName);
186
- }
187
- }
188
-
189
- /**
190
- * Executes a node with retry logic
191
- * @param node - The node to execute
192
- * @param contextProxy - The proxied graph context
193
- * @param params - Input data for the node
194
- * @param nodeName - The name of the node
195
- * @param params - Parameters for the node
196
- * @throws Error if all retry attempts fail
197
- * @private
198
- */
199
- private async executeWithRetry(
200
- node: Node<T, any>,
201
- contextProxy: GraphContext<T>,
202
- nodeName: string,
203
- params?: NodeParams
204
- ): Promise<void> {
205
- let attempts = 0;
206
- let lastError: Error = new Error("Unknown error");
207
-
208
- while (attempts < (node.retry?.maxAttempts || 1)) {
209
- try {
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);
216
- return;
217
- } catch (error: any) {
218
- lastError =
219
- error instanceof Error
220
- ? error
221
- : new Error(error?.message || "Unknown error");
222
- attempts++;
223
-
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;
228
- }
229
- throw lastError;
230
- }
231
-
232
- await new Promise((resolve) =>
233
- setTimeout(resolve, node.retry?.delay || 0)
234
- );
235
- }
236
- }
237
- }
238
-
239
- /**
240
- * Handles correlated events for a node
241
- * @param node - The node with correlated events
242
- * @param nodeName - The name of the node
243
- * @throws Error if correlation fails or timeout occurs
244
- * @private
245
- */
246
- private async handleCorrelatedEvents(
247
- node: Node<T, any>,
248
- nodeName: string
249
- ): Promise<void> {
250
- if (node.correlateEvents) {
251
- const { events, timeout, correlation } = node.correlateEvents;
252
- this.logger.addLog(
253
- `⏳ Node "${nodeName}" waiting for correlated events: ${events.join(
254
- ", "
255
- )}`
256
- );
257
-
258
- try {
259
- // Attendre les événements
260
- const receivedEvents = await this.eventManager.waitForEvents(
261
- events,
262
- timeout
263
- );
264
-
265
- // Vérifier la corrélation
266
- if (!correlation(receivedEvents)) {
267
- this.logger.addLog(
268
- `❌ Event correlation failed for node "${nodeName}"`
269
- );
270
- throw new Error(`Event correlation failed for node "${nodeName}"`);
271
- }
272
-
273
- this.logger.addLog(
274
- `✅ Event correlation succeeded for node "${nodeName}"`
275
- );
276
- } catch (error) {
277
- this.logger.addLog(
278
- `❌ Error waiting for events: ${(error as Error).message}`
279
- );
280
- throw error;
281
- }
282
- }
283
- }
284
-
285
- /**
286
- * Handles waiting for events
287
- * @param node - The node waiting for events
288
- * @param nodeName - The name of the node
289
- * @throws Error if timeout occurs
290
- * @private
291
- */
292
- private async handleWaitForEvents(
293
- node: Node<T, any>,
294
- nodeName: string
295
- ): Promise<void> {
296
- if (node.waitForEvents) {
297
- const { events, timeout } = node.waitForEvents;
298
- this.logger.addLog(
299
- `⏳ Node "${nodeName}" waiting for events: ${events.join(", ")}`
300
- );
301
- await this.eventManager.waitForEvents(events, timeout);
302
- this.logger.addLog(`✅ All events received for node "${nodeName}"`);
303
- }
304
- }
305
- }
package/graph/observer.ts DELETED
@@ -1,368 +0,0 @@
1
- import {
2
- BehaviorSubject,
3
- Observable,
4
- Subject,
5
- combineLatest,
6
- firstValueFrom,
7
- } from "rxjs";
8
- import {
9
- debounceTime,
10
- distinctUntilChanged,
11
- filter,
12
- map,
13
- share,
14
- startWith,
15
- take,
16
- takeUntil,
17
- timeout,
18
- } from "rxjs/operators";
19
- import { ZodSchema } from "zod";
20
- import { GraphObservable } from "../interfaces";
21
- import { GraphContext, GraphEvent } from "../types";
22
- import { GraphFlow } from "./index";
23
-
24
- /**
25
- * GraphObserver class provides reactive observation capabilities for a GraphFlow instance
26
- * It allows monitoring state changes, node updates, and specific events in the graph
27
- * @template T - The Zod schema type that defines the structure of the graph data
28
- */
29
- export class GraphObserver<T extends ZodSchema> {
30
- constructor(
31
- private graph: GraphFlow<T>,
32
- private eventSubject: Subject<GraphEvent<T>>,
33
- private stateSubject: BehaviorSubject<GraphContext<T>>,
34
- private destroySubject: Subject<void>
35
- ) {}
36
-
37
- /**
38
- * Observes the entire graph state changes
39
- * @param options Configuration options for the observation
40
- * @param options.debounce Debounce time in milliseconds
41
- * @param options.delay Delay between emissions in milliseconds
42
- * @param options.stream If true, streams the specified properties letter by letter
43
- * @param options.properties List of properties to stream
44
- * @param options.onStreamLetter Callback for each letter emitted during streaming
45
- * @param options.onStreamComplete Callback when streaming is complete
46
- * @returns An Observable that emits the complete graph context whenever it changes
47
- */
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> {
58
- const baseObservable = new Observable<any>((subscriber) => {
59
- const subscription = combineLatest([
60
- this.eventSubject.pipe(
61
- filter((event) => event.type === "nodeStateChanged"),
62
- map((event) => event.payload.context),
63
- distinctUntilChanged(
64
- (prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
65
- ),
66
- debounceTime(options.debounce || 100)
67
- ),
68
- this.stateSubject,
69
- ])
70
- .pipe(
71
- map(([eventContext, stateContext]) => ({
72
- ...stateContext,
73
- ...eventContext,
74
- })),
75
- distinctUntilChanged(
76
- (prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
77
- )
78
- )
79
- .subscribe(subscriber);
80
-
81
- // Stream the specified properties if specified
82
- if (options.stream && options.properties) {
83
- const context = this.stateSubject.getValue();
84
- options.properties.forEach((property) => {
85
- const message = context[property];
86
- if (message) {
87
- this.streamMessage(
88
- message.toString(),
89
- 500,
90
- property as string
91
- ).subscribe({
92
- next: (data) => options.onStreamLetter?.(data),
93
- complete: () => options.onStreamComplete?.(),
94
- });
95
- }
96
- });
97
- }
98
-
99
- return () => subscription.unsubscribe();
100
- });
101
-
102
- // Extend the observable with our custom methods
103
- return Object.assign(baseObservable, {
104
- state: () => this.stateSubject.asObservable(),
105
- node: (nodeName: string) =>
106
- this.stateSubject.pipe(map((state) => ({ ...state, nodeName }))),
107
- nodes: (nodeNames: string[]) =>
108
- this.eventSubject.pipe(
109
- filter(
110
- (event) =>
111
- event.type === "nodeStateChanged" &&
112
- nodeNames.includes(event.payload?.name ?? "")
113
- ),
114
- map((event) => event.payload.context),
115
- distinctUntilChanged(
116
- (prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
117
- ),
118
- takeUntil(this.destroySubject),
119
- share()
120
- ),
121
- property: (props: string | string[]) =>
122
- this.stateSubject.pipe(
123
- map((state) => {
124
- const properties = Array.isArray(props) ? props : [props];
125
- return properties.reduce(
126
- (acc, prop) => ({
127
- ...acc,
128
- [prop]: state[prop],
129
- }),
130
- {}
131
- );
132
- })
133
- ),
134
- event: (eventName: string) =>
135
- this.eventSubject.pipe(filter((event) => event.type === eventName)),
136
- until: (
137
- observable: Observable<any>,
138
- predicate: (state: any) => boolean
139
- ) => firstValueFrom(observable.pipe(filter(predicate), take(1))),
140
- }) as GraphObservable<T>;
141
- }
142
-
143
- /**
144
- * Observes state changes for a specific node
145
- * @param name - The name of the node to observe
146
- * @returns An Observable that emits the graph context when the specified node changes
147
- */
148
- node(name: string): Observable<GraphContext<T>> {
149
- return this.eventSubject.pipe(
150
- filter(
151
- (event) =>
152
- event.type === "nodeStateChanged" && event.payload?.name === name
153
- ),
154
- map((event) => event.payload.context),
155
- distinctUntilChanged(
156
- (prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
157
- ),
158
- takeUntil(this.destroySubject),
159
- share()
160
- );
161
- }
162
-
163
- /**
164
- * Observes state changes for multiple nodes
165
- * @param names - Array of node names to observe
166
- * @returns An Observable that emits the graph context when any of the specified nodes change
167
- */
168
- nodes(names: string[]): Observable<GraphContext<T>> {
169
- return this.eventSubject.pipe(
170
- filter(
171
- (event) =>
172
- names.includes(event.payload?.name ?? "") &&
173
- event.type === "nodeStateChanged"
174
- ),
175
- map(() => this.graph.getContext()),
176
- distinctUntilChanged(
177
- (prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
178
- ),
179
- takeUntil(this.destroySubject),
180
- share()
181
- );
182
- }
183
-
184
- /**
185
- * Observes specific properties of the graph context
186
- * @param keys - Single or multiple property keys to observe
187
- * @returns An Observable that emits an object containing only the specified properties
188
- * @template K - The key of the property to observe from GraphContext<T>
189
- */
190
- property<K extends keyof GraphContext<T>>(
191
- keys: K | K[]
192
- ): Observable<{ [P in K]: GraphContext<T>[P] } & { name: string }> {
193
- const properties = Array.isArray(keys) ? keys : [keys];
194
-
195
- return this.eventSubject.pipe(
196
- filter(
197
- (event) =>
198
- event.type === "nodeStateChanged" &&
199
- properties.some((key) => event.payload?.property === key)
200
- ),
201
- map((event) => ({
202
- ...properties.reduce(
203
- (obj, key) => ({
204
- ...obj,
205
- [key]: event.payload.context[key],
206
- }),
207
- {} as { [P in K]: GraphContext<T>[P] }
208
- ),
209
- name: event.payload.name as string,
210
- })),
211
- startWith({
212
- ...properties.reduce(
213
- (obj, key) => ({
214
- ...obj,
215
- [key]: this.stateSubject.value[key],
216
- }),
217
- {}
218
- ),
219
- name: "initial",
220
- } as { [P in K]: GraphContext<T>[P] } & { name: string }),
221
- distinctUntilChanged(
222
- (prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
223
- ),
224
- share()
225
- );
226
- }
227
-
228
- /**
229
- * Observes specific events in the graph
230
- * @param type - The type of event to observe
231
- * @returns An Observable that emits events of the specified type
232
- */
233
- event(type: string): Observable<GraphEvent<T>> {
234
- return this.eventSubject.pipe(
235
- filter((event) => event.type === type),
236
- map((event) => event),
237
- takeUntil(this.destroySubject),
238
- share()
239
- );
240
- }
241
-
242
- /**
243
- * Waits for a specific condition to be met on an observable
244
- * @param observable - The Observable to watch
245
- * @param predicate - A function that returns true when the desired condition is met
246
- * @returns A Promise that resolves with the value when the predicate returns true
247
- * @template R - The type of value emitted by the observable
248
- */
249
- until<R>(
250
- observable: Observable<R>,
251
- predicate: (value: R) => boolean
252
- ): Promise<R> {
253
- return new Promise((resolve) => {
254
- const subscription = observable.subscribe({
255
- next: (value) => {
256
- if (predicate(value)) {
257
- subscription.unsubscribe();
258
- resolve(value);
259
- }
260
- },
261
- });
262
- });
263
- }
264
-
265
- /**
266
- * Waits for correlated events to occur and validates them using a correlation function
267
- * @param eventTypes - Array of event types to wait for
268
- * @param timeoutMs - Timeout duration in milliseconds
269
- * @param correlationFn - Function to validate the correlation between events
270
- * @returns Promise that resolves when all correlated events are received
271
- */
272
- waitForCorrelatedEvents(
273
- eventTypes: string[],
274
- timeoutMs: number,
275
- correlationFn: (events: GraphEvent<T>[]) => boolean
276
- ): Promise<GraphEvent<T>[]> {
277
- return new Promise((resolve, reject) => {
278
- const eventObservables = eventTypes.map((eventType) =>
279
- this.eventSubject.pipe(
280
- filter((event): event is GraphEvent<T> => {
281
- return event.type === eventType && "timestamp" in event;
282
- }),
283
- take(1)
284
- )
285
- );
286
-
287
- combineLatest(eventObservables)
288
- .pipe(timeout(timeoutMs), take(1))
289
- .subscribe({
290
- next: (events) => {
291
- if (correlationFn(events)) {
292
- resolve(events);
293
- } else {
294
- reject(new Error(`Correlation validation failed`));
295
- }
296
- },
297
- error: (error) => reject(error),
298
- });
299
- });
300
- }
301
-
302
- /**
303
- * Observes the current state of the graph
304
- * @returns Observable that emits the current graph context
305
- */
306
- observeState(): Observable<GraphContext<T>> {
307
- return this.stateSubject.asObservable().pipe(
308
- takeUntil(this.destroySubject),
309
- distinctUntilChanged(
310
- (prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
311
- )
312
- );
313
- }
314
-
315
- /**
316
- * Observes specific event types in the graph
317
- * @param eventType - The type of event to observe
318
- * @returns Observable that emits events of the specified type
319
- */
320
- observeEvents(eventType: string): Observable<GraphEvent<T>> {
321
- return this.eventSubject.asObservable().pipe(
322
- takeUntil(this.destroySubject),
323
- filter((event) => event.type === eventType)
324
- );
325
- }
326
-
327
- /**
328
- * Observes state changes for a specific node
329
- * @param nodeName - The name of the node to observe
330
- * @returns Observable that emits the graph context when the specified node changes
331
- */
332
- observeNodeState(nodeName: string): Observable<GraphContext<T>> {
333
- return this.eventSubject.asObservable().pipe(
334
- takeUntil(this.destroySubject),
335
- filter(
336
- (event) =>
337
- event.type === "nodeStateChanged" && event.payload?.name === nodeName
338
- ),
339
- map(() => this.stateSubject.value)
340
- );
341
- }
342
-
343
- /**
344
- * Streams a message letter by letter with a specified delay
345
- * @param message - The message to stream
346
- * @param delayMs - The delay in milliseconds between each letter
347
- * @param property - The property name being streamed
348
- * @returns An Observable that emits each letter of the message along with its property
349
- */
350
- streamMessage(
351
- message: string,
352
- delayMs: number,
353
- property: string
354
- ): Observable<{ letter: string; property: string }> {
355
- return new Observable<{ letter: string; property: string }>(
356
- (subscriber) => {
357
- for (let i = 0; i < message.length; i++) {
358
- setTimeout(() => {
359
- subscriber.next({ letter: message[i], property });
360
- if (i === message.length - 1) {
361
- subscriber.complete();
362
- }
363
- }, i * delayMs);
364
- }
365
- }
366
- );
367
- }
368
- }