@ai.ntellect/core 0.7.8 → 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 -93
  14. package/dist/graph/event-manager.d.ts.map +0 -1
  15. package/dist/graph/event-manager.js +0 -296
  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 -93
  26. package/dist/graph/node.d.ts.map +0 -1
  27. package/dist/graph/node.js +0 -259
  28. package/dist/graph/node.js.map +0 -1
  29. package/dist/graph/observer.d.ts +0 -115
  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 -170
  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 -363
  87. package/graph/index.ts +0 -395
  88. package/graph/logger.ts +0 -70
  89. package/graph/node.ts +0 -327
  90. package/graph/observer.ts +0 -368
  91. package/interfaces/index.ts +0 -548
  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 -118
  102. package/test/graph/index.test.ts +0 -684
  103. package/test/graph/node.test.ts +0 -655
  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 -190
  110. package/utils/generate-action-schema.ts +0 -46
  111. package/utils/header-builder.ts +0 -40
package/graph/node.ts DELETED
@@ -1,327 +0,0 @@
1
- import { IEventEmitter } from "interfaces";
2
- import { BehaviorSubject, Subject } from "rxjs";
3
- import { ZodSchema } from "zod";
4
- import { GraphContext, GraphEvent, Node } from "../types";
5
- import { GraphEventManager } from "./event-manager";
6
-
7
- /**
8
- * Represents a node in the graph that can execute operations and manage state
9
- * @template T - The Zod schema type for validation
10
- */
11
- export interface NodeParams<T = any> {
12
- [key: string]: T;
13
- }
14
-
15
- export interface GraphLogger {
16
- addLog: (message: string, data?: any) => void;
17
- }
18
-
19
- export class GraphNode<T extends ZodSchema> {
20
- private lastStateEvent: GraphEvent<T> | null = null;
21
- private eventEmitter: IEventEmitter;
22
-
23
- /**
24
- * Creates a new GraphNode instance
25
- * @param nodes - Map of all nodes in the graph
26
- * @param logger - Logger instance for tracking node operations
27
- * @param eventManager - Manager for handling graph events
28
- * @param eventSubject - Subject for emitting events
29
- * @param stateSubject - Subject for managing graph state
30
- */
31
- constructor(
32
- private nodes: Map<string, Node<T, any>>,
33
- private logger: GraphLogger,
34
- private eventManager: GraphEventManager<T>,
35
- private eventSubject: Subject<GraphEvent<T>>,
36
- private stateSubject: BehaviorSubject<GraphContext<T>>
37
- ) {
38
- this.eventEmitter = eventManager["eventEmitter"];
39
- }
40
-
41
- /**
42
- * Emits an event with the specified type and payload
43
- * @param type - The type of event to emit
44
- * @param payload - The data associated with the event
45
- * @private
46
- */
47
- private emitEvent(type: string, payload: any) {
48
- if (type === "nodeStateChanged") {
49
- if (
50
- this.lastStateEvent?.type === type &&
51
- this.lastStateEvent.payload.property === payload.property &&
52
- this.lastStateEvent.payload.newValue === payload.newValue &&
53
- this.lastStateEvent.payload.nodeName === payload.nodeName
54
- ) {
55
- return;
56
- }
57
- }
58
-
59
- const event = {
60
- type,
61
- payload: {
62
- ...payload,
63
- name: type === "nodeStateChanged" ? payload.nodeName : payload.name,
64
- context: { ...payload.context },
65
- },
66
- timestamp: Date.now(),
67
- };
68
-
69
- this.eventSubject.next(event);
70
- this.eventManager.emitEvent(type, event);
71
-
72
- if (type === "nodeStateChanged") {
73
- this.lastStateEvent = event;
74
- this.stateSubject.next({ ...payload.context });
75
- }
76
- }
77
-
78
- /**
79
- * Executes a node with the given name and context
80
- * @param nodeName - The name of the node to execute
81
- * @param context - The current graph context
82
- * @param params - Input data for the node
83
- * @param triggeredByEvent - Whether the execution was triggered by an event
84
- * @throws Error if the node is not found or execution fails
85
- */
86
- public async executeNode(
87
- nodeName: string,
88
- context: GraphContext<T>,
89
- params: any,
90
- triggeredByEvent: boolean = false
91
- ): Promise<void> {
92
- const node = this.nodes.get(nodeName);
93
- if (!node) throw new Error(`Node "${nodeName}" not found.`);
94
-
95
- const nodeContext = { ...context };
96
- this.emitEvent("nodeStarted", { name: nodeName, context: nodeContext });
97
-
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
-
117
- const contextProxy = new Proxy(nodeContext, {
118
- set: (target: any, prop: string | symbol, value: any) => {
119
- const oldValue = target[prop.toString()];
120
- if (oldValue === value) return true;
121
-
122
- target[prop.toString()] = value;
123
- // Mettre à jour le contexte global
124
- context[prop.toString() as keyof typeof context] = value;
125
-
126
- this.emitEvent("nodeStateChanged", {
127
- nodeName,
128
- property: prop.toString(),
129
- oldValue,
130
- newValue: value,
131
- context: { ...target },
132
- });
133
- return true;
134
- },
135
- });
136
-
137
- if (node.condition && !node.condition(contextProxy, params)) {
138
- return;
139
- }
140
-
141
- await this.executeWithRetry(node, contextProxy, nodeName, params);
142
- this.emitEvent("nodeCompleted", { name: nodeName, context: nodeContext });
143
-
144
- if (!triggeredByEvent && node.next) {
145
- const nextNodes =
146
- typeof node.next === "function" ? node.next(contextProxy) : node.next;
147
- for (const nextNodeName of nextNodes) {
148
- await this.executeNode(nextNodeName, context, undefined, false);
149
- }
150
- }
151
- } catch (error) {
152
- this.emitEvent("nodeError", {
153
- name: nodeName,
154
- error,
155
- context: nodeContext,
156
- });
157
- throw error;
158
- }
159
- }
160
-
161
- /**
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
165
- * @param nodeName - The name of the node (for error messages)
166
- * @throws Error if validation fails
167
- * @private
168
- */
169
- private async validateParams(
170
- node: Node<T, any>,
171
- params: any,
172
- nodeName: string
173
- ): Promise<void> {
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;
176
-
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}"`);
180
- }
181
-
182
- try {
183
- return node.params.parse(params);
184
- } catch (error: any) {
185
- throw error;
186
- }
187
- }
188
-
189
- /**
190
- * Handles event-related operations for a node
191
- * @param node - The node whose events need handling
192
- * @param nodeName - The name of the node
193
- * @param context - The current graph context
194
- * @private
195
- */
196
- private async handleEvents(
197
- node: Node<T, any>,
198
- nodeName: string,
199
- context: GraphContext<T>
200
- ): Promise<void> {
201
- if (node.correlateEvents) {
202
- await this.handleCorrelatedEvents(node, nodeName);
203
- }
204
-
205
- if (node.waitForEvents) {
206
- await this.handleWaitForEvents(node, nodeName);
207
- }
208
- }
209
-
210
- /**
211
- * Executes a node with retry logic
212
- * @param node - The node to execute
213
- * @param contextProxy - The proxied graph context
214
- * @param params - Input data for the node
215
- * @param nodeName - The name of the node
216
- * @param params - Parameters for the node
217
- * @throws Error if all retry attempts fail
218
- * @private
219
- */
220
- private async executeWithRetry(
221
- node: Node<T, any>,
222
- contextProxy: GraphContext<T>,
223
- nodeName: string,
224
- params?: NodeParams
225
- ): Promise<void> {
226
- let attempts = 0;
227
- let lastError: Error = new Error("Unknown error");
228
-
229
- while (attempts < (node.retry?.maxAttempts || 1)) {
230
- try {
231
- if (node.params) {
232
- await this.validateParams(node, params, nodeName);
233
- }
234
-
235
- await node.execute(contextProxy, params, {
236
- eventEmitter: this.eventEmitter,
237
- });
238
- return;
239
- } catch (error: any) {
240
- lastError =
241
- error instanceof Error
242
- ? error
243
- : new Error(error?.message || "Unknown error");
244
- attempts++;
245
-
246
- if (attempts === (node.retry?.maxAttempts || 1)) {
247
- if (node.retry?.onRetryFailed) {
248
- await node.retry.onRetryFailed(lastError, contextProxy);
249
- if (node.retry.continueOnFailed) return;
250
- }
251
- throw lastError;
252
- }
253
-
254
- await new Promise((resolve) =>
255
- setTimeout(resolve, node.retry?.delay || 0)
256
- );
257
- }
258
- }
259
- }
260
-
261
- /**
262
- * Handles correlated events for a node
263
- * @param node - The node with correlated events
264
- * @param nodeName - The name of the node
265
- * @throws Error if correlation fails or timeout occurs
266
- * @private
267
- */
268
- private async handleCorrelatedEvents(
269
- node: Node<T, any>,
270
- nodeName: string
271
- ): Promise<void> {
272
- if (node.correlateEvents) {
273
- const { events, timeout, correlation } = node.correlateEvents;
274
- this.logger.addLog(
275
- `⏳ Node "${nodeName}" waiting for correlated events: ${events.join(
276
- ", "
277
- )}`
278
- );
279
-
280
- try {
281
- // Attendre les événements
282
- const receivedEvents = await this.eventManager.waitForEvents(
283
- events,
284
- timeout
285
- );
286
-
287
- // Vérifier la corrélation
288
- if (!correlation(receivedEvents)) {
289
- this.logger.addLog(
290
- `❌ Event correlation failed for node "${nodeName}"`
291
- );
292
- throw new Error(`Event correlation failed for node "${nodeName}"`);
293
- }
294
-
295
- this.logger.addLog(
296
- `✅ Event correlation succeeded for node "${nodeName}"`
297
- );
298
- } catch (error) {
299
- this.logger.addLog(
300
- `❌ Error waiting for events: ${(error as Error).message}`
301
- );
302
- throw error;
303
- }
304
- }
305
- }
306
-
307
- /**
308
- * Handles waiting for events
309
- * @param node - The node waiting for events
310
- * @param nodeName - The name of the node
311
- * @throws Error if timeout occurs
312
- * @private
313
- */
314
- private async handleWaitForEvents(
315
- node: Node<T, any>,
316
- nodeName: string
317
- ): Promise<void> {
318
- if (node.waitForEvents) {
319
- const { events, timeout } = node.waitForEvents;
320
- this.logger.addLog(
321
- `⏳ Node "${nodeName}" waiting for events: ${events.join(", ")}`
322
- );
323
- await this.eventManager.waitForEvents(events, timeout);
324
- this.logger.addLog(`✅ All events received for node "${nodeName}"`);
325
- }
326
- }
327
- }
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
- 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
-
33
- /**
34
- * GraphObserver class provides reactive observation capabilities for a GraphFlow instance
35
- * It allows monitoring state changes, node updates, and specific events in the graph
36
- * @template T - The Zod schema type that defines the structure of the graph data
37
- */
38
- export class GraphObserver<T extends ZodSchema> {
39
- constructor(
40
- private graph: GraphFlow<T>,
41
- private eventSubject: Subject<GraphEvent<T>>,
42
- private stateSubject: BehaviorSubject<GraphContext<T>>,
43
- private destroySubject: Subject<void>
44
- ) {}
45
-
46
- /**
47
- * Observes the entire graph state changes
48
- * @param options Configuration options for the observation
49
- * @param options.debounce Debounce time in milliseconds
50
- * @param options.delay Delay between emissions in milliseconds
51
- * @param options.stream If true, streams the specified properties letter by letter
52
- * @param options.properties List of properties to stream
53
- * @param options.onStreamLetter Callback for each letter emitted during streaming
54
- * @param options.onStreamComplete Callback when streaming is complete
55
- * @returns An Observable that emits the complete graph context whenever it changes
56
- */
57
- state(options: ObserverOptions = {}): 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
- }