memories-lite 0.10.1 → 0.99.1
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/dist/config/defaults.js +11 -4
- package/dist/config/manager.js +2 -4
- package/dist/memory/index.d.ts +11 -0
- package/dist/memory/index.js +62 -124
- package/dist/memory/memory.types.d.ts +1 -2
- package/dist/prompts/index.d.ts +53 -17
- package/dist/prompts/index.js +63 -23
- package/dist/types/index.d.ts +28 -137
- package/dist/types/index.js +2 -8
- package/memories-lite-a42ac5108869b599bcbac21069f63fb47f07452fcc4b87e89b3c06a945612d0b.db +0 -0
- package/memories-lite-a9137698d8d3fdbf27efcdc8cd372084b52d484e8db866c5455bbb3f85299b54.db +0 -0
- package/package.json +1 -1
- package/src/config/defaults.ts +11 -4
- package/src/config/manager.ts +2 -4
- package/src/memory/index.ts +76 -158
- package/src/memory/memory.types.ts +1 -2
- package/src/prompts/index.ts +69 -26
- package/src/types/index.ts +4 -11
- package/tests/lite.spec.ts +44 -34
- package/tests/memory.discussion.test.ts +279 -0
- package/tests/memory.update.test.ts +5 -1
- package/tests/memory.facts.test.ts +0 -175
- package/tests/memory.todo.test.ts +0 -126
|
@@ -0,0 +1,279 @@
|
|
|
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(60000); // 60s pour les tests avec LLM
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tests pour le nouveau type de mémoire 'discussion'
|
|
13
|
+
* Permet de mémoriser une discussion avec synthèse (title + summary)
|
|
14
|
+
*/
|
|
15
|
+
describe("Memory Discussion - Synthèse de discussions", () => {
|
|
16
|
+
let memory: MemoriesLite;
|
|
17
|
+
let userId: string;
|
|
18
|
+
|
|
19
|
+
//
|
|
20
|
+
// Exemple de discussion à synthétiser
|
|
21
|
+
const discussionMessages = [
|
|
22
|
+
{
|
|
23
|
+
role: "user",
|
|
24
|
+
content: "Tu dois vérifier les autorisations (ou APA) de la FAO de Genève, et ensuite tu dois extraire toutes les adresses et chercher nos immeubles. Merci de me mettre les liens des FAO que tu jugeras utiles."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
role: "assistant",
|
|
28
|
+
content: `Voici un premier lot traité. Dites-moi si vous voulez que je continue sur plus d'avis.
|
|
29
|
+
|
|
30
|
+
Adresses extraites et recherche de nos immeubles
|
|
31
|
+
- Chemin Émile-Bressler 19, 1226 Thônex
|
|
32
|
+
- Immeubles PRSA à proximité (200 m) : aucun trouvé.
|
|
33
|
+
- FAO: Requête M/341685/1 — Démolition d'une villa
|
|
34
|
+
- Chemin du Port 9, 1246 Corsier
|
|
35
|
+
- À vérifier (souhaitez-vous que je cherche nos immeubles autour de cette adresse ?)
|
|
36
|
+
- FAO: Requête M/341714/1 — Démolition
|
|
37
|
+
- Boulevard de Saint‑Georges 46, 1205 Genève (Plainpalais)
|
|
38
|
+
- À vérifier (je peux lancer la recherche de nos immeubles).
|
|
39
|
+
- FAO: Autorisation DD 330818 — Transformation`
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
role: "user",
|
|
43
|
+
content: "oui merci pour le premier"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
role: "assistant",
|
|
47
|
+
content: `Parfait. Je poursuis avec ces trois adresses et vous reviens avec:
|
|
48
|
+
- adresses exactes confirmées
|
|
49
|
+
- présence/absence de nos immeubles à 200 m
|
|
50
|
+
- liens FAO utiles
|
|
51
|
+
|
|
52
|
+
Cibles:
|
|
53
|
+
- Chemin du Port 9, 1246 Corsier
|
|
54
|
+
- Boulevard de Saint‑Georges 46, 1205 Genève
|
|
55
|
+
- Chemin du Champ‑des‑Bois 8, 1255 Veyrier`
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
role: "user",
|
|
59
|
+
content: "oui"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
role: "assistant",
|
|
63
|
+
content: `Voici la suite.
|
|
64
|
+
|
|
65
|
+
Résultats par adresse (rayon 200 m)
|
|
66
|
+
- Chemin du Port 9, 1246 Corsier — aucun immeuble PRSA trouvé.
|
|
67
|
+
- Boulevard de Saint‑Georges 46, 1205 Genève — aucun immeuble PRSA trouvé.
|
|
68
|
+
- Chemin du Champ‑des‑Bois 8, 1255 Veyrier — aucun immeuble PRSA trouvé.
|
|
69
|
+
|
|
70
|
+
Souhaitez-vous que je:
|
|
71
|
+
- élargisse à 300–500 m,
|
|
72
|
+
- continue l'extraction sur d'autres avis FAO récents (démolitions/transformations/APA),
|
|
73
|
+
- ou cible des communes/quartiers spécifiques ?`
|
|
74
|
+
}
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
beforeEach(async () => {
|
|
78
|
+
({ memory, userId } = createTestMemory({}));
|
|
79
|
+
await memory.reset(userId);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
afterEach(async () => {
|
|
83
|
+
await memory.reset(userId);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("Capture de discussion", () => {
|
|
87
|
+
|
|
88
|
+
it("should capture a discussion and generate exactly ONE memory with title + summary", async () => {
|
|
89
|
+
//
|
|
90
|
+
// Capturer la discussion avec type 'discussion'
|
|
91
|
+
const result = await memory.capture(discussionMessages, userId, {
|
|
92
|
+
metadata: {
|
|
93
|
+
type: 'discussion',
|
|
94
|
+
discussionId: '7c3e43ac-7621-470d-a22e-82713c3a113e'
|
|
95
|
+
}
|
|
96
|
+
}) as SearchResult;
|
|
97
|
+
|
|
98
|
+
expect(result).toBeDefined();
|
|
99
|
+
expect(result.results).toBeDefined();
|
|
100
|
+
expect(Array.isArray(result.results)).toBe(true);
|
|
101
|
+
|
|
102
|
+
//
|
|
103
|
+
// ✅ Vérifier qu'UNE SEULE mémoire est créée par capture
|
|
104
|
+
expect(result.results.length).toBe(1);
|
|
105
|
+
|
|
106
|
+
//
|
|
107
|
+
// Vérifier que la mémoire contient title et type
|
|
108
|
+
const mem = result.results[0];
|
|
109
|
+
expect(mem.id).toBeDefined();
|
|
110
|
+
expect(mem.type).toBe('discussion');
|
|
111
|
+
expect(mem.memory).toBeDefined(); // summary
|
|
112
|
+
expect(mem.metadata?.title).toBeDefined();
|
|
113
|
+
|
|
114
|
+
console.log('--- Title:', mem.metadata?.title);
|
|
115
|
+
console.log('--- Summary:', mem.memory);
|
|
116
|
+
|
|
117
|
+
//
|
|
118
|
+
// ✅ Vérifier qu'il n'y a bien qu'une seule mémoire stockée
|
|
119
|
+
const allMemories = await memory.getAll(userId, { type: 'discussion' });
|
|
120
|
+
expect(allMemories.results.length).toBe(1);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should use custom capturePrompt when provided", async () => {
|
|
124
|
+
const customPrompt = `Tu es un expert en synthèse. Génère:
|
|
125
|
+
1. TITRE: Un titre technique en français (max 8 mots)
|
|
126
|
+
2. SUMMARY: Résumé technique des actions effectuées (max 50 mots)
|
|
127
|
+
|
|
128
|
+
Discussion:
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
const result = await memory.capture(discussionMessages, userId, {
|
|
132
|
+
capturePrompt: customPrompt,
|
|
133
|
+
metadata: {
|
|
134
|
+
type: 'discussion',
|
|
135
|
+
discussionId: 'custom-test-id'
|
|
136
|
+
}
|
|
137
|
+
}) as SearchResult;
|
|
138
|
+
|
|
139
|
+
expect(result).toBeDefined();
|
|
140
|
+
expect(result.results.length).toBeGreaterThan(0);
|
|
141
|
+
expect(result.results[0].type).toBe('discussion');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should retrieve discussion memories by semantic search with title in metadata", async () => {
|
|
145
|
+
//
|
|
146
|
+
// D'abord capturer la discussion
|
|
147
|
+
const captureResult = await memory.capture(discussionMessages, userId, {
|
|
148
|
+
metadata: {
|
|
149
|
+
type: 'discussion',
|
|
150
|
+
discussionId: 'search-test-id'
|
|
151
|
+
}
|
|
152
|
+
}) as SearchResult;
|
|
153
|
+
|
|
154
|
+
const originalTitle = captureResult.results[0].metadata?.title;
|
|
155
|
+
expect(originalTitle).toBeDefined();
|
|
156
|
+
|
|
157
|
+
//
|
|
158
|
+
// Recherche sémantique avec retrieve()
|
|
159
|
+
const searchResult = await memory.retrieve(
|
|
160
|
+
"recherche FAO autorisation immeuble Genève",
|
|
161
|
+
userId,
|
|
162
|
+
{ filters: { type: 'discussion' } }
|
|
163
|
+
) as SearchResult;
|
|
164
|
+
|
|
165
|
+
expect(searchResult).toBeDefined();
|
|
166
|
+
expect(searchResult.results).toBeDefined();
|
|
167
|
+
expect(searchResult.results.length).toBeGreaterThan(0);
|
|
168
|
+
|
|
169
|
+
//
|
|
170
|
+
// ✅ Vérifier que le title est dans metadata
|
|
171
|
+
expect(searchResult.results[0].metadata?.title).toBeDefined();
|
|
172
|
+
expect(searchResult.results[0].metadata?.title).toBe(originalTitle);
|
|
173
|
+
expect(searchResult.results[0].score).toBeDefined();
|
|
174
|
+
expect(searchResult.results[0].type).toBe('discussion');
|
|
175
|
+
|
|
176
|
+
console.log('--- retrieve() title:', searchResult.results[0].metadata?.title);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("should list all discussion memories with title in metadata (getAll)", async () => {
|
|
180
|
+
//
|
|
181
|
+
// Capturer plusieurs discussions
|
|
182
|
+
const capture1 = await memory.capture(discussionMessages, userId, {
|
|
183
|
+
metadata: { type: 'discussion', discussionId: 'disc-1' }
|
|
184
|
+
}) as SearchResult;
|
|
185
|
+
|
|
186
|
+
const capture2 = await memory.capture([
|
|
187
|
+
{ role: "user", content: "Comment résilier un bail ?" },
|
|
188
|
+
{ role: "assistant", content: "Voici la procédure de résiliation..." }
|
|
189
|
+
], userId, {
|
|
190
|
+
metadata: { type: 'discussion', discussionId: 'disc-2' }
|
|
191
|
+
}) as SearchResult;
|
|
192
|
+
|
|
193
|
+
//
|
|
194
|
+
// Lister toutes les mémoires discussion avec getAll()
|
|
195
|
+
const allResult = await memory.getAll(userId, { type: 'discussion' }) as SearchResult;
|
|
196
|
+
|
|
197
|
+
expect(allResult).toBeDefined();
|
|
198
|
+
expect(allResult.results.length).toBe(2);
|
|
199
|
+
expect(allResult.results.every(r => r.type === 'discussion')).toBe(true);
|
|
200
|
+
|
|
201
|
+
//
|
|
202
|
+
// ✅ Vérifier que chaque mémoire a un title dans metadata
|
|
203
|
+
expect(allResult.results.every(r => r.metadata?.title !== undefined)).toBe(true);
|
|
204
|
+
|
|
205
|
+
console.log('--- getAll() titles:', allResult.results.map(r => r.metadata?.title));
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("should get a single discussion memory with title in metadata (get)", async () => {
|
|
209
|
+
//
|
|
210
|
+
// Capturer
|
|
211
|
+
const captureResult = await memory.capture(discussionMessages, userId, {
|
|
212
|
+
metadata: { type: 'discussion', discussionId: 'get-test' }
|
|
213
|
+
}) as SearchResult;
|
|
214
|
+
|
|
215
|
+
const memoryId = captureResult.results[0].id;
|
|
216
|
+
const originalTitle = captureResult.results[0].metadata?.title;
|
|
217
|
+
|
|
218
|
+
//
|
|
219
|
+
// Récupérer avec get()
|
|
220
|
+
const getResult = await memory.get(memoryId, userId);
|
|
221
|
+
|
|
222
|
+
expect(getResult).not.toBeNull();
|
|
223
|
+
expect(getResult?.type).toBe('discussion');
|
|
224
|
+
expect(getResult?.memory).toBeDefined();
|
|
225
|
+
|
|
226
|
+
//
|
|
227
|
+
// ✅ Vérifier que le title est dans metadata
|
|
228
|
+
expect(getResult?.metadata?.title).toBeDefined();
|
|
229
|
+
expect(getResult?.metadata?.title).toBe(originalTitle);
|
|
230
|
+
|
|
231
|
+
console.log('--- get() title:', getResult?.metadata?.title);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("should delete a discussion memory", async () => {
|
|
235
|
+
//
|
|
236
|
+
// Capturer
|
|
237
|
+
const result = await memory.capture(discussionMessages, userId, {
|
|
238
|
+
metadata: { type: 'discussion', discussionId: 'delete-test' }
|
|
239
|
+
}) as SearchResult;
|
|
240
|
+
|
|
241
|
+
const memoryId = result.results[0].id;
|
|
242
|
+
|
|
243
|
+
//
|
|
244
|
+
// Supprimer
|
|
245
|
+
await memory.delete(memoryId, userId);
|
|
246
|
+
|
|
247
|
+
//
|
|
248
|
+
// Vérifier suppression
|
|
249
|
+
const deleted = await memory.get(memoryId, userId);
|
|
250
|
+
expect(deleted).toBeNull();
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe("Scoring discussion", () => {
|
|
255
|
+
|
|
256
|
+
it("should have constant score (beta=1, halfLife=Infinity)", async () => {
|
|
257
|
+
//
|
|
258
|
+
// Capturer une discussion
|
|
259
|
+
await memory.capture(discussionMessages, userId, {
|
|
260
|
+
metadata: { type: 'discussion', discussionId: 'score-test' }
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
//
|
|
264
|
+
// Rechercher immédiatement
|
|
265
|
+
const result1 = await memory.retrieve("FAO Genève", userId, {
|
|
266
|
+
filters: { type: 'discussion' }
|
|
267
|
+
}) as SearchResult;
|
|
268
|
+
|
|
269
|
+
//
|
|
270
|
+
// Le score ne devrait pas dépendre du temps (halfLife=Infinity)
|
|
271
|
+
// Avec alpha=0, beta=1, gamma=0, le score = recency = 1 (constant)
|
|
272
|
+
expect(result1.results.length).toBeGreaterThan(0);
|
|
273
|
+
expect(result1.results[0].score).toBeDefined();
|
|
274
|
+
|
|
275
|
+
console.log('--- Score:', result1.results[0].score);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
@@ -8,7 +8,11 @@ dotenv.config();
|
|
|
8
8
|
|
|
9
9
|
jest.setTimeout(30000); // Increase timeout to 30 seconds
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated Tests use factual/episodic types that have been removed
|
|
13
|
+
* These tests are skipped - see migration to discussion memory type
|
|
14
|
+
*/
|
|
15
|
+
describe.skip("Memory Updates - Type Preservation (DEPRECATED)", () => {
|
|
12
16
|
let memory: MemoriesLite;
|
|
13
17
|
let userId: string;
|
|
14
18
|
|
|
@@ -1,175 +0,0 @@
|
|
|
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 Class facts regression tests", () => {
|
|
12
|
-
let memory: MemoriesLite;
|
|
13
|
-
let userId: string;
|
|
14
|
-
|
|
15
|
-
beforeEach(async () => {
|
|
16
|
-
// Initialize memory via helper
|
|
17
|
-
({ memory, userId } = createTestMemory({customPrompt:"L'utilisateur travail pour une régie immobilière!"}));
|
|
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("Edge cases for Facts", () => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
it("should not add memory: Qui suis-je ?", async () => {
|
|
31
|
-
const customFacts = "Je suis Olivier Poulain\nIT et je travaille chez Immeuble SA";
|
|
32
|
-
const result = (await memory.capture([
|
|
33
|
-
{role:"user", content:"Qui suis-je ?"},
|
|
34
|
-
{role:"assistant", content:"Vous êtes Olivier Poulain, Chef de Projets au département IT & Gestion de projet, dans l'équipe IT de Immeuble SA"}],
|
|
35
|
-
userId,
|
|
36
|
-
{customFacts},
|
|
37
|
-
)) as SearchResult;
|
|
38
|
-
expect(result).toBeDefined();
|
|
39
|
-
expect(result.results).toBeDefined();
|
|
40
|
-
expect(result.results.length).toBe(0);
|
|
41
|
-
// expect(result.results[0]?.type).toBe("factual");
|
|
42
|
-
});
|
|
43
|
-
it("should not create memory for temporal events (episodic removed)", async () => {
|
|
44
|
-
const customFacts = "Je suis Olivier Poulain\nIT et je travaille chez Immeuble SA";
|
|
45
|
-
const result = (await memory.capture([
|
|
46
|
-
{role:"user", content:"J'ai faim, je veux manger des sushis pour ma pause de midi."},
|
|
47
|
-
{role:"user", content:"Cherche un restaurant de sushis près de chez moi."}],
|
|
48
|
-
userId,
|
|
49
|
-
{customFacts},
|
|
50
|
-
)) as SearchResult;
|
|
51
|
-
|
|
52
|
-
expect(result).toBeDefined();
|
|
53
|
-
expect(result.results).toBeDefined();
|
|
54
|
-
// Should not create memory for temporal events or action requests
|
|
55
|
-
expect(result.results.length).toBe(0);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
it("should add assistant_preference memory", async () => {
|
|
60
|
-
const result = (await memory.capture(
|
|
61
|
-
"tu dois répondre de manière concise et précise",
|
|
62
|
-
userId,
|
|
63
|
-
{},
|
|
64
|
-
)) as SearchResult;
|
|
65
|
-
|
|
66
|
-
expect(result).toBeDefined();
|
|
67
|
-
expect(result.results).toBeDefined();
|
|
68
|
-
expect(Array.isArray(result.results)).toBe(true);
|
|
69
|
-
expect(result.results.length).toBeGreaterThan(0);
|
|
70
|
-
expect(result.results[0]?.id).toBeDefined();
|
|
71
|
-
expect(result.results[0]?.type).toBe("assistant_preference");
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("business: should not create memory for business queries", async () => {
|
|
75
|
-
// Capture a query that contains a name but is asking for contact information
|
|
76
|
-
const result = (await memory.capture(
|
|
77
|
-
"je cherche le téléphone de mon client Alphonse MAGLOIRE",
|
|
78
|
-
userId,
|
|
79
|
-
{},
|
|
80
|
-
)) as SearchResult;
|
|
81
|
-
|
|
82
|
-
// Verify no memory was created (business query - procedural removed)
|
|
83
|
-
expect(result).toBeDefined();
|
|
84
|
-
expect(result.results).toBeDefined();
|
|
85
|
-
expect(Array.isArray(result.results)).toBe(true);
|
|
86
|
-
// Business queries should not create memories anymore
|
|
87
|
-
expect(result.results.length).toBe(0);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("business: should not create memory for business property questions", async () => {
|
|
91
|
-
const result = (await memory.capture([
|
|
92
|
-
{role:"user", content:"Le logement de Alphonse MAGLOIRE au 5ème étage est de combien pièces.",},
|
|
93
|
-
{role:"assitant", content:"Alphonse MAGLOIRE a un logement de 4 pièces au 5ème étage",}],
|
|
94
|
-
userId,
|
|
95
|
-
{customFacts:"Je suis Olivier Poulain, Je m'occupe de la gérance locataire chez Immeuble SA"},
|
|
96
|
-
)) as SearchResult;
|
|
97
|
-
expect(result).toBeDefined();
|
|
98
|
-
expect(result.results).toBeDefined();
|
|
99
|
-
expect(Array.isArray(result.results)).toBe(true);
|
|
100
|
-
// Business information should not be stored (procedural removed)
|
|
101
|
-
expect(result.results.length).toBe(0);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
it("business: should not create memory for procedure questions", async () => {
|
|
106
|
-
const result = (await memory.capture([
|
|
107
|
-
{role:"user", content:"Quelle est la procédure pour résilier un bail chez Pouet & Compagnie SA ?"}],
|
|
108
|
-
userId,
|
|
109
|
-
{customFacts:"Je suis Olivier Poulain, Je m'occupe de la gérance locataire chez Immeuble SA"},
|
|
110
|
-
)) as SearchResult;
|
|
111
|
-
|
|
112
|
-
expect(result).toBeDefined();
|
|
113
|
-
expect(result.results).toBeDefined();
|
|
114
|
-
expect(Array.isArray(result.results)).toBe(true);
|
|
115
|
-
// Procedure questions should not create memories (procedural removed)
|
|
116
|
-
expect(result.results.length).toBe(0);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("business: should not create memory for client status queries", async () => {
|
|
120
|
-
const result = (await memory.capture([
|
|
121
|
-
{role:"user", content:"Est-ce que Claude RIBUR est à jour avec son loyer ?"}],
|
|
122
|
-
userId,
|
|
123
|
-
{customFacts:"Je suis Olivier Poulain, Je m'occupe de la gérance locataire chez Immeuble SA"},
|
|
124
|
-
)) as SearchResult;
|
|
125
|
-
expect(result).toBeDefined();
|
|
126
|
-
expect(result.results).toBeDefined();
|
|
127
|
-
expect(Array.isArray(result.results)).toBe(true);
|
|
128
|
-
// Business/client queries should not create memories (procedural removed)
|
|
129
|
-
expect(result.results.length).toBe(0);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
it("should add and remove a single memory", async () => {
|
|
134
|
-
const result = (await memory.capture(
|
|
135
|
-
"je veux que tu répondes en italien",
|
|
136
|
-
userId,
|
|
137
|
-
{},
|
|
138
|
-
)) as SearchResult;
|
|
139
|
-
expect(result.results?.[0]?.id).toBeDefined();
|
|
140
|
-
const memoryId = result.results[0]?.id;
|
|
141
|
-
await memory.capture(
|
|
142
|
-
"je veux que tu répondes en français",
|
|
143
|
-
userId,
|
|
144
|
-
{},
|
|
145
|
-
) as SearchResult;
|
|
146
|
-
|
|
147
|
-
const id = await memory.get(memoryId, userId);
|
|
148
|
-
console.log("-- DBG id:", id);
|
|
149
|
-
// expect(id).toBeNull();
|
|
150
|
-
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it.skip("should add and remove a single memory", async () => {
|
|
154
|
-
const result = (await memory.capture(
|
|
155
|
-
"je veux que tu répondes en italien",
|
|
156
|
-
userId,
|
|
157
|
-
{},
|
|
158
|
-
)) as SearchResult;
|
|
159
|
-
expect(result.results?.[0]?.id).toBeDefined();
|
|
160
|
-
const memoryId = result.results[0]?.id;
|
|
161
|
-
await memory.capture(
|
|
162
|
-
"je ne veux plus que tu répondes en Italien",
|
|
163
|
-
userId,
|
|
164
|
-
{},
|
|
165
|
-
) as SearchResult;
|
|
166
|
-
|
|
167
|
-
const id = await memory.get(memoryId, userId);
|
|
168
|
-
console.log("-- DBG id:", id);
|
|
169
|
-
expect(id).toBeNull();
|
|
170
|
-
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
});
|
|
175
|
-
});
|
|
@@ -1,126 +0,0 @@
|
|
|
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("Memory Class TODO regression tests", () => {
|
|
12
|
-
let memory: MemoriesLite;
|
|
13
|
-
let userId: string;
|
|
14
|
-
|
|
15
|
-
beforeEach(async () => {
|
|
16
|
-
// Initialize memory via helper
|
|
17
|
-
({ memory, userId } = createTestMemory({ customPrompt: "L'utilisateur travail pour une régie immobilière!" }));
|
|
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("Manage TODOs (add/remove/update)", () => {
|
|
28
|
-
it("should add a TODO when explicitly asked with 'ajoute un todo'", async () => {
|
|
29
|
-
const result = (await memory.capture([
|
|
30
|
-
{ role: "user", content: "Ajoute un todo: appeler la banque demain à 09:00" },
|
|
31
|
-
{ role: "assistant", content: "Compris, j'ajoute un todo." }
|
|
32
|
-
], userId, {})) as SearchResult;
|
|
33
|
-
|
|
34
|
-
expect(result).toBeDefined();
|
|
35
|
-
expect(Array.isArray(result.results)).toBe(true);
|
|
36
|
-
// At least one TODO should be captured when explicitly asked
|
|
37
|
-
expect(result.results.some(r => r.type === "todo")).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("should not create a TODO when task is implicit only", async () => {
|
|
41
|
-
const result = (await memory.capture([
|
|
42
|
-
{ role: "user", content: "Je dois appeler la banque demain à 09:00" },
|
|
43
|
-
{ role: "assistant", content: "Très bien." }
|
|
44
|
-
], userId, {})) as SearchResult;
|
|
45
|
-
|
|
46
|
-
expect(result).toBeDefined();
|
|
47
|
-
expect(Array.isArray(result.results)).toBe(true);
|
|
48
|
-
// Must not infer TODO without explicit user request
|
|
49
|
-
expect(result.results.every(r => r.type !== "todo")).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("should not create memory for how-to business requests (procedural removed)", async () => {
|
|
53
|
-
const result = (await memory.capture([
|
|
54
|
-
{ role: "user", content: "Je veux résilier un bail, comment faire ?" },
|
|
55
|
-
{ role: "assistant", content: "Voici la procédure..." }
|
|
56
|
-
], userId, {})) as SearchResult;
|
|
57
|
-
|
|
58
|
-
expect(result).toBeDefined();
|
|
59
|
-
expect(Array.isArray(result.results)).toBe(true);
|
|
60
|
-
// Business procedures should not create memories (procedural type removed)
|
|
61
|
-
expect(result.results.length).toBe(0);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("should add then update a TODO via new capture", async () => {
|
|
65
|
-
const add = (await memory.capture([
|
|
66
|
-
{ role: "user", content: "Ajoute un todo: appeler la banque demain à 09:00" },
|
|
67
|
-
{ role: "assistant", content: "Compris, j'ajoute un todo." }
|
|
68
|
-
], userId, {})) as SearchResult;
|
|
69
|
-
|
|
70
|
-
expect(add).toBeDefined();
|
|
71
|
-
const todo = add.results.find(r => r.type === "todo");
|
|
72
|
-
expect(todo?.id).toBeDefined();
|
|
73
|
-
|
|
74
|
-
const newText = "Appeler la banque lundi à 10:00";
|
|
75
|
-
const updateRes = (await memory.capture([
|
|
76
|
-
{ role: "user", content: `Mets à jour ce todo: ${newText}` },
|
|
77
|
-
{ role: "assistant", content: "OK, je mets à jour le todo." }
|
|
78
|
-
], userId, {})) as SearchResult;
|
|
79
|
-
|
|
80
|
-
expect(updateRes).toBeDefined();
|
|
81
|
-
// Expect an UPDATE event captured (merger decides UPDATE vs ADD)
|
|
82
|
-
const updatedItem = updateRes.results.find(r => (r as any).metadata?.event === 'UPDATE' && r.type === 'todo');
|
|
83
|
-
expect(updatedItem).toBeDefined();
|
|
84
|
-
// Allow optional suffix from LLM like annotations removal; assert containment
|
|
85
|
-
expect(updatedItem?.memory?.toLowerCase().includes("appeler la banque lundi à 10:00".toLowerCase())).toBe(true);
|
|
86
|
-
|
|
87
|
-
const persisted = await memory.get(updatedItem!.id, userId);
|
|
88
|
-
expect(persisted).not.toBeNull();
|
|
89
|
-
expect(persisted?.memory?.toLowerCase().includes("appeler la banque lundi à 10:00".toLowerCase())).toBe(true);
|
|
90
|
-
expect(persisted?.type).toBe("todo");
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("should add then delete a TODO via new capture", async () => {
|
|
94
|
-
const add = (await memory.capture([
|
|
95
|
-
{ role: "user", content: "Ajoute un todo: appeler la banque demain à 09:00" },
|
|
96
|
-
{ role: "assistant", content: "Compris, j'ajoute un todo." }
|
|
97
|
-
], userId, {})) as SearchResult;
|
|
98
|
-
|
|
99
|
-
const todo = add.results.find(r => r.type === "todo");
|
|
100
|
-
expect(todo?.id).toBeDefined();
|
|
101
|
-
|
|
102
|
-
const delRes = (await memory.capture([
|
|
103
|
-
{ role: "user", content: `Supprime ce todo: ${todo!.memory}` },
|
|
104
|
-
{ role: "assistant", content: "Je supprime le todo." }
|
|
105
|
-
], userId, {})) as SearchResult;
|
|
106
|
-
|
|
107
|
-
expect(delRes).toBeDefined();
|
|
108
|
-
const deletedItem = delRes.results.find(r => (r as any).metadata?.event === 'DELETE' && r.type === 'todo');
|
|
109
|
-
expect(deletedItem).toBeDefined();
|
|
110
|
-
|
|
111
|
-
const persisted = await memory.get(deletedItem!.id, userId);
|
|
112
|
-
expect(persisted).toBeNull();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("should add a TODO using 'tâche' wording (synonym)", async () => {
|
|
116
|
-
const result = (await memory.capture([
|
|
117
|
-
{ role: "user", content: "Ajoute une tâche: appeler la banque demain à 09:00" },
|
|
118
|
-
{ role: "assistant", content: "C'est noté, j'ajoute la tâche." }
|
|
119
|
-
], userId, {})) as SearchResult;
|
|
120
|
-
|
|
121
|
-
expect(result).toBeDefined();
|
|
122
|
-
expect(Array.isArray(result.results)).toBe(true);
|
|
123
|
-
expect(result.results.some(r => r.type === "todo")).toBe(true);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
});
|