memories-lite 0.9.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 (101) hide show
  1. package/MEMORIES.md +39 -0
  2. package/README.md +221 -0
  3. package/TECHNICAL.md +135 -0
  4. package/dist/config/defaults.d.ts +2 -0
  5. package/dist/config/defaults.js +61 -0
  6. package/dist/config/manager.d.ts +4 -0
  7. package/dist/config/manager.js +121 -0
  8. package/dist/embeddings/base.d.ts +4 -0
  9. package/dist/embeddings/base.js +2 -0
  10. package/dist/embeddings/google.d.ts +10 -0
  11. package/dist/embeddings/google.js +28 -0
  12. package/dist/embeddings/openai.d.ts +10 -0
  13. package/dist/embeddings/openai.js +31 -0
  14. package/dist/graphs/configs.d.ts +14 -0
  15. package/dist/graphs/configs.js +19 -0
  16. package/dist/graphs/tools.d.ts +271 -0
  17. package/dist/graphs/tools.js +220 -0
  18. package/dist/graphs/utils.d.ts +9 -0
  19. package/dist/graphs/utils.js +105 -0
  20. package/dist/index.d.ts +14 -0
  21. package/dist/index.js +30 -0
  22. package/dist/llms/base.d.ts +16 -0
  23. package/dist/llms/base.js +2 -0
  24. package/dist/llms/google.d.ts +11 -0
  25. package/dist/llms/google.js +44 -0
  26. package/dist/llms/openai.d.ts +9 -0
  27. package/dist/llms/openai.js +73 -0
  28. package/dist/llms/openai_structured.d.ts +11 -0
  29. package/dist/llms/openai_structured.js +72 -0
  30. package/dist/memory/index.d.ts +42 -0
  31. package/dist/memory/index.js +499 -0
  32. package/dist/memory/memory.types.d.ts +23 -0
  33. package/dist/memory/memory.types.js +2 -0
  34. package/dist/prompts/index.d.ts +102 -0
  35. package/dist/prompts/index.js +233 -0
  36. package/dist/storage/DummyHistoryManager.d.ts +7 -0
  37. package/dist/storage/DummyHistoryManager.js +19 -0
  38. package/dist/storage/MemoryHistoryManager.d.ts +8 -0
  39. package/dist/storage/MemoryHistoryManager.js +36 -0
  40. package/dist/storage/base.d.ts +6 -0
  41. package/dist/storage/base.js +2 -0
  42. package/dist/storage/index.d.ts +3 -0
  43. package/dist/storage/index.js +19 -0
  44. package/dist/types/index.d.ts +1071 -0
  45. package/dist/types/index.js +100 -0
  46. package/dist/utils/bm25.d.ts +13 -0
  47. package/dist/utils/bm25.js +51 -0
  48. package/dist/utils/factory.d.ts +13 -0
  49. package/dist/utils/factory.js +49 -0
  50. package/dist/utils/logger.d.ts +7 -0
  51. package/dist/utils/logger.js +9 -0
  52. package/dist/utils/memory.d.ts +3 -0
  53. package/dist/utils/memory.js +44 -0
  54. package/dist/utils/telemetry.d.ts +11 -0
  55. package/dist/utils/telemetry.js +74 -0
  56. package/dist/utils/telemetry.types.d.ts +27 -0
  57. package/dist/utils/telemetry.types.js +2 -0
  58. package/dist/vectorstores/base.d.ts +11 -0
  59. package/dist/vectorstores/base.js +2 -0
  60. package/dist/vectorstores/lite.d.ts +40 -0
  61. package/dist/vectorstores/lite.js +319 -0
  62. package/dist/vectorstores/llm.d.ts +31 -0
  63. package/dist/vectorstores/llm.js +88 -0
  64. package/jest.config.js +22 -0
  65. package/memories-lite.db +0 -0
  66. package/package.json +38 -0
  67. package/src/config/defaults.ts +61 -0
  68. package/src/config/manager.ts +132 -0
  69. package/src/embeddings/base.ts +4 -0
  70. package/src/embeddings/google.ts +32 -0
  71. package/src/embeddings/openai.ts +33 -0
  72. package/src/graphs/configs.ts +30 -0
  73. package/src/graphs/tools.ts +267 -0
  74. package/src/graphs/utils.ts +114 -0
  75. package/src/index.ts +14 -0
  76. package/src/llms/base.ts +20 -0
  77. package/src/llms/google.ts +56 -0
  78. package/src/llms/openai.ts +85 -0
  79. package/src/llms/openai_structured.ts +82 -0
  80. package/src/memory/index.ts +723 -0
  81. package/src/memory/memory.types.ts +27 -0
  82. package/src/prompts/index.ts +268 -0
  83. package/src/storage/DummyHistoryManager.ts +27 -0
  84. package/src/storage/MemoryHistoryManager.ts +58 -0
  85. package/src/storage/base.ts +14 -0
  86. package/src/storage/index.ts +3 -0
  87. package/src/types/index.ts +243 -0
  88. package/src/utils/bm25.ts +64 -0
  89. package/src/utils/factory.ts +59 -0
  90. package/src/utils/logger.ts +13 -0
  91. package/src/utils/memory.ts +48 -0
  92. package/src/utils/telemetry.ts +98 -0
  93. package/src/utils/telemetry.types.ts +34 -0
  94. package/src/vectorstores/base.ts +27 -0
  95. package/src/vectorstores/lite.ts +402 -0
  96. package/src/vectorstores/llm.ts +126 -0
  97. package/tests/lite.spec.ts +158 -0
  98. package/tests/memory.facts.test.ts +211 -0
  99. package/tests/memory.test.ts +406 -0
  100. package/tsconfig.json +16 -0
  101. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,211 @@
1
+ /// <reference types="jest" />
2
+ import { MemoriesLite } from "../src";
3
+ import { MemoryItem, SearchResult } from "../src/types";
4
+ import dotenv from "dotenv";
5
+
6
+ dotenv.config();
7
+
8
+ jest.setTimeout(30000); // Increase timeout to 30 seconds
9
+
10
+ describe("Memory Class facts regression tests", () => {
11
+ 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;
17
+
18
+ beforeEach(async () => {
19
+ // Initialize with default configuration
20
+ memory = new MemoriesLite({
21
+ version: "v1.1",
22
+ disableHistory: true,
23
+ customPrompt: "L'utilisateur travail pour une régie immobilière!",
24
+ embedder: {
25
+ provider: "openai",
26
+ config: {
27
+ dimension,
28
+ apiKey: process.env.OPENAI_API_KEY || "",
29
+ model: "text-embedding-3-small",
30
+ },
31
+ },
32
+ vectorStore: {
33
+ provider: "lite",
34
+ config: {
35
+ dimension,
36
+ rootPath: ":memory:",
37
+ },
38
+ },
39
+ llm: {
40
+ provider: "openai",
41
+ config: {
42
+ apiKey: process.env.OPENAI_API_KEY || "",
43
+ model: "gpt-4.1-mini",
44
+ },
45
+ },
46
+ historyDbPath: ":memory:", // Use in-memory SQLite for tests
47
+ });
48
+ // Reset all memories before each test
49
+ await memory.reset(userId);
50
+ });
51
+
52
+ afterEach(async () => {
53
+ // Clean up after each test
54
+ await memory.reset(userId);
55
+ });
56
+
57
+ describe("Edge cases for Facts", () => {
58
+
59
+ it("should not extract personal information as facts from business queries", async () => {
60
+ // type?: "factual" | "episodic" | "semantic"|"procedural" | "assistant_preference";
61
+ // Capture a query that contains a name but is asking for contact information
62
+ const result = (await memory.capture(
63
+ "je cherche le téléphone de mon client Alphonse MAGLOIRE",
64
+ userId,
65
+ {},
66
+ )) as SearchResult;
67
+
68
+ // Verify no memory was created (business query)
69
+ expect(result).toBeDefined();
70
+ expect(result.results).toBeDefined();
71
+ expect(Array.isArray(result.results)).toBe(true);
72
+ expect(result.results.length).toBe(1);
73
+ const type = result.results[0]?.type;
74
+ expect(["procedural","episodic"].includes(type)).toBe(true);
75
+ // Now search for memories that might contain "Alphonse MAGLOIRE"
76
+ // const searchResult = (await memory.retrieve(
77
+ // "Qui est Alphonse MAGLOIRE?",
78
+ // userId,
79
+ // {},
80
+ // )) as SearchResult;
81
+
82
+ // // Verify no personal fact like "Je m'appelle Alphonse MAGLOIRE" was created
83
+ // expect(searchResult).toBeDefined();
84
+ // expect(searchResult.results).toBeDefined();
85
+ // expect(Array.isArray(searchResult.results)).toBe(true);
86
+ // expect(searchResult.results.length).toBe(0);
87
+
88
+ // // Ensure no memory contains the name as a personal fact
89
+ // const allMemories = await memory.getAll(userId, {});
90
+ // const personalFacts = allMemories.results.filter(mem =>
91
+ // mem.memory.toLowerCase().includes("Alphonse MAGLOIRE")
92
+ // );
93
+ // expect(personalFacts.length).toBe(0);
94
+ });
95
+
96
+ it("should add a single procedural memory", async () => {
97
+ const customFacts = "Je suis Olivier Poulain\nIT chez Immeuble SA";
98
+ const result = (await memory.capture([
99
+ {role:"user", content:"Qui suis-je ?"},
100
+ {role:"assitant", content:"Vous êtes Olivier Poulain, Chef de Projets au département IT & Gestion de projet, dans l'équipe IT de Immeuble SA. Ces informations proviennent de votre profil collaborateur enregistré dans le système interne de Immeuble SA"}],
101
+ userId,
102
+ {customFacts},
103
+ )) as SearchResult;
104
+ expect(result).toBeDefined();
105
+ expect(result.results).toBeDefined();
106
+ expect(result.results.length).toBe(1);
107
+ expect(result.results[0]?.type).toBe("factual");
108
+ });
109
+ it("should add a single episodic memory", async () => {
110
+ const result = (await memory.capture([
111
+ {role:"user", content:"je veux trouver un bon film pour ce soir au cinéma avec ma copine,"}],
112
+ userId,
113
+ {},
114
+ )) as SearchResult;
115
+
116
+ expect(result).toBeDefined();
117
+ expect(result.results).toBeDefined();
118
+ expect(result.results.length).toBeGreaterThan(0);
119
+ expect(result.results[0]?.type).toBe("episodic");
120
+ });
121
+
122
+ it("should add assistant_preference memory", async () => {
123
+ const result = (await memory.capture(
124
+ "tu dois répondre de manière concise et précise",
125
+ userId,
126
+ {},
127
+ )) as SearchResult;
128
+
129
+ expect(result).toBeDefined();
130
+ expect(result.results).toBeDefined();
131
+ expect(Array.isArray(result.results)).toBe(true);
132
+ expect(result.results.length).toBeGreaterThan(0);
133
+ expect(result.results[0]?.id).toBeDefined();
134
+ expect(result.results[0]?.type).toBe("assistant_preference");
135
+ });
136
+
137
+ it("business facts are not factual (1)", async () => {
138
+ const result = (await memory.capture([
139
+ {role:"user", content:"Je sais que Alphonse MAGLOIRE a un logement de 4 pièces au 5ème étage",}],
140
+ userId,
141
+ {customFacts:"Je suis Olivier Poulain, Je m'occupe de la gérance locataire chez Immeuble SA"},
142
+ )) as SearchResult;
143
+ expect(result).toBeDefined();
144
+ expect(result.results).toBeDefined();
145
+ expect(Array.isArray(result.results)).toBe(true);
146
+ //
147
+ // result can also be empty
148
+ if(result.results.length > 0) {
149
+ expect(result.results.length).toBe(1);
150
+ expect(result.results[0]?.type).toBe("procedural");
151
+ }
152
+ });
153
+
154
+
155
+ it("business task are not factual (2)", async () => {
156
+ const result = (await memory.capture([
157
+ {role:"user", content:"Quelle est la procédure pour résilier un bail chez Pilet-Renaud SA ?"}],
158
+ userId,
159
+ {customFacts:"Je suis Olivier Poulain, Je m'occupe de la gérance locataire chez Immeuble SA"},
160
+ )) as SearchResult;
161
+
162
+
163
+ expect(result).toBeDefined();
164
+ expect(result.results).toBeDefined();
165
+ expect(Array.isArray(result.results)).toBe(true);
166
+ //
167
+ // result can also be empty
168
+ if(result.results.length > 0) {
169
+ expect(result.results.length).toBe(1);
170
+ expect(result.results[0]?.type).toBe("procedural");
171
+ }
172
+ });
173
+
174
+ it("should not add business queries (3)", async () => {
175
+ const result = (await memory.capture([
176
+ {role:"user", content:"Est-ce que Jean BURRI est à jour avec son loyer ?"}],
177
+ userId,
178
+ {customFacts:"Je suis Olivier Poulain, Je m'occupe de la gérance locataire chez Immeuble SA"},
179
+ )) as SearchResult;
180
+ expect(result).toBeDefined();
181
+ expect(result.results).toBeDefined();
182
+ expect(Array.isArray(result.results)).toBe(true);
183
+ expect(result.results.length).toBe(1);
184
+ expect(result.results[0]?.type).toBe("procedural");
185
+
186
+
187
+ });
188
+
189
+
190
+ it("should add and remove a single memory", async () => {
191
+ const result = (await memory.capture(
192
+ "je veux que tu répondes en italien",
193
+ userId,
194
+ {},
195
+ )) as SearchResult;
196
+ expect(result.results?.[0]?.id).toBeDefined();
197
+ const memoryId = result.results[0]?.id;
198
+ await memory.capture(
199
+ "je ne veux plus que tu répondes en italien",
200
+ userId,
201
+ {},
202
+ ) as SearchResult;
203
+
204
+ const id = await memory.get(memoryId, userId);
205
+ expect(id).toBeNull();
206
+
207
+ });
208
+
209
+
210
+ });
211
+ });
@@ -0,0 +1,406 @@
1
+ /// <reference types="jest" />
2
+ import { MemoriesLite } from "../src";
3
+ import { MemoryItem, SearchResult } from "../src/types";
4
+ import dotenv from "dotenv";
5
+
6
+ dotenv.config();
7
+
8
+ jest.setTimeout(30000); // Increase timeout to 30 seconds
9
+
10
+ describe("Memory Class", () => {
11
+ 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;
17
+
18
+ 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
+ });
47
+ // Reset all memories before each test
48
+ await memory.reset(userId);
49
+ });
50
+
51
+ afterEach(async () => {
52
+ // Clean up after each test
53
+ await memory.reset(userId);
54
+ });
55
+
56
+ describe("Basic Memory Operations", () => {
57
+ it("should add a single memory", async () => {
58
+ const result = (await memory.capture(
59
+ "Bonjour, je m'appelle Olivier et je suis boulanger.",
60
+ userId,
61
+ {},
62
+ )) as SearchResult;
63
+
64
+ expect(result).toBeDefined();
65
+ expect(result.results).toBeDefined();
66
+ expect(Array.isArray(result.results)).toBe(true);
67
+ expect(result.results.length).toBeGreaterThan(0);
68
+ expect(result.results[0]?.id).toBeDefined();
69
+ });
70
+
71
+ it("should add multiple messages", async () => {
72
+ const messages = [
73
+ { role: "user", content: "What is your favorite city?" },
74
+ { role: "assistant", content: "I love Paris, it is my favorite city." },
75
+ { role: "user", content: "I prefer Geneva." },
76
+ ];
77
+
78
+ const result = (await memory.capture(messages, userId, {})) as SearchResult;
79
+
80
+ expect(result).toBeDefined();
81
+ expect(result.results).toBeDefined();
82
+ expect(Array.isArray(result.results)).toBe(true);
83
+ expect(result.results.length).toBeGreaterThan(0);
84
+ });
85
+
86
+ it("should get a single memory", async () => {
87
+ // First add a memory
88
+ const addResult = (await memory.capture(
89
+ "I am a big advocate of using AI to make the world a better place",
90
+ userId,
91
+ {},
92
+ )) as SearchResult;
93
+
94
+ expect(addResult.results?.[0]?.id).toBeDefined();
95
+ const memoryId = addResult.results[0].id;
96
+ const result = (await memory.get(memoryId, userId)) as MemoryItem;
97
+
98
+ expect(result).toBeDefined();
99
+ expect(result.id).toBe(memoryId);
100
+ expect(result.memory).toBeDefined();
101
+ expect(typeof result.memory).toBe("string");
102
+ });
103
+
104
+ it("should update a memory", async () => {
105
+ // First add a memory
106
+ const addResult = (await memory.capture(
107
+ "I love speaking foreign languages especially Spanish",
108
+ userId,
109
+ {},
110
+ )) as SearchResult;
111
+ expect(addResult.results?.[0]?.id).toBeDefined();
112
+
113
+
114
+ const memoryId = addResult.results[0].id;
115
+ const updatedContent = "Updated content";
116
+ const result = await memory.update(memoryId, updatedContent, userId);
117
+
118
+ expect(result).toBeDefined();
119
+ expect(result.message).toBe("Memory updated successfully!");
120
+
121
+ // Verify the update by getting the memory
122
+ const updatedMemory = (await memory.get(memoryId, userId)) as MemoryItem;
123
+ expect(updatedMemory.memory).toBe(updatedContent);
124
+
125
+ });
126
+
127
+ it("should update a memory", async () => {
128
+ // First add a memory
129
+ 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;
132
+ const addResult = (await memory.capture("I love to drink red wine with friends", userId, {})) as SearchResult;
133
+ expect(addResult.results?.[0]?.id).toBeDefined();
134
+
135
+ const memoryId = addResult.results[0].id;
136
+ expect(memoryId).toBe(initId);
137
+ // Delete the memory
138
+ await memory.delete(memoryId, userId);
139
+
140
+ // Try to get the deleted memory - should throw or return null
141
+ const result = await memory.get(memoryId, userId);
142
+ expect(result).toBeNull();
143
+ });
144
+
145
+
146
+ it("should get all memories for distinct users", async () => {
147
+ // Add a few memories
148
+ await memory.capture("I love visiting new places in the winters", userId, {});
149
+ await memory.capture("I like to rule the world", userId, {});
150
+
151
+ const result = (await memory.getAll(userId, {})) as SearchResult;
152
+
153
+ expect(result).toBeDefined();
154
+ expect(Array.isArray(result.results)).toBe(true);
155
+ expect(result.results.length).toBeGreaterThanOrEqual(2);
156
+
157
+ const otherUser = (await memory.getAll(userId+'-other', {})) as SearchResult;
158
+
159
+ expect(otherUser).toBeDefined();
160
+ expect(Array.isArray(otherUser.results)).toBe(true);
161
+ expect(otherUser.results.length).toBeGreaterThanOrEqual(0);
162
+
163
+ });
164
+
165
+ it("should search memories", async () => {
166
+ // Add some test memories
167
+ await memory.capture("I love programming in Python", userId, {});
168
+ await memory.capture("JavaScript is my favorite language", userId, {});
169
+
170
+ const result = (await memory.retrieve(
171
+ "What programming languages do I know?",
172
+ userId,
173
+ {},
174
+ )) as SearchResult;
175
+
176
+ expect(result).toBeDefined();
177
+ expect(Array.isArray(result.results)).toBe(true);
178
+ expect(result.results.length).toBeGreaterThan(0);
179
+ });
180
+
181
+ it.skip("should get memory history", async () => {
182
+ // Add and update a memory to create history
183
+ const addResult = (await memory.capture(
184
+ "I like swimming in warm water",
185
+ userId,
186
+ {},
187
+ )) as SearchResult;
188
+
189
+ if (!addResult.results?.[0]?.id) {
190
+ throw new Error("Failed to create test memory");
191
+ }
192
+
193
+ const memoryId = addResult.results[0].id;
194
+ await memory.update(memoryId, "Updated content", userId);
195
+
196
+ const history = await memory.history(memoryId);
197
+
198
+ expect(history).toBeDefined();
199
+ expect(Array.isArray(history)).toBe(true);
200
+ expect(history.length).toBeGreaterThan(0);
201
+ });
202
+
203
+ it("should delete a memory", async () => {
204
+ // First add a memory
205
+ const addResult = (await memory.capture("I love to drink red wine with friends", userId, {})) as SearchResult;
206
+ expect(addResult.results?.[0]?.id).toBeDefined();
207
+
208
+ const memoryId = addResult.results[0].id;
209
+ // Delete the memory
210
+ await memory.delete(memoryId, userId);
211
+
212
+ // Try to get the deleted memory - should throw or return null
213
+ const result = await memory.get(memoryId, userId);
214
+ expect(result).toBeNull();
215
+ });
216
+
217
+ it("should remember specific user semantic", async () => {
218
+ // First add a memory
219
+ const result = (await memory.capture([
220
+ {role: "user", content: "est-ce que pol pot à été formé avec l'histoire de la Commune de 1871"},
221
+ {role: "assistant", content: "En bref : oui — pendant ses années d'études à Paris (1949-1953), Saloth Sâr, le futur Pol Pot, a découvert le marxisme dans les cercles d'étudiants khmers et français où la Commune de 1871 était un épisode-clé du patrimoine révolutionnaire. Cette référence l'a fortement marqué ; il affirma plus tard que la Commune avait échoué parce que « le prolétariat n'avait pas exercé la dictature sur la bourgeoisie » et jura de ne pas « répéter cette erreur ». Il s'est donc bien « formé » idéologiquement, au moins en partie, à travers l'histoire et les leçons qu'on tirait alors de la Commune"},
222
+ ],
223
+ userId,
224
+ {},
225
+ )) as SearchResult;
226
+ expect(result).toBeDefined();
227
+ expect(Array.isArray(result.results)).toBe(true);
228
+ expect(result.results.length).toBeGreaterThan(0);
229
+
230
+ });
231
+
232
+ it("should avoid specific user discussions", async () => {
233
+ // First add a memory
234
+ const result = (await memory.capture([
235
+ {role: "user", content: "est-ce que pol pot à été formé avec l'histoire de la Commune de 1871"},
236
+ {role: "assistant", content: "En bref : oui — pendant ses années d'études à Paris (1949-1953), Saloth Sâr, le futur Pol Pot, a découvert le marxisme dans les cercles d'étudiants khmers et français où la Commune de 1871 était un épisode-clé du patrimoine révolutionnaire. Cette référence l'a fortement marqué ; il affirma plus tard que la Commune avait échoué parce que « le prolétariat n'avait pas exercé la dictature sur la bourgeoisie » et jura de ne pas « répéter cette erreur ». Il s'est donc bien « formé » idéologiquement, au moins en partie, à travers l'histoire et les leçons qu'on tirait alors de la Commune"},
237
+ {role: "user", content: "cette information ne m'interesse pas"},
238
+ ],
239
+ userId,
240
+ {},
241
+ )) as SearchResult;
242
+ expect(result).toBeDefined();
243
+ expect(Array.isArray(result.results)).toBe(true);
244
+ expect(result.results.length).toBe(0);
245
+
246
+ });
247
+
248
+ it("should remember encoded user preferences", async () => {
249
+ // First add a memory
250
+ const addResult = (await memory.capture(
251
+ "lorsque j'utilise la commande spéciale 'en\"\"' dans mes questions tu dois traduire le texte",
252
+ userId,
253
+ {},
254
+ )) as SearchResult;
255
+
256
+ if (!addResult.results?.[0]?.id) {
257
+ throw new Error("Failed to create test memory");
258
+ }
259
+
260
+
261
+ const result = (await memory.retrieve(
262
+ "en\"\"quelle est l'âge du capitaine\"\"",
263
+ userId,
264
+ {},
265
+ )) as SearchResult;
266
+
267
+ expect(result).toBeDefined();
268
+ expect(Array.isArray(result.results)).toBe(true);
269
+ expect(result.results.length).toBeGreaterThan(0);
270
+ });
271
+
272
+
273
+
274
+ it("should include 'type' field when capturing and retrieving memory", async () => {
275
+ const captureResult = await memory.capture(
276
+ "User likes sushi.",
277
+ userId,
278
+ {},
279
+ );
280
+
281
+ expect(captureResult.results).toBeDefined();
282
+ expect(captureResult.results.length).toBeGreaterThan(0);
283
+ const memoryId = captureResult.results[0]?.id;
284
+ expect(memoryId).toBeDefined();
285
+
286
+ // Verify the type is present in the capture result (returned immediately)
287
+ // Note: The LLM decides the type, so we check for its existence, not a specific value unless mocked.
288
+ expect(captureResult.results[0]?.type).toBeDefined();
289
+ expect(typeof captureResult.results[0]?.type).toBe('string');
290
+
291
+ // Retrieve the memory to check persistence
292
+ const retrievedMemory = await memory.get(memoryId!, userId);
293
+ expect(retrievedMemory).not.toBeNull();
294
+ expect(retrievedMemory?.type).toBeDefined();
295
+ expect(typeof retrievedMemory?.type).toBe('string');
296
+ // Check payload directly via retrieve (which gets it from vector store)
297
+ const searchResult = await memory.retrieve("User preference on food", userId, {});
298
+ expect(searchResult.results).toBeDefined();
299
+ expect(searchResult.results.length).toBeGreaterThan(0);
300
+ const foundMemory = searchResult.results.find(m => m.id === memoryId);
301
+ expect(foundMemory).toBeDefined();
302
+ expect(foundMemory?.type).toBeDefined();
303
+ expect(typeof foundMemory?.type).toBe('string');
304
+ });
305
+
306
+ it("should ensure 'type' field is passed to vector store during capture (ADD)", async () => {
307
+ const mockVectorStore = {
308
+ insert: jest.fn().mockResolvedValue(undefined),
309
+ search: jest.fn().mockResolvedValue([]), // Mock search to avoid errors in update logic
310
+ get: jest.fn(),
311
+ list: jest.fn(),
312
+ update: jest.fn(),
313
+ delete: jest.fn(),
314
+ deleteCol: jest.fn(),
315
+ ensureCollection: jest.fn(),
316
+ };
317
+
318
+ // Mock the getVectorStore method to return our mock
319
+ jest.spyOn(memory, 'getVectorStore').mockResolvedValue(mockVectorStore as any);
320
+
321
+ // Mock LLM responses to force an ADD action with a specific type
322
+ const mockLlm = {
323
+ generateResponse: jest.fn()
324
+ .mockResolvedValueOnce({ // Fact extraction response
325
+ facts: [{ fact: "User likes cats", type: "PREFERENCE" }]
326
+ })
327
+ .mockResolvedValueOnce({ // Memory update response
328
+ memory: [{ event: "ADD", text: "User likes cats", type: "PREFERENCE" }]
329
+ })
330
+ };
331
+ // Replace the LLM instance with the mock
332
+ (memory as any).llm = mockLlm;
333
+
334
+
335
+ await memory.capture("User likes cats", userId, {});
336
+
337
+ // Verify mockVectorStore.insert was called
338
+ expect(mockVectorStore.insert).toHaveBeenCalled();
339
+
340
+ // Get the arguments passed to the insert call
341
+ const insertArgs = mockVectorStore.insert.mock.calls[0];
342
+ const metadataPayload = insertArgs[2][0]; // Metadata is the third argument, first item in the array
343
+
344
+ // Assert that the type field is present in the metadata payload
345
+ expect(metadataPayload).toBeDefined();
346
+ expect(metadataPayload.type).toBe('PREFERENCE');
347
+ });
348
+
349
+ });
350
+
351
+ describe("Memory with Custom Configuration", () => {
352
+ 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
+ });
382
+ });
383
+
384
+ afterEach(async () => {
385
+ await customMemory.reset(userId);
386
+ });
387
+
388
+
389
+ it("should perform semantic search with custom embeddings", async () => {
390
+ // Add test memories
391
+ await customMemory.capture("The weather in London is rainy today", userId, {});
392
+ await customMemory.capture("The temperature in Paris is 25 degrees", userId, {});
393
+
394
+ const result = (await customMemory.retrieve(
395
+ "What is the weather like?",
396
+ userId,
397
+ {},
398
+ )) as SearchResult;
399
+
400
+ expect(result).toBeDefined();
401
+ expect(Array.isArray(result.results)).toBe(true);
402
+ // Results should be ordered by relevance
403
+ expect(result.results.length).toBe(0);
404
+ });
405
+ });
406
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "declaration": true,
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true
13
+ },
14
+ "include": ["src/**/*", "jest.config.js"],
15
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
16
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/index.ts","./src/config/defaults.ts","./src/config/manager.ts","./src/embeddings/base.ts","./src/embeddings/google.ts","./src/embeddings/openai.ts","./src/graphs/configs.ts","./src/graphs/tools.ts","./src/graphs/utils.ts","./src/llms/base.ts","./src/llms/google.ts","./src/llms/openai.ts","./src/llms/openai_structured.ts","./src/memory/index.ts","./src/memory/memory.types.ts","./src/prompts/index.ts","./src/storage/DummyHistoryManager.ts","./src/storage/MemoryHistoryManager.ts","./src/storage/base.ts","./src/storage/index.ts","./src/types/index.ts","./src/utils/bm25.ts","./src/utils/factory.ts","./src/utils/logger.ts","./src/utils/memory.ts","./src/utils/telemetry.ts","./src/utils/telemetry.types.ts","./src/vectorstores/base.ts","./src/vectorstores/lite.ts","./src/vectorstores/llm.ts"],"version":"5.8.3"}