@ai.ntellect/core 0.6.21 → 0.7.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.
- package/.mocharc.json +2 -1
- package/README.md +61 -85
- package/graph/controller.ts +1 -1
- package/graph/event-manager.ts +288 -0
- package/graph/index.ts +153 -367
- package/graph/logger.ts +70 -0
- package/graph/node.ts +398 -0
- package/graph/observer.ts +361 -0
- package/interfaces/index.ts +102 -1
- package/modules/agenda/index.ts +3 -16
- package/package.json +10 -5
- package/test/graph/index.test.ts +244 -113
- package/test/graph/observer.test.ts +398 -0
- package/test/modules/agenda/node-cron.test.ts +37 -16
- package/test/modules/memory/adapters/in-memory.test.ts +2 -2
- package/test/modules/memory/adapters/meilisearch.test.ts +28 -24
- package/test/modules/memory/base.test.ts +3 -3
- package/tsconfig.json +4 -2
- package/types/index.ts +23 -2
- package/dist/graph/controller.js +0 -72
- package/dist/graph/index.js +0 -501
- package/dist/index.js +0 -41
- package/dist/interfaces/index.js +0 -17
- package/dist/modules/agenda/adapters/node-cron/index.js +0 -29
- package/dist/modules/agenda/index.js +0 -140
- package/dist/modules/embedding/adapters/ai/index.js +0 -57
- package/dist/modules/embedding/index.js +0 -59
- package/dist/modules/memory/adapters/in-memory/index.js +0 -210
- package/dist/modules/memory/adapters/meilisearch/index.js +0 -320
- package/dist/modules/memory/adapters/redis/index.js +0 -158
- package/dist/modules/memory/index.js +0 -103
- package/dist/types/index.js +0 -2
- package/dist/utils/generate-action-schema.js +0 -43
- package/dist/utils/header-builder.js +0 -34
- package/test/modules/embedding/ai.test.ts +0 -78
- package/test/modules/memory/adapters/redis.test.ts +0 -169
- package/test/services/agenda.test.ts +0 -279
@@ -0,0 +1,398 @@
|
|
1
|
+
import { expect } from "chai";
|
2
|
+
import { BehaviorSubject, Subject } from "rxjs";
|
3
|
+
import { z } from "zod";
|
4
|
+
import { GraphFlow } from "../../graph";
|
5
|
+
import { GraphObserver } from "../../graph/observer";
|
6
|
+
import { GraphContext, GraphEvent, Node } from "../../types";
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Test schema definition for observer tests
|
10
|
+
* Defines a schema with:
|
11
|
+
* - value: numeric value for tracking state changes
|
12
|
+
* - status: enum representing node execution status
|
13
|
+
*/
|
14
|
+
const TestSchema = z.object({
|
15
|
+
value: z.number().default(0),
|
16
|
+
status: z.enum(["pending", "completed", "failed"]).optional(),
|
17
|
+
});
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Test suite for the GraphObserver implementation
|
21
|
+
* This suite validates the reactive observation capabilities:
|
22
|
+
* - State observation (global and node-specific)
|
23
|
+
* - Property observation (single and multiple)
|
24
|
+
* - Event observation and correlation
|
25
|
+
* - Workflow monitoring
|
26
|
+
* - Node execution triggers
|
27
|
+
*/
|
28
|
+
describe("GraphObserver", () => {
|
29
|
+
let graph: GraphFlow<typeof TestSchema>;
|
30
|
+
let eventSubject: Subject<GraphEvent<typeof TestSchema>>;
|
31
|
+
let stateSubject: BehaviorSubject<GraphContext<typeof TestSchema>>;
|
32
|
+
let destroySubject: Subject<void>;
|
33
|
+
let observer: GraphObserver<typeof TestSchema>;
|
34
|
+
|
35
|
+
beforeEach(() => {
|
36
|
+
graph = new GraphFlow("TestGraph", {
|
37
|
+
name: "TestGraph",
|
38
|
+
nodes: [],
|
39
|
+
context: { value: 0 },
|
40
|
+
schema: TestSchema,
|
41
|
+
});
|
42
|
+
|
43
|
+
// Initialize subjects
|
44
|
+
eventSubject = new Subject();
|
45
|
+
stateSubject = new BehaviorSubject(graph.getContext());
|
46
|
+
destroySubject = new Subject();
|
47
|
+
|
48
|
+
// Create observer instance
|
49
|
+
observer = new GraphObserver(
|
50
|
+
graph,
|
51
|
+
eventSubject,
|
52
|
+
stateSubject,
|
53
|
+
destroySubject
|
54
|
+
);
|
55
|
+
});
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Test suite for state observation functionality
|
59
|
+
* Validates the ability to track and react to state changes
|
60
|
+
* in the graph context
|
61
|
+
*/
|
62
|
+
describe("State Observation", () => {
|
63
|
+
/**
|
64
|
+
* Tests sequential state change observation
|
65
|
+
* Validates that:
|
66
|
+
* - All state changes are captured in order
|
67
|
+
* - Multiple state changes in a single node are tracked
|
68
|
+
* - Initial state is included in observations
|
69
|
+
*/
|
70
|
+
it("should observe state changes", async () => {
|
71
|
+
const states: any[] = [];
|
72
|
+
const testNode: Node<typeof TestSchema> = {
|
73
|
+
name: "testNode",
|
74
|
+
execute: async (context) => {
|
75
|
+
context.value = 1;
|
76
|
+
context.value = 2;
|
77
|
+
context.value = 3;
|
78
|
+
},
|
79
|
+
};
|
80
|
+
|
81
|
+
graph.addNode(testNode);
|
82
|
+
const subscription = graph
|
83
|
+
.observe()
|
84
|
+
.state()
|
85
|
+
.subscribe((state) => {
|
86
|
+
if (state.value !== undefined) {
|
87
|
+
states.push(state.value);
|
88
|
+
}
|
89
|
+
});
|
90
|
+
|
91
|
+
await graph.execute("testNode");
|
92
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
93
|
+
subscription.unsubscribe();
|
94
|
+
|
95
|
+
expect(states).to.deep.equal([0, 1, 2, 3]);
|
96
|
+
});
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Tests node-specific state observation
|
100
|
+
* Validates that:
|
101
|
+
* - State changes for specific nodes are tracked
|
102
|
+
* - Status transitions are captured correctly
|
103
|
+
* - Async state changes are properly observed
|
104
|
+
*/
|
105
|
+
it("should observe specific node state changes", async () => {
|
106
|
+
const states: any[] = [];
|
107
|
+
const testNode: Node<typeof TestSchema> = {
|
108
|
+
name: "testNode",
|
109
|
+
execute: async (context) => {
|
110
|
+
context.status = "pending";
|
111
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
112
|
+
context.status = "completed";
|
113
|
+
},
|
114
|
+
};
|
115
|
+
|
116
|
+
graph.addNode(testNode);
|
117
|
+
const subscription = graph
|
118
|
+
.observe()
|
119
|
+
.node("testNode")
|
120
|
+
.subscribe((state) => state.status && states.push(state.status));
|
121
|
+
|
122
|
+
await graph.execute("testNode");
|
123
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
124
|
+
subscription.unsubscribe();
|
125
|
+
|
126
|
+
expect(states).to.deep.equal(["pending", "completed"]);
|
127
|
+
});
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Tests multi-node state observation
|
131
|
+
* Validates that:
|
132
|
+
* - Multiple nodes can be observed simultaneously
|
133
|
+
* - State changes are correctly attributed to nodes
|
134
|
+
* - Sequential execution is properly tracked
|
135
|
+
*/
|
136
|
+
it("should observe multiple nodes", async () => {
|
137
|
+
const states: any[] = [];
|
138
|
+
const node1: Node<typeof TestSchema> = {
|
139
|
+
name: "node1",
|
140
|
+
execute: async (context) => {
|
141
|
+
context.value = 1;
|
142
|
+
},
|
143
|
+
};
|
144
|
+
const node2: Node<typeof TestSchema> = {
|
145
|
+
name: "node2",
|
146
|
+
execute: async (context) => {
|
147
|
+
context.value = 2;
|
148
|
+
},
|
149
|
+
};
|
150
|
+
|
151
|
+
graph.addNode(node1);
|
152
|
+
graph.addNode(node2);
|
153
|
+
|
154
|
+
const subscription = graph
|
155
|
+
.observe()
|
156
|
+
.nodes(["node1", "node2"])
|
157
|
+
.subscribe((state) => state.value && states.push(state.value));
|
158
|
+
|
159
|
+
await graph.execute("node1");
|
160
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
161
|
+
await graph.execute("node2");
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
163
|
+
subscription.unsubscribe();
|
164
|
+
|
165
|
+
expect(states).to.deep.equal([1, 2]);
|
166
|
+
});
|
167
|
+
});
|
168
|
+
|
169
|
+
/**
|
170
|
+
* Test suite for property observation functionality
|
171
|
+
* Validates the ability to track specific properties
|
172
|
+
* of the graph context
|
173
|
+
*/
|
174
|
+
describe("Property Observation", () => {
|
175
|
+
/**
|
176
|
+
* Tests single property observation
|
177
|
+
* Validates that:
|
178
|
+
* - Individual property changes are tracked
|
179
|
+
* - Initial property value is captured
|
180
|
+
* - Property updates trigger observations
|
181
|
+
*/
|
182
|
+
it("should observe single property", async () => {
|
183
|
+
const values: any[] = [];
|
184
|
+
const testNode: Node<typeof TestSchema> = {
|
185
|
+
name: "testNode",
|
186
|
+
execute: async (context) => {
|
187
|
+
context.value = 42;
|
188
|
+
},
|
189
|
+
};
|
190
|
+
|
191
|
+
graph.addNode(testNode);
|
192
|
+
const subscription = graph
|
193
|
+
.observe()
|
194
|
+
.property("value")
|
195
|
+
.subscribe((state) => values.push(state.value));
|
196
|
+
|
197
|
+
await graph.execute("testNode");
|
198
|
+
subscription.unsubscribe();
|
199
|
+
|
200
|
+
expect(values).to.deep.equal([0, 42]);
|
201
|
+
});
|
202
|
+
|
203
|
+
/**
|
204
|
+
* Tests multiple property observation
|
205
|
+
* Validates that:
|
206
|
+
* - Multiple properties can be tracked simultaneously
|
207
|
+
* - Changes to any observed property trigger updates
|
208
|
+
* - Final state contains all tracked properties
|
209
|
+
*/
|
210
|
+
it("should observe multiple properties", async () => {
|
211
|
+
const values: any[] = [];
|
212
|
+
const testNode: Node<typeof TestSchema> = {
|
213
|
+
name: "testNode",
|
214
|
+
execute: async (context) => {
|
215
|
+
context.value = 42;
|
216
|
+
context.status = "completed";
|
217
|
+
},
|
218
|
+
};
|
219
|
+
|
220
|
+
graph.addNode(testNode);
|
221
|
+
const subscription = graph
|
222
|
+
.observe()
|
223
|
+
.property(["value", "status"])
|
224
|
+
.subscribe((change) => {
|
225
|
+
const { name, ...rest } = change;
|
226
|
+
values.push(rest);
|
227
|
+
});
|
228
|
+
|
229
|
+
await graph.execute("testNode");
|
230
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
231
|
+
subscription.unsubscribe();
|
232
|
+
|
233
|
+
expect(values[values.length - 1]).to.deep.equal({
|
234
|
+
value: 42,
|
235
|
+
status: "completed",
|
236
|
+
});
|
237
|
+
});
|
238
|
+
|
239
|
+
/**
|
240
|
+
* Tests condition-based observation
|
241
|
+
* Validates that:
|
242
|
+
* - Multiple conditions can be combined
|
243
|
+
* - Observation completes when conditions are met
|
244
|
+
* - State reflects all required changes
|
245
|
+
*/
|
246
|
+
it("should wait for multiple conditions", async () => {
|
247
|
+
const testNode: Node<typeof TestSchema> = {
|
248
|
+
name: "testNode",
|
249
|
+
execute: async (context) => {
|
250
|
+
context.value = 42;
|
251
|
+
context.status = "completed";
|
252
|
+
},
|
253
|
+
};
|
254
|
+
|
255
|
+
graph.addNode(testNode);
|
256
|
+
const promise = graph
|
257
|
+
.observe()
|
258
|
+
.until(
|
259
|
+
graph.observe().property(["value", "status"]),
|
260
|
+
(state) => state.value === 42 && state.status === "completed"
|
261
|
+
);
|
262
|
+
|
263
|
+
await graph.execute("testNode");
|
264
|
+
await promise;
|
265
|
+
|
266
|
+
const context = graph.getContext();
|
267
|
+
expect(context.value).to.equal(42);
|
268
|
+
expect(context.status).to.equal("completed");
|
269
|
+
});
|
270
|
+
});
|
271
|
+
|
272
|
+
/**
|
273
|
+
* Test suite for event observation functionality
|
274
|
+
* Validates the handling of events and event correlation
|
275
|
+
*/
|
276
|
+
describe("Event Observation", () => {
|
277
|
+
/**
|
278
|
+
* Tests specific event observation
|
279
|
+
*/
|
280
|
+
it("should observe specific events", async () => {
|
281
|
+
const events: GraphEvent<typeof TestSchema>[] = [];
|
282
|
+
const subscription = graph
|
283
|
+
.observe()
|
284
|
+
.event("testEvent")
|
285
|
+
.subscribe((e) => events.push(e));
|
286
|
+
|
287
|
+
await graph.emit("testEvent", { value: 1 });
|
288
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
289
|
+
subscription.unsubscribe();
|
290
|
+
|
291
|
+
expect(events.length).to.equal(1);
|
292
|
+
expect(events[0].type).to.equal("testEvent");
|
293
|
+
});
|
294
|
+
|
295
|
+
/**
|
296
|
+
* Tests debounced event handling
|
297
|
+
*/
|
298
|
+
it("should handle debounced events", async () => {
|
299
|
+
const events: GraphEvent<typeof TestSchema>[] = [];
|
300
|
+
const subscription = graph
|
301
|
+
.observe()
|
302
|
+
.event("event1")
|
303
|
+
.subscribe((e) => events.push(e));
|
304
|
+
|
305
|
+
await graph.emit("event1", { value: 1 });
|
306
|
+
await graph.emit("event1", { value: 2 });
|
307
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
308
|
+
subscription.unsubscribe();
|
309
|
+
|
310
|
+
expect(events.length).to.equal(2);
|
311
|
+
});
|
312
|
+
|
313
|
+
/**
|
314
|
+
* Tests event correlation functionality
|
315
|
+
* Validates that:
|
316
|
+
* - Multiple events can be correlated
|
317
|
+
* - Correlation conditions are properly evaluated
|
318
|
+
* - Timeout handling works correctly
|
319
|
+
* - Events are captured in correct order
|
320
|
+
*/
|
321
|
+
it("should wait for correlated events", async () => {
|
322
|
+
// Create test events
|
323
|
+
const eventA = {
|
324
|
+
type: "eventA",
|
325
|
+
payload: { eventPayload: { status: "success" } },
|
326
|
+
timestamp: Date.now(),
|
327
|
+
} as GraphEvent<typeof TestSchema>;
|
328
|
+
|
329
|
+
const eventB = {
|
330
|
+
type: "eventB",
|
331
|
+
payload: { eventPayload: { status: "success" } },
|
332
|
+
timestamp: Date.now(),
|
333
|
+
} as GraphEvent<typeof TestSchema>;
|
334
|
+
|
335
|
+
// Emit events after a short delay
|
336
|
+
setTimeout(() => {
|
337
|
+
eventSubject.next(eventA);
|
338
|
+
eventSubject.next(eventB);
|
339
|
+
}, 100);
|
340
|
+
|
341
|
+
const events = await observer.waitForCorrelatedEvents(
|
342
|
+
["eventA", "eventB"],
|
343
|
+
2000,
|
344
|
+
(events) =>
|
345
|
+
events.every((e) => e.payload.eventPayload?.status === "success")
|
346
|
+
);
|
347
|
+
|
348
|
+
expect(events.length).to.equal(2);
|
349
|
+
expect(events[0].type).to.equal("eventA");
|
350
|
+
expect(events[1].type).to.equal("eventB");
|
351
|
+
});
|
352
|
+
|
353
|
+
afterEach(() => {
|
354
|
+
destroySubject.next();
|
355
|
+
destroySubject.complete();
|
356
|
+
eventSubject.complete();
|
357
|
+
stateSubject.complete();
|
358
|
+
});
|
359
|
+
});
|
360
|
+
|
361
|
+
/**
|
362
|
+
* Test suite for workflow observation functionality
|
363
|
+
* Validates the ability to monitor complete workflow execution
|
364
|
+
*/
|
365
|
+
describe("Workflow Observation", () => {
|
366
|
+
/**
|
367
|
+
* Tests complete workflow observation
|
368
|
+
* Validates that:
|
369
|
+
* - Entire workflow execution is tracked
|
370
|
+
* - State transitions are captured
|
371
|
+
* - Final state reflects completed workflow
|
372
|
+
*/
|
373
|
+
it("should observe complete workflow", async () => {
|
374
|
+
const states: any[] = [];
|
375
|
+
const node: Node<typeof TestSchema> = {
|
376
|
+
name: "testNode",
|
377
|
+
execute: async (context) => {
|
378
|
+
context.status = "pending";
|
379
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
380
|
+
context.status = "completed";
|
381
|
+
},
|
382
|
+
};
|
383
|
+
|
384
|
+
graph.addNode(node);
|
385
|
+
const subscription = graph
|
386
|
+
.observe()
|
387
|
+
.node("testNode")
|
388
|
+
.subscribe((state) => states.push(state));
|
389
|
+
|
390
|
+
await graph.execute("testNode");
|
391
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
392
|
+
subscription.unsubscribe();
|
393
|
+
|
394
|
+
expect(states.length).to.be.greaterThan(0);
|
395
|
+
expect(states[states.length - 1].status).to.equal("completed");
|
396
|
+
});
|
397
|
+
});
|
398
|
+
});
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import { expect } from "chai";
|
2
2
|
import sinon from "sinon";
|
3
3
|
import { Agenda } from "../../../modules/agenda";
|
4
|
-
import { NodeCronAdapter } from "../../../modules/agenda/adapters/
|
4
|
+
import { NodeCronAdapter } from "../../../modules/agenda/adapters/node-cron";
|
5
|
+
import { Memory } from "../../../modules/memory";
|
5
6
|
import { InMemoryAdapter } from "../../../modules/memory/adapters/in-memory";
|
6
7
|
|
7
8
|
before(function () {
|
@@ -14,8 +15,9 @@ describe("Agenda Service", () => {
|
|
14
15
|
|
15
16
|
beforeEach(() => {
|
16
17
|
const cronService = new NodeCronAdapter();
|
17
|
-
const
|
18
|
-
|
18
|
+
const inMemory = new InMemoryAdapter();
|
19
|
+
const memory = new Memory(inMemory);
|
20
|
+
agenda = new Agenda(cronService, memory);
|
19
21
|
});
|
20
22
|
|
21
23
|
afterEach(async () => {
|
@@ -42,9 +44,9 @@ describe("Agenda Service", () => {
|
|
42
44
|
scheduledIds.push(id); // Track the ID
|
43
45
|
|
44
46
|
expect(id).to.be.a("string");
|
45
|
-
expect(agenda.getScheduledRequests()).to.have.lengthOf(1);
|
47
|
+
expect(await agenda.getScheduledRequests()).to.have.lengthOf(1);
|
46
48
|
|
47
|
-
const scheduledRequest = agenda.getScheduledRequests()[0];
|
49
|
+
const scheduledRequest = (await agenda.getScheduledRequests())[0];
|
48
50
|
expect(scheduledRequest.originalRequest).to.equal(
|
49
51
|
request.originalRequest
|
50
52
|
);
|
@@ -103,13 +105,11 @@ describe("Agenda Service", () => {
|
|
103
105
|
|
104
106
|
const id = await agenda.scheduleRequest(request);
|
105
107
|
scheduledIds.push(id);
|
106
|
-
|
107
|
-
expect(requests).to.have.lengthOf(1);
|
108
|
+
expect(await agenda.getScheduledRequests()).to.have.lengthOf(1);
|
108
109
|
|
109
110
|
const cancelled = await agenda.cancelScheduledRequest(id);
|
110
111
|
expect(cancelled).to.be.true;
|
111
|
-
|
112
|
-
expect(remainingRequests).to.have.lengthOf(0);
|
112
|
+
expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
|
113
113
|
});
|
114
114
|
|
115
115
|
it("should return false when cancelling non-existent request", async () => {
|
@@ -139,6 +139,28 @@ describe("Agenda Service", () => {
|
|
139
139
|
expect(scheduledRequests[0].originalRequest).to.equal("request 1");
|
140
140
|
expect(scheduledRequests[1].originalRequest).to.equal("request 2");
|
141
141
|
});
|
142
|
+
|
143
|
+
it("should stop all scheduled requests", async () => {
|
144
|
+
const requests = [
|
145
|
+
{
|
146
|
+
originalRequest: "request 1",
|
147
|
+
cronExpression: "*/1 * * * *",
|
148
|
+
},
|
149
|
+
{
|
150
|
+
originalRequest: "request 2",
|
151
|
+
cronExpression: "*/5 * * * *",
|
152
|
+
},
|
153
|
+
];
|
154
|
+
|
155
|
+
for (const request of requests) {
|
156
|
+
await agenda.scheduleRequest(request);
|
157
|
+
}
|
158
|
+
|
159
|
+
expect(await agenda.getScheduledRequests()).to.have.lengthOf(2);
|
160
|
+
|
161
|
+
await agenda.stopAll();
|
162
|
+
expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
|
163
|
+
});
|
142
164
|
});
|
143
165
|
|
144
166
|
describe("Global Management", () => {
|
@@ -158,12 +180,10 @@ describe("Agenda Service", () => {
|
|
158
180
|
await agenda.scheduleRequest(request);
|
159
181
|
}
|
160
182
|
|
161
|
-
|
162
|
-
expect(initialRequests).to.have.lengthOf(2);
|
183
|
+
expect(await agenda.getScheduledRequests()).to.have.lengthOf(2);
|
163
184
|
|
164
185
|
await agenda.stopAll();
|
165
|
-
|
166
|
-
expect(remainingRequests).to.have.lengthOf(0);
|
186
|
+
expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
|
167
187
|
});
|
168
188
|
});
|
169
189
|
|
@@ -223,7 +243,7 @@ describe("Agenda Service", () => {
|
|
223
243
|
}
|
224
244
|
|
225
245
|
expect(onExecutedSpy.calledOnce).to.be.true;
|
226
|
-
expect(agenda.getScheduledRequests()).to.have.lengthOf(0);
|
246
|
+
expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
|
227
247
|
});
|
228
248
|
|
229
249
|
it("should log execution status", async function () {
|
@@ -270,8 +290,9 @@ describe("Agenda Service", () => {
|
|
270
290
|
let globalAgenda: Agenda;
|
271
291
|
before(() => {
|
272
292
|
const cronService = new NodeCronAdapter();
|
273
|
-
const
|
274
|
-
|
293
|
+
const inMemoryAdapter = new InMemoryAdapter();
|
294
|
+
const memory = new Memory(inMemoryAdapter);
|
295
|
+
globalAgenda = new Agenda(cronService, memory);
|
275
296
|
});
|
276
297
|
|
277
298
|
after(async () => {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { expect } from "chai";
|
2
|
-
import { InMemoryAdapter } from "
|
3
|
-
import { BaseMemoryType } from "
|
2
|
+
import { InMemoryAdapter } from "../../../../modules/memory/adapters/in-memory";
|
3
|
+
import { BaseMemoryType } from "../../../../types";
|
4
4
|
|
5
5
|
describe("InMemoryAdapter", () => {
|
6
6
|
let inMemoryAdapter: InMemoryAdapter;
|
@@ -1,8 +1,7 @@
|
|
1
1
|
import { expect } from "chai";
|
2
2
|
import dotenv from "dotenv";
|
3
|
-
import { MeilisearchAdapter } from "
|
4
|
-
import { BaseMemoryType } from "
|
5
|
-
|
3
|
+
import { MeilisearchAdapter } from "../../../../modules/memory/adapters/meilisearch";
|
4
|
+
import { BaseMemoryType } from "../../../../types";
|
6
5
|
// Load environment variables
|
7
6
|
dotenv.config();
|
8
7
|
|
@@ -126,7 +125,6 @@ describe("MeilisearchAdapter", () => {
|
|
126
125
|
|
127
126
|
try {
|
128
127
|
await meilisearchAdapter.init(TEST_ROOM_ID);
|
129
|
-
await meilisearchAdapter.initializeStorage(TEST_ROOM_ID);
|
130
128
|
} catch (error) {
|
131
129
|
console.error("Failed to initialize:", error);
|
132
130
|
throw error;
|
@@ -134,14 +132,14 @@ describe("MeilisearchAdapter", () => {
|
|
134
132
|
});
|
135
133
|
|
136
134
|
it("should create memory", async () => {
|
137
|
-
const
|
135
|
+
const input = {
|
136
|
+
id: "test-id",
|
137
|
+
roomId: "test-room",
|
138
138
|
data: "test data",
|
139
|
-
|
140
|
-
});
|
139
|
+
};
|
141
140
|
|
142
|
-
|
143
|
-
expect(result?.
|
144
|
-
expect(result?.embedding).to.be.null;
|
141
|
+
const result = await meilisearchAdapter.createMemory(input);
|
142
|
+
expect(result?.embedding).to.be.undefined;
|
145
143
|
});
|
146
144
|
|
147
145
|
it("should search memories", async () => {
|
@@ -161,27 +159,34 @@ describe("MeilisearchAdapter", () => {
|
|
161
159
|
});
|
162
160
|
|
163
161
|
it("should handle memory retrieval by ID", async () => {
|
162
|
+
const testDate = new Date("2025-02-11T08:24:38.251Z");
|
163
|
+
|
164
|
+
const mockData = {
|
165
|
+
id: "test-id",
|
166
|
+
roomId: "test-room",
|
167
|
+
data: "test data",
|
168
|
+
createdAt: testDate.toISOString(),
|
169
|
+
embedding: undefined,
|
170
|
+
};
|
171
|
+
|
164
172
|
global.fetch = async (input: RequestInfo | URL) => {
|
165
173
|
const url = input.toString();
|
166
|
-
if (url.includes(`/indexes
|
167
|
-
return new Response(
|
168
|
-
JSON.stringify({
|
169
|
-
...testMemory,
|
170
|
-
createdAt: testMemory.createdAt.toISOString(),
|
171
|
-
})
|
172
|
-
);
|
174
|
+
if (url.includes(`/indexes/test-room/documents/test-id`)) {
|
175
|
+
return new Response(JSON.stringify(mockData));
|
173
176
|
}
|
174
177
|
return new Response(JSON.stringify({}));
|
175
178
|
};
|
176
179
|
|
177
180
|
const result = await meilisearchAdapter.getMemoryById(
|
178
181
|
"test-id",
|
179
|
-
|
182
|
+
"test-room"
|
180
183
|
);
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
expect(result).to.
|
184
|
+
|
185
|
+
expect(result?.id).to.equal(mockData.id);
|
186
|
+
expect(result?.roomId).to.equal(mockData.roomId);
|
187
|
+
expect(result?.data).to.equal(mockData.data);
|
188
|
+
expect(result?.createdAt).to.equal(mockData.createdAt); // Compare directement les strings
|
189
|
+
expect(result?.embedding).to.be.undefined;
|
185
190
|
});
|
186
191
|
|
187
192
|
it("should handle non-existent memory", async () => {
|
@@ -276,8 +281,7 @@ describe("MeilisearchAdapter", () => {
|
|
276
281
|
return new Response(JSON.stringify({}));
|
277
282
|
};
|
278
283
|
|
279
|
-
await expect(meilisearchAdapter.
|
280
|
-
.throw;
|
284
|
+
await expect(meilisearchAdapter.init(TEST_ROOM_ID)).to.not.throw;
|
281
285
|
});
|
282
286
|
});
|
283
287
|
});
|
@@ -201,14 +201,14 @@ describe("Memory", () => {
|
|
201
201
|
describe("Edge Cases", () => {
|
202
202
|
it("should handle undefined embedding", async () => {
|
203
203
|
const input = {
|
204
|
-
|
205
|
-
query: "test query",
|
204
|
+
id: "test-id",
|
206
205
|
roomId: "test-room",
|
206
|
+
data: "test data",
|
207
207
|
embedding: undefined,
|
208
208
|
};
|
209
209
|
|
210
210
|
const result = await memory.createMemory(input);
|
211
|
-
expect(result?.embedding).to.be.
|
211
|
+
expect(result?.embedding).to.be.undefined;
|
212
212
|
});
|
213
213
|
|
214
214
|
it("should handle empty query results", async () => {
|
package/tsconfig.json
CHANGED
@@ -7,7 +7,9 @@
|
|
7
7
|
"strict": true,
|
8
8
|
"skipLibCheck": true,
|
9
9
|
"baseUrl": ".",
|
10
|
-
"outDir": "./dist"
|
10
|
+
"outDir": "./dist",
|
11
|
+
"types": ["node", "mocha"]
|
11
12
|
},
|
12
|
-
"
|
13
|
+
"include": ["src/**/*", "graph/**/*", "types/**/*", "interfaces/**/*"],
|
14
|
+
"exclude": ["node_modules", "dist", "test", "examples"]
|
13
15
|
}
|
package/types/index.ts
CHANGED
@@ -94,10 +94,18 @@ export interface Node<T extends ZodSchema, I = any> {
|
|
94
94
|
condition?: (context: GraphContext<T>) => boolean;
|
95
95
|
/** Array of next node names */
|
96
96
|
next?: string[] | ((context: GraphContext<T>) => string[]);
|
97
|
-
/** Array of event names */
|
97
|
+
/** Array of event names that trigger this node */
|
98
98
|
events?: string[];
|
99
|
-
/** Wait for event */
|
99
|
+
/** Wait for a single event before continuing */
|
100
100
|
waitForEvent?: boolean;
|
101
|
+
/** Wait for multiple events configuration */
|
102
|
+
waitForEvents?: WaitForEvents;
|
103
|
+
/** Event correlation configuration */
|
104
|
+
correlateEvents?: {
|
105
|
+
events: string[];
|
106
|
+
correlation: (events: GraphEvent<T>[]) => boolean;
|
107
|
+
timeout?: number;
|
108
|
+
};
|
101
109
|
/** Retry configuration */
|
102
110
|
retry?: {
|
103
111
|
/** Maximum number of retry attempts */
|
@@ -164,3 +172,16 @@ export type MeilisearchSettings = {
|
|
164
172
|
/** Array of sortable attributes */
|
165
173
|
sortableAttributes?: string[];
|
166
174
|
};
|
175
|
+
|
176
|
+
export interface GraphEvent<T extends ZodSchema> {
|
177
|
+
type: string;
|
178
|
+
payload?: any;
|
179
|
+
timestamp: number;
|
180
|
+
}
|
181
|
+
|
182
|
+
export interface WaitForEvents {
|
183
|
+
events: string[];
|
184
|
+
timeout?: number;
|
185
|
+
strategy: "all" | "any" | "race";
|
186
|
+
onSuccess?: <T extends ZodSchema>(context: GraphContext<T>) => Promise<void>;
|
187
|
+
}
|