memories-lite 0.9.1 → 0.9.3
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/README.md +127 -152
- package/dist/memory/index.js +9 -2
- package/dist/prompts/index.js +3 -3
- package/dist/vectorstores/lite.d.ts +10 -0
- package/dist/vectorstores/lite.js +46 -15
- package/package.json +3 -1
- package/src/memory/index.ts +9 -3
- package/src/prompts/index.ts +3 -3
- package/src/vectorstores/lite.ts +52 -14
- package/tests/init.mem.ts +40 -0
- package/tests/memory.facts.test.ts +29 -75
- package/tests/memory.test.ts +16 -74
- package/tests/memory.update.test.ts +150 -0
- package/tests/memory.users.test.ts +235 -0
- package/memories-lite.db +0 -0
|
@@ -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
|
+
});
|
package/memories-lite.db
DELETED
|
Binary file
|