memories-lite 0.99.1 → 0.99.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/dist/config/defaults.js +5 -19
- package/dist/config/manager.js +1 -6
- package/dist/memory/index.d.ts +2 -4
- package/dist/memory/index.js +16 -47
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.js +2 -2
- package/dist/types/index.d.ts +12 -97
- package/dist/types/index.js +2 -19
- package/dist/vectorstores/lite.d.ts +1 -0
- package/dist/vectorstores/lite.js +11 -6
- package/memories-lite-a42ac5108869b599bcbac21069f63fb47f07452fcc4b87e89b3c06a945612d0b.db +0 -0
- package/memories-lite-a9137698d8d3fdbf27efcdc8cd372084b52d484e8db866c5455bbb3f85299b54.db +0 -0
- package/package.json +1 -1
- package/src/config/defaults.ts +5 -19
- package/src/config/manager.ts +1 -6
- package/src/memory/index.ts +25 -57
- package/src/prompts/index.ts +2 -2
- package/src/types/index.ts +4 -35
- package/src/vectorstores/lite.ts +13 -7
- package/tests/init.mem.ts +7 -4
- package/tests/lite.spec.ts +16 -16
- package/tests/memory.discussion.search.test.ts +377 -0
|
@@ -0,0 +1,377 @@
|
|
|
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(120000); // 120s pour les tests avec LLM et multiples recherches
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tests de recherche sémantique pour les mémoires de discussion
|
|
13
|
+
* Objectif: Mesurer le clivage des scores entre recherches pertinentes et non pertinentes
|
|
14
|
+
*/
|
|
15
|
+
describe("Memory Discussion - Recherche sémantique et scoring", () => {
|
|
16
|
+
let memory: MemoriesLite;
|
|
17
|
+
let userId: string;
|
|
18
|
+
|
|
19
|
+
// Test avec searchThreshold=0.50
|
|
20
|
+
let memoryWithThreshold: MemoriesLite;
|
|
21
|
+
let userIdWithThreshold: string;
|
|
22
|
+
|
|
23
|
+
//
|
|
24
|
+
// Discussion FAO (Feuille d'Avis Officielle) - Genève immeubles
|
|
25
|
+
const discussionFAO = [
|
|
26
|
+
{
|
|
27
|
+
role: "user",
|
|
28
|
+
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."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
role: "assistant",
|
|
32
|
+
content: `Voici un premier lot traité. Dites-moi si vous voulez que je continue sur plus d'avis.
|
|
33
|
+
|
|
34
|
+
Adresses extraites et recherche de nos immeubles
|
|
35
|
+
- Chemin Émile-Bressler 19, 1226 Thônex
|
|
36
|
+
- Immeubles PRSA à proximité (200 m) : aucun trouvé.
|
|
37
|
+
- FAO: Requête M/341685/1 — Démolition d'une villa
|
|
38
|
+
- Chemin du Port 9, 1246 Corsier
|
|
39
|
+
- FAO: Requête M/341714/1 — Démolition
|
|
40
|
+
- Boulevard de Saint‑Georges 46, 1205 Genève (Plainpalais)
|
|
41
|
+
- FAO: Autorisation DD 330818 — Transformation`
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
role: "user",
|
|
45
|
+
content: "oui merci pour le premier"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
role: "assistant",
|
|
49
|
+
content: `Parfait. Je poursuis avec ces trois adresses et vous reviens avec:
|
|
50
|
+
- adresses exactes confirmées
|
|
51
|
+
- présence/absence de nos immeubles à 200 m
|
|
52
|
+
- liens FAO utiles
|
|
53
|
+
|
|
54
|
+
Cibles:
|
|
55
|
+
- Chemin du Port 9, 1246 Corsier
|
|
56
|
+
- Boulevard de Saint‑Georges 46, 1205 Genève
|
|
57
|
+
- Chemin du Champ‑des‑Bois 8, 1255 Veyrier`
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
role: "user",
|
|
61
|
+
content: "oui"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
role: "assistant",
|
|
65
|
+
content: `Voici la suite.
|
|
66
|
+
|
|
67
|
+
Résultats par adresse (rayon 200 m)
|
|
68
|
+
- Chemin du Port 9, 1246 Corsier — aucun immeuble PRSA trouvé.
|
|
69
|
+
- Boulevard de Saint‑Georges 46, 1205 Genève — aucun immeuble PRSA trouvé.
|
|
70
|
+
- Chemin du Champ‑des‑Bois 8, 1255 Veyrier — aucun immeuble PRSA trouvé.
|
|
71
|
+
|
|
72
|
+
Souhaitez-vous que je:
|
|
73
|
+
- élargisse à 300–500 m,
|
|
74
|
+
- continue l'extraction sur d'autres avis FAO récents (démolitions/transformations/APA),
|
|
75
|
+
- ou cible des communes/quartiers spécifiques ?`
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
//
|
|
80
|
+
// Discussion résiliation de bail
|
|
81
|
+
const discussionBail = [
|
|
82
|
+
{ role: "user", content: "Comment résilier un bail commercial ?" },
|
|
83
|
+
{ role: "assistant", content: `Voici la procédure de résiliation d'un bail commercial:
|
|
84
|
+
|
|
85
|
+
1. **Délai de préavis**: Généralement 6 mois avant l'échéance
|
|
86
|
+
2. **Forme**: Lettre recommandée obligatoire
|
|
87
|
+
3. **Motifs légitimes**: Non-paiement, usage non conforme, besoin personnel
|
|
88
|
+
4. **Formulaire officiel**: Utiliser le formulaire cantonal
|
|
89
|
+
|
|
90
|
+
Points importants:
|
|
91
|
+
- Respecter les délais stricts
|
|
92
|
+
- Conserver une copie de l'envoi
|
|
93
|
+
- Vérifier les clauses spéciales du bail` }
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
//
|
|
97
|
+
// Discussion comptabilité
|
|
98
|
+
const discussionCompta = [
|
|
99
|
+
{ role: "user", content: "Comment faire le bouclement annuel ?" },
|
|
100
|
+
{ role: "assistant", content: `Procédure de bouclement annuel:
|
|
101
|
+
|
|
102
|
+
1. **Inventaire**: Vérifier les stocks et actifs
|
|
103
|
+
2. **Rapprochements**: Comptes bancaires, créanciers, débiteurs
|
|
104
|
+
3. **Provisions**: Estimer les risques et charges
|
|
105
|
+
4. **Amortissements**: Calculer selon le plan
|
|
106
|
+
5. **Ajustements**: Régularisations de fin d'année
|
|
107
|
+
6. **États financiers**: Bilan, compte de résultat, annexe
|
|
108
|
+
|
|
109
|
+
Échéances: 3 mois après clôture pour les sociétés suisses.` }
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
beforeAll(async () => {
|
|
113
|
+
//
|
|
114
|
+
// Setup unique - capturer toutes les discussions une fois (sans seuil)
|
|
115
|
+
({ memory, userId } = createTestMemory({}));
|
|
116
|
+
await memory.reset(userId);
|
|
117
|
+
|
|
118
|
+
// Capturer les 3 discussions
|
|
119
|
+
await memory.capture(discussionFAO, userId, {
|
|
120
|
+
metadata: { type: 'discussion', discussionId: 'fao-1' }
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await memory.capture(discussionBail, userId, {
|
|
124
|
+
metadata: { type: 'discussion', discussionId: 'bail-1' }
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
await memory.capture(discussionCompta, userId, {
|
|
128
|
+
metadata: { type: 'discussion', discussionId: 'compta-1' }
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
//
|
|
132
|
+
// Setup avec searchThreshold=0.50
|
|
133
|
+
({ memory: memoryWithThreshold, userId: userIdWithThreshold } = createTestMemory({ searchThreshold: 0.50 }));
|
|
134
|
+
await memoryWithThreshold.reset(userIdWithThreshold);
|
|
135
|
+
|
|
136
|
+
// Capturer les mêmes discussions
|
|
137
|
+
await memoryWithThreshold.capture(discussionFAO, userIdWithThreshold, {
|
|
138
|
+
metadata: { type: 'discussion', discussionId: 'fao-1' }
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await memoryWithThreshold.capture(discussionBail, userIdWithThreshold, {
|
|
142
|
+
metadata: { type: 'discussion', discussionId: 'bail-1' }
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
await memoryWithThreshold.capture(discussionCompta, userIdWithThreshold, {
|
|
146
|
+
metadata: { type: 'discussion', discussionId: 'compta-1' }
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
console.log('\n📚 3 discussions capturées (2x: sans seuil + avec seuil 0.50)\n');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
afterAll(async () => {
|
|
153
|
+
await memory.reset(userId);
|
|
154
|
+
await memoryWithThreshold.reset(userIdWithThreshold);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe("Clivage des scores - Recherches pertinentes vs non pertinentes", () => {
|
|
158
|
+
|
|
159
|
+
it("should return HIGH score for FAO-related queries (> 0.3)", async () => {
|
|
160
|
+
const queries = [
|
|
161
|
+
"recherche FAO autorisation immeuble Genève",
|
|
162
|
+
"APA démolition villa Thônex",
|
|
163
|
+
"vérifier permis construction Corsier",
|
|
164
|
+
"immeubles PRSA rayon 200m"
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
console.log('\n🎯 Recherches PERTINENTES pour FAO:\n');
|
|
168
|
+
|
|
169
|
+
const scores: number[] = [];
|
|
170
|
+
for (const query of queries) {
|
|
171
|
+
const result = await memory.retrieve(query, userId, {
|
|
172
|
+
limit: 3,
|
|
173
|
+
filters: { type: 'discussion' }
|
|
174
|
+
}) as SearchResult;
|
|
175
|
+
|
|
176
|
+
const topResult = result.results[0];
|
|
177
|
+
const score = topResult?.score || 0;
|
|
178
|
+
scores.push(score);
|
|
179
|
+
|
|
180
|
+
console.log(` "${query}"`);
|
|
181
|
+
console.log(` → ${topResult?.metadata?.title}`);
|
|
182
|
+
console.log(` → Score: ${score.toFixed(4)}\n`);
|
|
183
|
+
|
|
184
|
+
expect(topResult).toBeDefined();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Les scores pour des recherches pertinentes devraient être > 0.3
|
|
188
|
+
const avgScore = scores.reduce((a, b) => a + b, 0) / scores.length;
|
|
189
|
+
console.log(` 📊 Score moyen FAO: ${avgScore.toFixed(4)}\n`);
|
|
190
|
+
expect(avgScore).toBeGreaterThan(0.3);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("should return LOW score for unrelated queries", async () => {
|
|
194
|
+
const queries = [
|
|
195
|
+
"recette de cuisine pasta carbonara",
|
|
196
|
+
"météo paris demain",
|
|
197
|
+
"comment jouer au tennis",
|
|
198
|
+
"voyage vacances Thaïlande"
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
console.log('\n❌ Recherches NON PERTINENTES:\n');
|
|
202
|
+
|
|
203
|
+
const scores: number[] = [];
|
|
204
|
+
|
|
205
|
+
for (const query of queries) {
|
|
206
|
+
const result = await memory.retrieve(query, userId, {
|
|
207
|
+
limit: 3,
|
|
208
|
+
filters: { type: 'discussion' }
|
|
209
|
+
}) as SearchResult;
|
|
210
|
+
|
|
211
|
+
const topResult = result.results[0];
|
|
212
|
+
const score = topResult?.score || 0;
|
|
213
|
+
scores.push(score);
|
|
214
|
+
|
|
215
|
+
console.log(` "${query}"`);
|
|
216
|
+
console.log(` → ${topResult?.metadata?.title || 'Aucun résultat'}`);
|
|
217
|
+
console.log(` → Score: ${score.toFixed(4)}\n`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Les scores pour des sujets non pertinents devraient être constants (1.0 pour discussion)
|
|
221
|
+
// car discussion utilise alpha=0, beta=1 (pas de cosine similarity)
|
|
222
|
+
// Le clivage se fait via la présence/absence de résultats ou via des filtres
|
|
223
|
+
expect(scores.length).toBe(4);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("should match bail queries to bail discussion", async () => {
|
|
227
|
+
const queries = [
|
|
228
|
+
"résiliation bail commercial",
|
|
229
|
+
"préavis locataire formulaire",
|
|
230
|
+
"lettre recommandée bail"
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
console.log('\n🏠 Recherches BAIL:\n');
|
|
234
|
+
|
|
235
|
+
for (const query of queries) {
|
|
236
|
+
const result = await memory.retrieve(query, userId, {
|
|
237
|
+
limit: 3,
|
|
238
|
+
filters: { type: 'discussion' }
|
|
239
|
+
}) as SearchResult;
|
|
240
|
+
|
|
241
|
+
const topResult = result.results[0];
|
|
242
|
+
console.log(` "${query}"`);
|
|
243
|
+
console.log(` → ${topResult?.metadata?.title}`);
|
|
244
|
+
console.log(` → Score: ${topResult?.score?.toFixed(4)}\n`);
|
|
245
|
+
|
|
246
|
+
expect(topResult).toBeDefined();
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("should match comptabilité queries to compta discussion", async () => {
|
|
251
|
+
const queries = [
|
|
252
|
+
"bouclement annuel comptabilité",
|
|
253
|
+
"amortissements provisions",
|
|
254
|
+
"états financiers bilan"
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
console.log('\n📊 Recherches COMPTABILITÉ:\n');
|
|
258
|
+
|
|
259
|
+
for (const query of queries) {
|
|
260
|
+
const result = await memory.retrieve(query, userId, {
|
|
261
|
+
limit: 3,
|
|
262
|
+
filters: { type: 'discussion' }
|
|
263
|
+
}) as SearchResult;
|
|
264
|
+
|
|
265
|
+
const topResult = result.results[0];
|
|
266
|
+
console.log(` "${query}"`);
|
|
267
|
+
console.log(` → ${topResult?.metadata?.title}`);
|
|
268
|
+
console.log(` → Score: ${topResult?.score?.toFixed(4)}\n`);
|
|
269
|
+
|
|
270
|
+
expect(topResult).toBeDefined();
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe("Analyse comparative des scores", () => {
|
|
276
|
+
|
|
277
|
+
it("should show score distribution across all queries", async () => {
|
|
278
|
+
const testCases = [
|
|
279
|
+
// Très pertinent FAO
|
|
280
|
+
{ query: "FAO Genève permis démolition", expected: 'FAO' },
|
|
281
|
+
{ query: "autorisation APA immeuble PRSA", expected: 'FAO' },
|
|
282
|
+
// Pertinent Bail
|
|
283
|
+
{ query: "résilier contrat location", expected: 'bail' },
|
|
284
|
+
{ query: "préavis bail commercial", expected: 'bail' },
|
|
285
|
+
// Pertinent Compta
|
|
286
|
+
{ query: "clôture exercice comptable", expected: 'compta' },
|
|
287
|
+
{ query: "bilan financier annuel", expected: 'compta' },
|
|
288
|
+
// Non pertinent
|
|
289
|
+
{ query: "pizza margherita recette", expected: 'none' },
|
|
290
|
+
{ query: "football match score", expected: 'none' },
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
console.log('\n📈 ANALYSE COMPARATIVE DES SCORES:\n');
|
|
294
|
+
console.log('Query | Top Match | Score');
|
|
295
|
+
console.log('------|-----------|------');
|
|
296
|
+
|
|
297
|
+
const results: { query: string, title: string, score: number, expected: string }[] = [];
|
|
298
|
+
|
|
299
|
+
for (const tc of testCases) {
|
|
300
|
+
const result = await memory.retrieve(tc.query, userId, {
|
|
301
|
+
limit: 1,
|
|
302
|
+
filters: { type: 'discussion' }
|
|
303
|
+
}) as SearchResult;
|
|
304
|
+
|
|
305
|
+
const top = result.results[0];
|
|
306
|
+
results.push({
|
|
307
|
+
query: tc.query,
|
|
308
|
+
title: top?.metadata?.title || 'N/A',
|
|
309
|
+
score: top?.score || 0,
|
|
310
|
+
expected: tc.expected
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
console.log(`"${tc.query.substring(0, 30).padEnd(30)}" | ${(top?.metadata?.title || 'N/A').substring(0, 40).padEnd(40)} | ${top?.score?.toFixed(4) || 'N/A'}`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
console.log('\n');
|
|
317
|
+
|
|
318
|
+
// Note: Avec discussion (alpha=1), le score = cosine similarity
|
|
319
|
+
// Le clivage se fait sur le score absolu
|
|
320
|
+
expect(results.length).toBe(8);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
describe("searchThreshold - Filtrage des résultats", () => {
|
|
325
|
+
|
|
326
|
+
it("should return NO results for unrelated queries with searchThreshold=0.50", async () => {
|
|
327
|
+
const unrelatedQueries = [
|
|
328
|
+
"pizza margherita recette",
|
|
329
|
+
"football match score",
|
|
330
|
+
"météo paris demain",
|
|
331
|
+
"voyage vacances Thaïlande"
|
|
332
|
+
];
|
|
333
|
+
|
|
334
|
+
console.log('\n🔒 TEST searchThreshold=0.50 - Recherches NON pertinentes:\n');
|
|
335
|
+
|
|
336
|
+
for (const query of unrelatedQueries) {
|
|
337
|
+
const result = await memoryWithThreshold.retrieve(query, userIdWithThreshold, {
|
|
338
|
+
limit: 3,
|
|
339
|
+
filters: { type: 'discussion' }
|
|
340
|
+
}) as SearchResult;
|
|
341
|
+
|
|
342
|
+
console.log(` "${query}"`);
|
|
343
|
+
console.log(` → Résultats: ${result.results.length}\n`);
|
|
344
|
+
|
|
345
|
+
// ✅ Aucun résultat car score < 0.50
|
|
346
|
+
expect(result.results.length).toBe(0);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("should return results for relevant queries with searchThreshold=0.50", async () => {
|
|
351
|
+
const relevantQueries = [
|
|
352
|
+
"autorisation APA immeuble PRSA",
|
|
353
|
+
"préavis bail commercial",
|
|
354
|
+
"bilan financier annuel"
|
|
355
|
+
];
|
|
356
|
+
|
|
357
|
+
console.log('\n✅ TEST searchThreshold=0.50 - Recherches PERTINENTES:\n');
|
|
358
|
+
|
|
359
|
+
for (const query of relevantQueries) {
|
|
360
|
+
const result = await memoryWithThreshold.retrieve(query, userIdWithThreshold, {
|
|
361
|
+
limit: 3,
|
|
362
|
+
filters: { type: 'discussion' }
|
|
363
|
+
}) as SearchResult;
|
|
364
|
+
|
|
365
|
+
const topResult = result.results[0];
|
|
366
|
+
console.log(` "${query}"`);
|
|
367
|
+
console.log(` → Résultats: ${result.results.length}`);
|
|
368
|
+
console.log(` → Top: ${topResult?.metadata?.title} (score: ${topResult?.score?.toFixed(4)})\n`);
|
|
369
|
+
|
|
370
|
+
// ✅ Au moins un résultat car score >= 0.50
|
|
371
|
+
expect(result.results.length).toBeGreaterThan(0);
|
|
372
|
+
expect(topResult?.score).toBeGreaterThanOrEqual(0.50);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|