memories-lite 0.9.0 → 0.9.2

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.
@@ -2,6 +2,7 @@
2
2
  import { MemoriesLite } from "../src";
3
3
  import { MemoryItem, SearchResult } from "../src/types";
4
4
  import dotenv from "dotenv";
5
+ import { createTestMemory } from "./init.mem";
5
6
 
6
7
  dotenv.config();
7
8
 
@@ -9,41 +10,11 @@ jest.setTimeout(30000); // Increase timeout to 30 seconds
9
10
 
10
11
  describe("Memory Class", () => {
11
12
  let memory: MemoriesLite;
12
- const userId =
13
- Math.random().toString(36).substring(2, 15) +
14
- Math.random().toString(36).substring(2, 15);
15
-
16
- const dimension = 768;
13
+ let userId: string;
17
14
 
18
15
  beforeEach(async () => {
19
- // Initialize with default configuration
20
- memory = new MemoriesLite({
21
- version: "v1.1",
22
- disableHistory: true,
23
- embedder: {
24
- provider: "openai",
25
- config: {
26
- dimension,
27
- apiKey: process.env.OPENAI_API_KEY || "",
28
- model: "text-embedding-3-small",
29
- },
30
- },
31
- vectorStore: {
32
- provider: "lite",
33
- config: {
34
- dimension,
35
- rootPath: ":memory:",
36
- },
37
- },
38
- llm: {
39
- provider: "openai",
40
- config: {
41
- apiKey: process.env.OPENAI_API_KEY || "",
42
- model: "gpt-4.1-mini",
43
- },
44
- },
45
- historyDbPath: ":memory:", // Use in-memory SQLite for tests
46
- });
16
+ // Initialize memory via helper
17
+ ({ memory, userId } = createTestMemory({}));
47
18
  // Reset all memories before each test
48
19
  await memory.reset(userId);
49
20
  });
@@ -101,7 +72,7 @@ describe("Memory Class", () => {
101
72
  expect(typeof result.memory).toBe("string");
102
73
  });
103
74
 
104
- it("should update a memory", async () => {
75
+ it("should directly update a memory content", async () => {
105
76
  // First add a memory
106
77
  const addResult = (await memory.capture(
107
78
  "I love speaking foreign languages especially Spanish",
@@ -127,23 +98,19 @@ describe("Memory Class", () => {
127
98
  it("should update a memory", async () => {
128
99
  // First add a memory
129
100
  const init = await memory.capture("I love to drink red wine", userId, {});
130
- // expect(init.results?.[0]?.id).toBeDefined();
131
- const initId = init.results?.[0]?.id;
101
+ expect(init.results?.[0]?.id).toBeDefined();
132
102
  const addResult = (await memory.capture("I love to drink red wine with friends", userId, {})) as SearchResult;
133
103
  expect(addResult.results?.[0]?.id).toBeDefined();
134
104
 
135
- const memoryId = addResult.results[0].id;
136
- expect(memoryId).toBe(initId);
137
- // Delete the memory
138
- await memory.delete(memoryId, userId);
105
+ const updated = (await memory.get(addResult.results[0].id, userId))!;
139
106
 
140
- // Try to get the deleted memory - should throw or return null
141
- const result = await memory.get(memoryId, userId);
142
- expect(result).toBeNull();
107
+ expect(updated).toBeDefined();
108
+ expect(updated.id).toBe(init.results[0].id);
109
+ expect(updated.type).toBe(init.results[0].type);
143
110
  });
144
111
 
145
112
 
146
- it("should get all memories for distinct users", async () => {
113
+ it.only("should get all memories for distinct users", async () => {
147
114
  // Add a few memories
148
115
  await memory.capture("I love visiting new places in the winters", userId, {});
149
116
  await memory.capture("I like to rule the world", userId, {});
@@ -350,42 +317,17 @@ describe("Memory Class", () => {
350
317
 
351
318
  describe("Memory with Custom Configuration", () => {
352
319
  let customMemory: MemoriesLite;
353
-
354
- beforeEach(() => {
355
- customMemory = new MemoriesLite({
356
- version: "v1.1",
357
- disableHistory: true,
358
- embedder: {
359
- provider: "openai",
360
- config: {
361
- dimension,
362
- apiKey: process.env.OPENAI_API_KEY || "",
363
- model: "text-embedding-3-small",
364
- },
365
- },
366
- vectorStore: {
367
- provider: "lite",
368
- config: {
369
- dimension,
370
- rootPath: ":memory:",
371
- },
372
- },
373
- llm: {
374
- provider: "openai",
375
- config: {
376
- apiKey: process.env.OPENAI_API_KEY || "",
377
- model: "gpt-4.1-mini",
378
- },
379
- },
380
- historyDbPath: ":memory:", // Use in-memory SQLite for tests
381
- });
320
+ beforeEach(async () => {
321
+ // Initialize customMemory via helper
322
+ ({ memory: customMemory, userId } = createTestMemory({}));
323
+ // Reset before custom tests
324
+ await customMemory.reset(userId);
382
325
  });
383
326
 
384
327
  afterEach(async () => {
385
328
  await customMemory.reset(userId);
386
329
  });
387
330
 
388
-
389
331
  it("should perform semantic search with custom embeddings", async () => {
390
332
  // Add test memories
391
333
  await customMemory.capture("The weather in London is rainy today", userId, {});
@@ -0,0 +1,150 @@
1
+ /// <reference types="jest" />
2
+ import { MemoriesLite } from "../src";
3
+ import { MemoryItem, SearchResult } from "../src/types";
4
+ import dotenv from "dotenv";
5
+ import { createTestMemory } from "./init.mem";
6
+
7
+ dotenv.config();
8
+
9
+ jest.setTimeout(30000); // Increase timeout to 30 seconds
10
+
11
+ describe("Memory Updates - Type Preservation", () => {
12
+ let memory: MemoriesLite;
13
+ let userId: string;
14
+
15
+ beforeEach(async () => {
16
+ // Initialize memory via helper
17
+ ({ memory, userId } = createTestMemory({}));
18
+ // Reset all memories before each test
19
+ await memory.reset(userId);
20
+ });
21
+
22
+ afterEach(async () => {
23
+ // Clean up after each test
24
+ await memory.reset(userId);
25
+ });
26
+
27
+ describe("Memory Type Preservation During Updates", () => {
28
+ it("should preserve memory type when updating content", async () => {
29
+ // Add a factual memory
30
+ const factResult = (await memory.capture(
31
+ "I am allergic to peanuts",
32
+ userId,
33
+ {},
34
+ )) as SearchResult;
35
+
36
+ expect(factResult.results.length).toBeGreaterThan(0);
37
+ expect(factResult.results[0]?.type).toBe("factual");
38
+
39
+ const factId = factResult.results[0].id;
40
+
41
+ // Update the factual memory
42
+ await memory.update(factId, "I am severely allergic to peanuts and tree nuts", userId);
43
+
44
+ // Verify type preservation
45
+ const updatedFact = await memory.get(factId, userId);
46
+ expect(updatedFact).not.toBeNull();
47
+ expect(updatedFact?.type).toBe("factual");
48
+ expect(updatedFact?.memory).toBe("I am severely allergic to peanuts and tree nuts");
49
+ });
50
+
51
+ it("should preserve assistant_preference type when updating", async () => {
52
+ // Add a preference memory
53
+ const prefResult = (await memory.capture(
54
+ "please respond to me in French",
55
+ userId,
56
+ {},
57
+ )) as SearchResult;
58
+
59
+ expect(prefResult.results.length).toBeGreaterThan(0);
60
+ expect(prefResult.results[0]?.type).toBe("assistant_preference");
61
+
62
+ const prefId = prefResult.results[0].id;
63
+
64
+ // Update the preference memory
65
+ await memory.update(prefId, "please respond to me in Spanish", userId);
66
+
67
+ // Verify type preservation
68
+ const updatedPref = await memory.get(prefId, userId);
69
+ expect(updatedPref).not.toBeNull();
70
+ expect(updatedPref?.type).toBe("assistant_preference");
71
+ expect(updatedPref?.memory).toBe("please respond to me in Spanish");
72
+ });
73
+
74
+ it("should preserve episodic memory type when updating", async () => {
75
+ // Add an episodic memory
76
+ const episodicResult = (await memory.capture(
77
+ "I have a doctor's appointment tomorrow at 2pm",
78
+ userId,
79
+ {},
80
+ )) as SearchResult;
81
+
82
+ expect(episodicResult.results.length).toBeGreaterThan(0);
83
+ expect(episodicResult.results[0]?.type).toBe("episodic");
84
+
85
+ const episodicId = episodicResult.results[0].id;
86
+
87
+ // Update the episodic memory
88
+ await memory.update(episodicId, "My doctor's appointment was rescheduled to Friday at 3pm", userId);
89
+
90
+ // Verify type preservation
91
+ const updatedEpisodic = await memory.get(episodicId, userId);
92
+ expect(updatedEpisodic).not.toBeNull();
93
+ expect(updatedEpisodic?.type).toBe("episodic");
94
+ expect(updatedEpisodic?.memory).toBe("My doctor's appointment was rescheduled to Friday at 3pm");
95
+ });
96
+
97
+ it.skip("should preserve memory metadata when updating content", async () => {
98
+ // Mock the vector store to verify metadata is preserved
99
+ const mockVectorStore = {
100
+ insert: jest.fn().mockResolvedValue(undefined),
101
+ search: jest.fn().mockResolvedValue([{
102
+ id: 'test-id',
103
+ score: 0.9,
104
+ metadata: {
105
+ type: 'factual',
106
+ userId,
107
+ timestamp: Date.now(),
108
+ custom: 'test-value'
109
+ },
110
+ values: []
111
+ }]),
112
+ get: jest.fn().mockResolvedValue({
113
+ id: 'test-id',
114
+ metadata: {
115
+ type: 'factual',
116
+ userId,
117
+ timestamp: Date.now(),
118
+ custom: 'test-value'
119
+ },
120
+ values: []
121
+ }),
122
+ update: jest.fn().mockResolvedValue(undefined),
123
+ list: jest.fn(),
124
+ delete: jest.fn(),
125
+ deleteCol: jest.fn(),
126
+ ensureCollection: jest.fn(),
127
+ };
128
+
129
+ // Mock the getVectorStore method to return our mock
130
+ jest.spyOn(memory, 'getVectorStore').mockResolvedValue(mockVectorStore as any);
131
+
132
+ // Perform an update
133
+ await memory.update('test-id', 'Updated content', userId);
134
+
135
+ // Verify mockVectorStore.update was called with metadata preserved
136
+ expect(mockVectorStore.update).toHaveBeenCalled();
137
+ const updateArgs = mockVectorStore.update.mock.calls[0];
138
+
139
+ // Verify the ID and content are correct
140
+ expect(updateArgs[0]).toBe('test-id'); // ID
141
+
142
+ // Verify that metadata is passed and preserved in the update call
143
+ const metadataArg = updateArgs[2];
144
+ expect(metadataArg).toBeDefined();
145
+ expect(metadataArg.type).toBe('factual');
146
+ expect(metadataArg.userId).toBe(userId);
147
+ expect(metadataArg.custom).toBe('test-value');
148
+ });
149
+ });
150
+ });
@@ -0,0 +1,235 @@
1
+ /// <reference types="jest" />
2
+ import { MemoriesLite } from "../src";
3
+ import { SearchResult } from "../src/types";
4
+ import dotenv from "dotenv";
5
+ import { createTestMemory } from "./init.mem";
6
+
7
+ dotenv.config();
8
+
9
+ jest.setTimeout(30000); // Increase timeout to 30 seconds
10
+
11
+ describe("MemoriesLite - User Separation Tests", () => {
12
+ let memory: MemoriesLite;
13
+ let userAlice: string;
14
+ let userBob: string;
15
+
16
+
17
+ beforeEach(async () => {
18
+ // Initialize a single memory instance that will be shared by both users
19
+ ({ memory } = createTestMemory({rootPath: process.cwd(), secure: true}));
20
+
21
+ // Create a second userId
22
+ userAlice = `alice-1`;
23
+ userBob = `bob-2`;
24
+
25
+ // Reset all memories before each test
26
+ await memory.reset(userAlice);
27
+ await memory.reset(userBob);
28
+ });
29
+
30
+ afterEach(async () => {
31
+ // Clean up after each test
32
+ await memory.reset(userAlice);
33
+ await memory.reset(userBob);
34
+ });
35
+
36
+ describe("Basic User Separation", () => {
37
+ it("should keep memories separate between users", async () => {
38
+ // Add a memory for user 1
39
+ const memory1 = "My name is Alice and I love hiking";
40
+ const result1 = (await memory.capture(memory1, userAlice, {})) as SearchResult;
41
+ expect(result1.results.length).toBeGreaterThanOrEqual(1);
42
+
43
+ // Get all memories for user 1
44
+ const allbob = (await memory.getAll(userBob, {})) as SearchResult;
45
+ expect(allbob.results.length).toBe(0);
46
+
47
+ // Add a different memory for user 2
48
+ const memory2 = "My name is Bob and I enjoy swimming";
49
+ const result2 = (await memory.capture(memory2, userBob, {})) as SearchResult;
50
+ expect(result2.results.length).toBeGreaterThanOrEqual(1);
51
+
52
+ // Get all memories for user 1
53
+ const allMemories1 = (await memory.getAll(userAlice, {})) as SearchResult;
54
+ expect(allMemories1.results.length).toBeGreaterThanOrEqual(1);;
55
+
56
+ // Get all memories for user 2
57
+ const allMemories2 = (await memory.getAll(userBob, {})) as SearchResult;
58
+ expect(allMemories2.results.length).toBeGreaterThanOrEqual(1);
59
+
60
+ // Verify user 1 can only see their own memories
61
+ const containsAlice = allMemories1.results.some(item =>
62
+ item.memory.includes("Alice") || item.memory.includes("hiking"));
63
+ const containsBob = allMemories1.results.some(item =>
64
+ item.memory.includes("Bob") || item.memory.includes("swimming"));
65
+
66
+ expect(containsAlice).toBe(true);
67
+ expect(containsBob).toBe(false);
68
+
69
+ // Verify user 2 can only see their own memories
70
+ const user2ContainsAlice = allMemories2.results.some(item =>
71
+ item.memory.includes("Alice") || item.memory.includes("hiking"));
72
+ const user2ContainsBob = allMemories2.results.some(item =>
73
+ item.memory.includes("Bob") || item.memory.includes("swimming"));
74
+
75
+ expect(user2ContainsAlice).toBe(false);
76
+ expect(user2ContainsBob).toBe(true);
77
+ });
78
+
79
+ it("should not allow cross-user memory access", async () => {
80
+ // Add a memory for user 1
81
+ const result1 = (await memory.capture(
82
+ "I prefer to be called Alex instead of Alexander",
83
+ userAlice,
84
+ {},
85
+ )) as SearchResult;
86
+
87
+ expect(result1.results.length).toBe(1);
88
+ const memoryId = result1.results[0].id;
89
+
90
+ // User 1 can access their memory
91
+ const memoryForUser1 = await memory.get(memoryId, userAlice);
92
+ expect(memoryForUser1).not.toBeNull();
93
+
94
+ // User 2 cannot access user 1's memory
95
+ const memoryForUser2 = await memory.get(memoryId, userBob);
96
+ expect(memoryForUser2).toBeNull();
97
+
98
+ const allMemories2 = (await memory.getAll(userBob, {})) as SearchResult;
99
+ expect(allMemories2.results.length).toBe(0);
100
+
101
+ });
102
+ });
103
+
104
+ describe("Memory Operations Across Users", () => {
105
+ it("should keep retrieve results separate between users", async () => {
106
+ // Add similar memories for both users
107
+ await memory.capture("I live in Paris and love French cuisine", userAlice, {});
108
+ await memory.capture("I live in Lyon and enjoy French wine", userBob, {});
109
+
110
+ // Search for "France" for both users
111
+ const results1 = (await memory.retrieve("Where do you live?", userAlice, {})) as SearchResult;
112
+ const results2 = (await memory.retrieve("France et Lyon", userBob, {})) as SearchResult;
113
+
114
+ // Verify that user 1 only sees their Paris memory
115
+ const containsParis = results1.results.some(item => item.memory.includes("Paris"));
116
+ const containsLyon = results1.results.some(item => item.memory.includes("Lyon"));
117
+
118
+ expect(containsParis).toBe(true);
119
+ expect(containsLyon).toBe(false);
120
+
121
+ // Verify that user 2 only sees their Lyon memory
122
+ const user2ContainsParis = results2.results.some(item => item.memory.includes("Paris"));
123
+ const user2ContainsLyon = results2.results.some(item => item.memory.includes("Lyon"));
124
+
125
+ expect(user2ContainsParis).toBe(false);
126
+ expect(user2ContainsLyon).toBe(true);
127
+ });
128
+
129
+ it("should maintain user separation during updates", async () => {
130
+ // Add identical memories for both users
131
+ const result1 = (await memory.capture("My favorite color is blue", userAlice, {})) as SearchResult;
132
+ const result2 = (await memory.capture("My favorite color is blue", userBob, {})) as SearchResult;
133
+
134
+ const memoryId1 = result1.results[0].id;
135
+ const memoryId2 = result2.results[0].id;
136
+
137
+ // Update user 1's memory
138
+ await memory.update(memoryId1, "My favorite color is green", userAlice);
139
+
140
+ // Verify user 1's memory was updated
141
+ const updatedMemory1 = await memory.get(memoryId1, userAlice);
142
+ expect(updatedMemory1?.memory).toBe("My favorite color is green");
143
+
144
+ // Verify user 2's memory was NOT updated
145
+ const memory2 = await memory.get(memoryId2, userBob);
146
+ expect(memory2?.memory).toContain("blue");
147
+ });
148
+
149
+ it("should maintain user separation during deletes", async () => {
150
+ // Add identical memories for both users
151
+ const result1 = (await memory.capture("I enjoy playing tennis", userAlice, {})) as SearchResult;
152
+ const result2 = (await memory.capture("I enjoy playing tennis", userBob, {})) as SearchResult;
153
+
154
+ const memoryId1 = result1.results[0].id;
155
+ const memoryId2 = result2.results[0].id;
156
+
157
+ // Delete user 1's memory
158
+ await memory.delete(memoryId1, userAlice);
159
+
160
+ // Verify user 1's memory was deleted
161
+ const memory1 = await memory.get(memoryId1, userAlice);
162
+ expect(memory1).toBeNull();
163
+
164
+ // Verify user 2's memory still exists
165
+ const memory2 = await memory.get(memoryId2, userBob);
166
+ expect(memory2).not.toBeNull();
167
+ expect(memory2?.memory).toBe("I enjoy playing tennis");
168
+ });
169
+
170
+ it("should reset only the targeted user's memories", async () => {
171
+ // Add memories for both users
172
+ await memory.capture("I'm learning to play piano", userAlice, {});
173
+ await memory.capture("I'm taking guitar lessons", userBob, {});
174
+
175
+ // Reset only user1's memories
176
+ await memory.reset(userAlice);
177
+
178
+ // Verify user1 has no memories
179
+ const memories1 = await memory.getAll(userAlice, {});
180
+ expect(memories1.results.length).toBe(0);
181
+
182
+ // Verify user2 still has memories
183
+ const memories2 = await memory.getAll(userBob, {});
184
+ expect(memories2.results.length).toBeGreaterThan(0);
185
+ });
186
+ });
187
+
188
+ describe("Memory Type Preservation Across Users", () => {
189
+ it("should preserve memory types distinctly for each user", async () => {
190
+ // Add memories with specific types for different users
191
+ const message1 = [
192
+ {role: "user", content: "Je préfère que tu me tutoies"},
193
+ {role: "assistant", content: "D'accord, je te tutoierai à partir de maintenant."}
194
+ ];
195
+
196
+ const message2 = [
197
+ {role: "user", content: "Je préfère que vous me vouvoyiez"},
198
+ {role: "assistant", content: "D'accord, je vous vouvoierai à partir de maintenant."}
199
+ ];
200
+
201
+ // Capture for user 1 (tutoring preference)
202
+ const result1 = (await memory.capture(message1, userAlice, {})) as SearchResult;
203
+ expect(result1.results.length).toBeGreaterThan(0);
204
+ expect(result1.results[0]?.type).toBe("assistant_preference");
205
+
206
+ // Capture for user 2 (formal preference)
207
+ const result2 = (await memory.capture(message2, userBob, {})) as SearchResult;
208
+ expect(result2.results.length).toBeGreaterThan(0);
209
+ expect(result2.results[0]?.type).toBe("assistant_preference");
210
+
211
+ // Retrieve preferences for both users
212
+ const retrieve1 = await memory.retrieve("How should I address the user?", userAlice, {});
213
+ const retrieve2 = await memory.retrieve("How should I address the user?", userBob, {});
214
+
215
+ // Verify the correct preferences are returned for each user
216
+ const user1HasTutoring = retrieve1.results.some(item =>
217
+ item.memory.toLowerCase().includes("tutoie") ||
218
+ item.memory.toLowerCase().includes("tutoierai"));
219
+
220
+ const user2HasFormal = retrieve2.results.some(item =>
221
+ item.memory.toLowerCase().includes("vouvoyiez") ||
222
+ item.memory.toLowerCase().includes("vouvoierai"));
223
+
224
+ expect(user1HasTutoring).toBe(true);
225
+ expect(user2HasFormal).toBe(true);
226
+
227
+ // Cross-check: User 1 should not have user 2's preference
228
+ const user1HasFormal = retrieve1.results.some(item =>
229
+ item.memory.toLowerCase().includes("vouvoyiez") ||
230
+ item.memory.toLowerCase().includes("vouvoierai"));
231
+
232
+ expect(user1HasFormal).toBe(false);
233
+ });
234
+ });
235
+ });