memories-lite 0.99.2 → 0.99.5
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/memory/index.d.ts +8 -1
- package/dist/memory/index.js +42 -12
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.js +5 -1
- package/memories-lite-a42ac5108869b599bcbac21069f63fb47f07452fcc4b87e89b3c06a945612d0b.db +0 -0
- package/memories-lite-a9137698d8d3fdbf27efcdc8cd372084b52d484e8db866c5455bbb3f85299b54.db +0 -0
- package/package.json +1 -1
- package/src/memory/index.ts +58 -15
- package/src/types/index.ts +16 -0
- package/tests/lite.spec.ts +16 -16
- package/tests/memory.applymode.test.ts +228 -0
package/dist/memory/index.d.ts
CHANGED
|
@@ -33,7 +33,14 @@ export declare class MemoriesLite {
|
|
|
33
33
|
capture(messages: string | Message[], userId: string, config: AddMemoryOptions): Promise<SearchResult>;
|
|
34
34
|
get(memoryId: string, userId: string): Promise<MemoryItem | null>;
|
|
35
35
|
retrieve(query: string, userId: string, config: SearchMemoryOptions): Promise<SearchResult>;
|
|
36
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Met à jour une mémoire existante
|
|
38
|
+
* @param memoryId - ID de la mémoire
|
|
39
|
+
* @param data - Nouveau contenu
|
|
40
|
+
* @param userId - ID utilisateur
|
|
41
|
+
* @param metadata - Métadonnées optionnelles (peut inclure applyMode pour changer le mode)
|
|
42
|
+
*/
|
|
43
|
+
update(memoryId: string, data: string, userId: string, metadata?: Record<string, any>): Promise<{
|
|
37
44
|
message: string;
|
|
38
45
|
}>;
|
|
39
46
|
delete(memoryId: string, userId: string): Promise<{
|
package/dist/memory/index.js
CHANGED
|
@@ -132,11 +132,14 @@ class MemoriesLite {
|
|
|
132
132
|
const embedding = await this.embedder.embed(title);
|
|
133
133
|
//
|
|
134
134
|
// Préparer les métadonnées
|
|
135
|
+
// applyMode par défaut = MEM_SMART (recherche sémantique)
|
|
135
136
|
const memoryType = metadata.type || 'discussion';
|
|
137
|
+
const applyMode = metadata.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE;
|
|
136
138
|
const memoryMetadata = {
|
|
137
139
|
...metadata,
|
|
138
140
|
title,
|
|
139
141
|
type: memoryType,
|
|
142
|
+
applyMode,
|
|
140
143
|
userId,
|
|
141
144
|
};
|
|
142
145
|
//
|
|
@@ -203,6 +206,7 @@ class MemoriesLite {
|
|
|
203
206
|
id: memory.id,
|
|
204
207
|
memory: memory.payload.data,
|
|
205
208
|
type: memory.payload.type,
|
|
209
|
+
applyMode: memory.payload.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE,
|
|
206
210
|
hash: memory.payload.hash,
|
|
207
211
|
createdAt: memory.payload.createdAt,
|
|
208
212
|
updatedAt: memory.payload.updatedAt,
|
|
@@ -252,12 +256,21 @@ class MemoriesLite {
|
|
|
252
256
|
"data",
|
|
253
257
|
"createdAt",
|
|
254
258
|
"updatedAt",
|
|
259
|
+
"applyMode",
|
|
255
260
|
]);
|
|
256
|
-
|
|
261
|
+
//
|
|
262
|
+
// Filtrer uniquement les mémoires avec applyMode = MEM_SMART (recherche sémantique)
|
|
263
|
+
// Les MEM_ALWAYS et MEM_MANUAL ne sont pas retournées par retrieve()
|
|
264
|
+
const filteredMemories = memories.filter((mem) => {
|
|
265
|
+
const mode = mem.payload.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE;
|
|
266
|
+
return mode === 'MEM_SMART';
|
|
267
|
+
});
|
|
268
|
+
const results = filteredMemories.map((mem) => ({
|
|
257
269
|
id: mem.id,
|
|
258
270
|
memory: mem.payload.data,
|
|
259
271
|
hash: mem.payload.hash,
|
|
260
272
|
type: mem.payload.type,
|
|
273
|
+
applyMode: mem.payload.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE,
|
|
261
274
|
createdAt: mem.payload.createdAt,
|
|
262
275
|
updatedAt: mem.payload.updatedAt,
|
|
263
276
|
score: mem.score,
|
|
@@ -271,10 +284,17 @@ class MemoriesLite {
|
|
|
271
284
|
results,
|
|
272
285
|
};
|
|
273
286
|
}
|
|
274
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Met à jour une mémoire existante
|
|
289
|
+
* @param memoryId - ID de la mémoire
|
|
290
|
+
* @param data - Nouveau contenu
|
|
291
|
+
* @param userId - ID utilisateur
|
|
292
|
+
* @param metadata - Métadonnées optionnelles (peut inclure applyMode pour changer le mode)
|
|
293
|
+
*/
|
|
294
|
+
async update(memoryId, data, userId, metadata = {}) {
|
|
275
295
|
// await this._captureEvent("update", { memory_id: memoryId });
|
|
276
296
|
const embedding = await this.embedder.embed(data);
|
|
277
|
-
await this.updateMemory(memoryId, data, { [data]: embedding },
|
|
297
|
+
await this.updateMemory(memoryId, data, { [data]: embedding }, metadata, userId);
|
|
278
298
|
return { message: "Memory updated successfully!" };
|
|
279
299
|
}
|
|
280
300
|
async delete(memoryId, userId) {
|
|
@@ -350,12 +370,14 @@ class MemoriesLite {
|
|
|
350
370
|
"data",
|
|
351
371
|
"createdAt",
|
|
352
372
|
"updatedAt",
|
|
373
|
+
"applyMode",
|
|
353
374
|
]);
|
|
354
375
|
const results = memories.map((mem) => ({
|
|
355
376
|
id: mem.id,
|
|
356
377
|
memory: mem.payload.data,
|
|
357
378
|
hash: mem.payload.hash,
|
|
358
379
|
type: mem.payload.type,
|
|
380
|
+
applyMode: mem.payload.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE,
|
|
359
381
|
createdAt: mem.payload.createdAt,
|
|
360
382
|
updatedAt: mem.payload.updatedAt,
|
|
361
383
|
metadata: Object.entries(mem.payload)
|
|
@@ -388,20 +410,28 @@ class MemoriesLite {
|
|
|
388
410
|
throw new Error(`Memory with ID ${memoryId} not found`);
|
|
389
411
|
}
|
|
390
412
|
const prevValue = existingMemory.payload.data;
|
|
413
|
+
const originalPayload = (existingMemory.payload ?? {});
|
|
391
414
|
const embedding = existingEmbeddings[data] || (await this.embedder.embed(data));
|
|
415
|
+
//
|
|
416
|
+
// Preserve origin fields to avoid corrupting ownership/linkage (discussionId/userId/agentId).
|
|
417
|
+
// - title: keep original unless explicitly changed
|
|
418
|
+
// - applyMode: keep original unless explicitly changed
|
|
419
|
+
// - discussionId, userId, agentId/runId: always keep original
|
|
420
|
+
const title = (typeof metadata.title === "string" && metadata.title.trim())
|
|
421
|
+
? metadata.title.trim()
|
|
422
|
+
: originalPayload.title;
|
|
423
|
+
//
|
|
424
|
+
// applyMode: si fourni dans metadata, on le prend, sinon on garde l'original ou le défaut
|
|
425
|
+
const applyMode = (metadata.applyMode && ['MEM_ALWAYS', 'MEM_SMART', 'MEM_MANUAL'].includes(metadata.applyMode))
|
|
426
|
+
? metadata.applyMode
|
|
427
|
+
: (originalPayload.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE);
|
|
392
428
|
const newMetadata = {
|
|
393
|
-
...
|
|
429
|
+
...originalPayload,
|
|
394
430
|
data,
|
|
395
431
|
hash: (0, crypto_1.createHash)("md5").update(data).digest("hex"),
|
|
396
|
-
type: existingMemory.payload.type,
|
|
397
|
-
createdAt: existingMemory.payload.createdAt,
|
|
398
432
|
updatedAt: new Date().toISOString(),
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}),
|
|
402
|
-
...(existingMemory.payload.runId && {
|
|
403
|
-
runId: existingMemory.payload.runId,
|
|
404
|
-
}),
|
|
433
|
+
title: title,
|
|
434
|
+
applyMode,
|
|
405
435
|
};
|
|
406
436
|
await vectorStore.update(memoryId, embedding, newMetadata);
|
|
407
437
|
await this.db.addHistory(memoryId, prevValue, data, "UPDATE", newMetadata.createdAt, newMetadata.updatedAt);
|
package/dist/types/index.d.ts
CHANGED
|
@@ -77,10 +77,24 @@ export interface MemoryConfig {
|
|
|
77
77
|
capturePrompt?: string;
|
|
78
78
|
}
|
|
79
79
|
export type MemoryType = 'assistant_preference' | 'discussion';
|
|
80
|
+
/**
|
|
81
|
+
* Mode d'application de la mémoire (inspiré de Cursor AI)
|
|
82
|
+
* Stocké dans metadata.applyMode avec préfixe MEM_* pour migration soft
|
|
83
|
+
*
|
|
84
|
+
* - MEM_ALWAYS: Toujours appliquer - la mémoire est toujours injectée dans le contexte
|
|
85
|
+
* - MEM_SMART: Appliquer intelligemment - utilisé pour la recherche sémantique (défaut)
|
|
86
|
+
* - MEM_MANUAL: Appliquer manuellement - la mémoire n'est jamais injectée automatiquement
|
|
87
|
+
*/
|
|
88
|
+
export type MemoryApplyMode = 'MEM_ALWAYS' | 'MEM_SMART' | 'MEM_MANUAL';
|
|
89
|
+
/**
|
|
90
|
+
* Mode par défaut pour la capture
|
|
91
|
+
*/
|
|
92
|
+
export declare const DEFAULT_MEMORY_APPLY_MODE: MemoryApplyMode;
|
|
80
93
|
export interface MemoryItem {
|
|
81
94
|
id: string;
|
|
82
95
|
memory: string;
|
|
83
96
|
type: MemoryType;
|
|
97
|
+
applyMode?: MemoryApplyMode;
|
|
84
98
|
event?: "ADD" | "UPDATE" | "DELETE" | "NONE";
|
|
85
99
|
hash?: string;
|
|
86
100
|
createdAt?: string;
|
package/dist/types/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MemoryConfigSchema = void 0;
|
|
3
|
+
exports.MemoryConfigSchema = exports.DEFAULT_MEMORY_APPLY_MODE = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Mode par défaut pour la capture
|
|
7
|
+
*/
|
|
8
|
+
exports.DEFAULT_MEMORY_APPLY_MODE = 'MEM_SMART';
|
|
5
9
|
exports.MemoryConfigSchema = zod_1.z.object({
|
|
6
10
|
version: zod_1.z.string().optional(),
|
|
7
11
|
embedder: zod_1.z.object({
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/src/memory/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
MemoryConfigSchema,
|
|
6
6
|
MemoryItem,
|
|
7
7
|
MemoryType,
|
|
8
|
+
MemoryApplyMode,
|
|
9
|
+
DEFAULT_MEMORY_APPLY_MODE,
|
|
8
10
|
Message,
|
|
9
11
|
SearchFilters,
|
|
10
12
|
SearchResult,
|
|
@@ -208,11 +210,14 @@ export class MemoriesLite {
|
|
|
208
210
|
|
|
209
211
|
//
|
|
210
212
|
// Préparer les métadonnées
|
|
213
|
+
// applyMode par défaut = MEM_SMART (recherche sémantique)
|
|
211
214
|
const memoryType: MemoryType = metadata.type || 'discussion';
|
|
215
|
+
const applyMode: MemoryApplyMode = metadata.applyMode || DEFAULT_MEMORY_APPLY_MODE;
|
|
212
216
|
const memoryMetadata = {
|
|
213
217
|
...metadata,
|
|
214
218
|
title,
|
|
215
219
|
type: memoryType,
|
|
220
|
+
applyMode,
|
|
216
221
|
userId,
|
|
217
222
|
};
|
|
218
223
|
|
|
@@ -228,7 +233,7 @@ export class MemoriesLite {
|
|
|
228
233
|
console.log(`-- 🧠 Memory created: "${title}" (${memoryType})`);
|
|
229
234
|
|
|
230
235
|
return [{
|
|
231
|
-
|
|
236
|
+
id: memoryId,
|
|
232
237
|
memory: summary,
|
|
233
238
|
type: memoryType,
|
|
234
239
|
metadata: { title, event: "ADD" },
|
|
@@ -292,7 +297,7 @@ export class MemoriesLite {
|
|
|
292
297
|
userId,
|
|
293
298
|
filters,
|
|
294
299
|
capturePrompt,
|
|
295
|
-
|
|
300
|
+
);
|
|
296
301
|
|
|
297
302
|
return {
|
|
298
303
|
results: vectorStoreResult,
|
|
@@ -315,6 +320,7 @@ export class MemoriesLite {
|
|
|
315
320
|
id: memory.id,
|
|
316
321
|
memory: memory.payload.data,
|
|
317
322
|
type: memory.payload.type,
|
|
323
|
+
applyMode: memory.payload.applyMode || DEFAULT_MEMORY_APPLY_MODE,
|
|
318
324
|
hash: memory.payload.hash,
|
|
319
325
|
createdAt: memory.payload.createdAt,
|
|
320
326
|
updatedAt: memory.payload.updatedAt,
|
|
@@ -382,12 +388,23 @@ export class MemoriesLite {
|
|
|
382
388
|
"data",
|
|
383
389
|
"createdAt",
|
|
384
390
|
"updatedAt",
|
|
391
|
+
"applyMode",
|
|
385
392
|
]);
|
|
386
|
-
|
|
393
|
+
|
|
394
|
+
//
|
|
395
|
+
// Filtrer uniquement les mémoires avec applyMode = MEM_SMART (recherche sémantique)
|
|
396
|
+
// Les MEM_ALWAYS et MEM_MANUAL ne sont pas retournées par retrieve()
|
|
397
|
+
const filteredMemories = memories.filter((mem) => {
|
|
398
|
+
const mode = mem.payload.applyMode || DEFAULT_MEMORY_APPLY_MODE;
|
|
399
|
+
return mode === 'MEM_SMART';
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
const results = filteredMemories.map((mem) => ({
|
|
387
403
|
id: mem.id,
|
|
388
404
|
memory: mem.payload.data,
|
|
389
405
|
hash: mem.payload.hash,
|
|
390
406
|
type: mem.payload.type,
|
|
407
|
+
applyMode: mem.payload.applyMode || DEFAULT_MEMORY_APPLY_MODE,
|
|
391
408
|
createdAt: mem.payload.createdAt,
|
|
392
409
|
updatedAt: mem.payload.updatedAt,
|
|
393
410
|
score: mem.score,
|
|
@@ -403,10 +420,22 @@ export class MemoriesLite {
|
|
|
403
420
|
};
|
|
404
421
|
}
|
|
405
422
|
|
|
406
|
-
|
|
423
|
+
/**
|
|
424
|
+
* Met à jour une mémoire existante
|
|
425
|
+
* @param memoryId - ID de la mémoire
|
|
426
|
+
* @param data - Nouveau contenu
|
|
427
|
+
* @param userId - ID utilisateur
|
|
428
|
+
* @param metadata - Métadonnées optionnelles (peut inclure applyMode pour changer le mode)
|
|
429
|
+
*/
|
|
430
|
+
async update(
|
|
431
|
+
memoryId: string,
|
|
432
|
+
data: string,
|
|
433
|
+
userId: string,
|
|
434
|
+
metadata: Record<string, any> = {},
|
|
435
|
+
): Promise<{ message: string }> {
|
|
407
436
|
// await this._captureEvent("update", { memory_id: memoryId });
|
|
408
437
|
const embedding = await this.embedder.embed(data);
|
|
409
|
-
await this.updateMemory(memoryId, data, { [data]: embedding },
|
|
438
|
+
await this.updateMemory(memoryId, data, { [data]: embedding }, metadata, userId);
|
|
410
439
|
return { message: "Memory updated successfully!" };
|
|
411
440
|
}
|
|
412
441
|
|
|
@@ -501,12 +530,14 @@ export class MemoriesLite {
|
|
|
501
530
|
"data",
|
|
502
531
|
"createdAt",
|
|
503
532
|
"updatedAt",
|
|
533
|
+
"applyMode",
|
|
504
534
|
]);
|
|
505
535
|
const results = memories.map((mem) => ({
|
|
506
536
|
id: mem.id,
|
|
507
537
|
memory: mem.payload.data,
|
|
508
538
|
hash: mem.payload.hash,
|
|
509
539
|
type: mem.payload.type,
|
|
540
|
+
applyMode: mem.payload.applyMode || DEFAULT_MEMORY_APPLY_MODE,
|
|
510
541
|
createdAt: mem.payload.createdAt,
|
|
511
542
|
updatedAt: mem.payload.updatedAt,
|
|
512
543
|
metadata: Object.entries(mem.payload)
|
|
@@ -564,22 +595,34 @@ export class MemoriesLite {
|
|
|
564
595
|
}
|
|
565
596
|
|
|
566
597
|
const prevValue = existingMemory.payload.data;
|
|
598
|
+
const originalPayload: Record<string, any> = (existingMemory.payload ?? {}) as Record<string, any>;
|
|
567
599
|
const embedding =
|
|
568
600
|
existingEmbeddings[data] || (await this.embedder.embed(data));
|
|
569
601
|
|
|
570
|
-
|
|
571
|
-
|
|
602
|
+
//
|
|
603
|
+
// Preserve origin fields to avoid corrupting ownership/linkage (discussionId/userId/agentId).
|
|
604
|
+
// - title: keep original unless explicitly changed
|
|
605
|
+
// - applyMode: keep original unless explicitly changed
|
|
606
|
+
// - discussionId, userId, agentId/runId: always keep original
|
|
607
|
+
const title =
|
|
608
|
+
(typeof metadata.title === "string" && metadata.title.trim())
|
|
609
|
+
? metadata.title.trim()
|
|
610
|
+
: originalPayload.title;
|
|
611
|
+
|
|
612
|
+
//
|
|
613
|
+
// applyMode: si fourni dans metadata, on le prend, sinon on garde l'original ou le défaut
|
|
614
|
+
const applyMode: MemoryApplyMode =
|
|
615
|
+
(metadata.applyMode && ['MEM_ALWAYS', 'MEM_SMART', 'MEM_MANUAL'].includes(metadata.applyMode))
|
|
616
|
+
? metadata.applyMode
|
|
617
|
+
: (originalPayload.applyMode || DEFAULT_MEMORY_APPLY_MODE);
|
|
618
|
+
|
|
619
|
+
const newMetadata: Record<string, any> = {
|
|
620
|
+
...originalPayload,
|
|
572
621
|
data,
|
|
573
622
|
hash: createHash("md5").update(data).digest("hex"),
|
|
574
|
-
type: existingMemory.payload.type,
|
|
575
|
-
createdAt: existingMemory.payload.createdAt,
|
|
576
623
|
updatedAt: new Date().toISOString(),
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}),
|
|
580
|
-
...(existingMemory.payload.runId && {
|
|
581
|
-
runId: existingMemory.payload.runId,
|
|
582
|
-
}),
|
|
624
|
+
title: title,
|
|
625
|
+
applyMode,
|
|
583
626
|
};
|
|
584
627
|
|
|
585
628
|
await vectorStore.update(memoryId, embedding, newMetadata);
|
package/src/types/index.ts
CHANGED
|
@@ -88,10 +88,26 @@ export interface MemoryConfig {
|
|
|
88
88
|
|
|
89
89
|
export type MemoryType = 'assistant_preference' | 'discussion';
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Mode d'application de la mémoire (inspiré de Cursor AI)
|
|
93
|
+
* Stocké dans metadata.applyMode avec préfixe MEM_* pour migration soft
|
|
94
|
+
*
|
|
95
|
+
* - MEM_ALWAYS: Toujours appliquer - la mémoire est toujours injectée dans le contexte
|
|
96
|
+
* - MEM_SMART: Appliquer intelligemment - utilisé pour la recherche sémantique (défaut)
|
|
97
|
+
* - MEM_MANUAL: Appliquer manuellement - la mémoire n'est jamais injectée automatiquement
|
|
98
|
+
*/
|
|
99
|
+
export type MemoryApplyMode = 'MEM_ALWAYS' | 'MEM_SMART' | 'MEM_MANUAL';
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Mode par défaut pour la capture
|
|
103
|
+
*/
|
|
104
|
+
export const DEFAULT_MEMORY_APPLY_MODE: MemoryApplyMode = 'MEM_SMART';
|
|
105
|
+
|
|
91
106
|
export interface MemoryItem {
|
|
92
107
|
id: string;
|
|
93
108
|
memory: string;
|
|
94
109
|
type: MemoryType;
|
|
110
|
+
applyMode?: MemoryApplyMode;
|
|
95
111
|
event?: "ADD" | "UPDATE" | "DELETE" | "NONE";
|
|
96
112
|
hash?: string;
|
|
97
113
|
createdAt?: string;
|
package/tests/lite.spec.ts
CHANGED
|
@@ -132,35 +132,35 @@ describe('LiteVectorStore Private Methods', () => {
|
|
|
132
132
|
//
|
|
133
133
|
// discussion with Infinity halfLife → old memories have same score as new
|
|
134
134
|
const payload: MemoryPayload = { memoryId: 'mem-d2', userId: userId, type: 'discussion', createdAt: veryOldDate };
|
|
135
|
-
|
|
135
|
+
const cosineScore = 0.9;
|
|
136
136
|
const expectedRecency = 1.0; // Infinity halfLife → recency = 1 even for old dates
|
|
137
|
-
|
|
137
|
+
const hybridScore = (store as any).calculateHybridScore(cosineScore, payload);
|
|
138
138
|
expect(hybridScore).toBe(1); // Score constant = 1
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
it('should handle assistant_preference with no decay (Infinity half-life)', () => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
const payload: MemoryPayload = { memoryId: 'mem-a1', userId: userId, type: 'assistant_preference', createdAt: veryOldDate };
|
|
143
|
+
const cosineScore = 0.7;
|
|
144
|
+
const expectedRecency = 1.0;
|
|
145
|
+
const expectedScore = scoring.assistant_preference.alpha * cosineScore + scoring.assistant_preference.beta * expectedRecency + scoring.assistant_preference.gamma;
|
|
146
|
+
const hybridScore = (store as any).calculateHybridScore(cosineScore, payload);
|
|
147
|
+
expect(hybridScore).toBeCloseTo(expectedScore, 5);
|
|
148
148
|
});
|
|
149
149
|
|
|
150
150
|
it('should use default scoring if type is missing', () => {
|
|
151
151
|
const payload: MemoryPayload = { memoryId: 'mem-def1', userId: userId, createdAt: sevenDaysAgo }; // No type specified
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
152
|
+
const cosineScore = 0.8;
|
|
153
|
+
const expectedRecency = (store as any).calculateRecencyScore(sevenDaysAgo, scoring.default.halfLifeDays);
|
|
154
|
+
const expectedScore = scoring.default.alpha * cosineScore + scoring.default.beta * expectedRecency + scoring.default.gamma;
|
|
155
|
+
const hybridScore = (store as any).calculateHybridScore(cosineScore, payload);
|
|
156
|
+
expect(hybridScore).toBeCloseTo(expectedScore, 5);
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
it('should return score >= 0 even with negative cosine similarity', () => {
|
|
160
160
|
const payload: MemoryPayload = { memoryId: 'mem-neg1', userId: userId, type: 'assistant_preference', createdAt: twoDaysAgo };
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
const cosineScore = -0.5;
|
|
162
|
+
const hybridScore = (store as any).calculateHybridScore(cosineScore, payload);
|
|
163
|
+
expect(hybridScore).toBeGreaterThanOrEqual(0);
|
|
164
164
|
});
|
|
165
165
|
|
|
166
166
|
});
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/// <reference types="jest" />
|
|
2
|
+
import { MemoriesLite, DEFAULT_MEMORY_APPLY_MODE, MemoryApplyMode } 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);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tests pour le MemoryApplyMode (type d'application de mémoire)
|
|
13
|
+
* - MEM_ALWAYS: Toujours appliquer
|
|
14
|
+
* - MEM_SMART: Appliquer intelligemment (défaut, recherche sémantique)
|
|
15
|
+
* - MEM_MANUAL: Appliquer manuellement
|
|
16
|
+
*/
|
|
17
|
+
describe("Memory ApplyMode", () => {
|
|
18
|
+
let memory: MemoriesLite;
|
|
19
|
+
let userId: string;
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
({ memory, userId } = createTestMemory({}));
|
|
23
|
+
await memory.reset(userId);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(async () => {
|
|
27
|
+
await memory.reset(userId);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("Default ApplyMode", () => {
|
|
31
|
+
it("should set applyMode to MEM_SMART by default on capture", async () => {
|
|
32
|
+
//
|
|
33
|
+
// Capture une discussion avec le mode par défaut
|
|
34
|
+
const messages = [
|
|
35
|
+
{ role: "user", content: "Bonjour, comment gérer les congés ?" },
|
|
36
|
+
{ role: "assistant", content: "Pour gérer les congés, vous devez..." }
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const result = await memory.capture(messages, userId, {
|
|
40
|
+
metadata: { type: "discussion" }
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(result.results.length).toBeGreaterThan(0);
|
|
44
|
+
const createdMemory = result.results[0];
|
|
45
|
+
|
|
46
|
+
//
|
|
47
|
+
// Vérifier que le mode par défaut est MEM_SMART
|
|
48
|
+
expect(createdMemory.metadata?.applyMode || DEFAULT_MEMORY_APPLY_MODE).toBe("MEM_SMART");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should return DEFAULT_MEMORY_APPLY_MODE as MEM_SMART", () => {
|
|
52
|
+
expect(DEFAULT_MEMORY_APPLY_MODE).toBe("MEM_SMART");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("ApplyMode Update", () => {
|
|
57
|
+
it("should update applyMode when specified in metadata", async () => {
|
|
58
|
+
//
|
|
59
|
+
// Créer une mémoire avec le mode par défaut
|
|
60
|
+
const messages = [
|
|
61
|
+
{ role: "user", content: "Comment faire une facture ?" },
|
|
62
|
+
{ role: "assistant", content: "Pour créer une facture..." }
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const result = await memory.capture(messages, userId, {
|
|
66
|
+
metadata: { type: "discussion" }
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(result.results.length).toBeGreaterThan(0);
|
|
70
|
+
const memoryId = result.results[0].id;
|
|
71
|
+
|
|
72
|
+
//
|
|
73
|
+
// Mettre à jour avec un nouveau mode
|
|
74
|
+
await memory.update(
|
|
75
|
+
memoryId,
|
|
76
|
+
"Procédure de facturation mise à jour",
|
|
77
|
+
userId,
|
|
78
|
+
{ applyMode: "MEM_ALWAYS" as MemoryApplyMode }
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
//
|
|
82
|
+
// Vérifier que le mode a été mis à jour
|
|
83
|
+
const updatedMemory = await memory.get(memoryId, userId);
|
|
84
|
+
expect(updatedMemory).not.toBeNull();
|
|
85
|
+
expect(updatedMemory?.applyMode).toBe("MEM_ALWAYS");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should preserve applyMode when not specified in update metadata", async () => {
|
|
89
|
+
//
|
|
90
|
+
// Créer une mémoire avec un mode spécifique
|
|
91
|
+
const messages = [
|
|
92
|
+
{ role: "user", content: "Règles de sécurité" },
|
|
93
|
+
{ role: "assistant", content: "Les règles de sécurité sont..." }
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const result = await memory.capture(messages, userId, {
|
|
97
|
+
metadata: { type: "discussion", applyMode: "MEM_MANUAL" }
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(result.results.length).toBeGreaterThan(0);
|
|
101
|
+
const memoryId = result.results[0].id;
|
|
102
|
+
|
|
103
|
+
//
|
|
104
|
+
// Mettre à jour le contenu sans changer le mode
|
|
105
|
+
await memory.update(
|
|
106
|
+
memoryId,
|
|
107
|
+
"Règles de sécurité mises à jour",
|
|
108
|
+
userId,
|
|
109
|
+
{} // Pas de applyMode dans les metadata
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
//
|
|
113
|
+
// Vérifier que le mode est préservé
|
|
114
|
+
const updatedMemory = await memory.get(memoryId, userId);
|
|
115
|
+
expect(updatedMemory).not.toBeNull();
|
|
116
|
+
expect(updatedMemory?.applyMode).toBe("MEM_MANUAL");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should change applyMode from MEM_SMART to MEM_MANUAL", async () => {
|
|
120
|
+
//
|
|
121
|
+
// Créer une mémoire avec le mode par défaut
|
|
122
|
+
const messages = [
|
|
123
|
+
{ role: "user", content: "Comment utiliser l'API ?" },
|
|
124
|
+
{ role: "assistant", content: "L'API s'utilise de la manière suivante..." }
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
const result = await memory.capture(messages, userId, {
|
|
128
|
+
metadata: { type: "discussion" }
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const memoryId = result.results[0].id;
|
|
132
|
+
|
|
133
|
+
//
|
|
134
|
+
// Changer de MEM_SMART vers MEM_MANUAL
|
|
135
|
+
await memory.update(
|
|
136
|
+
memoryId,
|
|
137
|
+
"Documentation API mise à jour",
|
|
138
|
+
userId,
|
|
139
|
+
{ applyMode: "MEM_MANUAL" as MemoryApplyMode }
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const updatedMemory = await memory.get(memoryId, userId);
|
|
143
|
+
expect(updatedMemory?.applyMode).toBe("MEM_MANUAL");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe("Retrieve Filtering by ApplyMode", () => {
|
|
148
|
+
it("should only return MEM_SMART memories in retrieve()", async () => {
|
|
149
|
+
//
|
|
150
|
+
// Créer 3 mémoires avec différents modes
|
|
151
|
+
const messagesBase = [
|
|
152
|
+
{ role: "user", content: "Question sur les procédures" },
|
|
153
|
+
{ role: "assistant", content: "Les procédures sont..." }
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
//
|
|
157
|
+
// Mémoire 1: MEM_SMART (par défaut)
|
|
158
|
+
await memory.capture(messagesBase, userId, {
|
|
159
|
+
metadata: { type: "discussion", customTag: "smart" }
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
//
|
|
163
|
+
// Mémoire 2: MEM_ALWAYS
|
|
164
|
+
await memory.capture([
|
|
165
|
+
{ role: "user", content: "Règle toujours appliquée" },
|
|
166
|
+
{ role: "assistant", content: "Cette règle est toujours appliquée..." }
|
|
167
|
+
], userId, {
|
|
168
|
+
metadata: { type: "discussion", applyMode: "MEM_ALWAYS", customTag: "always" }
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
//
|
|
172
|
+
// Mémoire 3: MEM_MANUAL
|
|
173
|
+
await memory.capture([
|
|
174
|
+
{ role: "user", content: "Note manuelle" },
|
|
175
|
+
{ role: "assistant", content: "Ceci est une note manuelle..." }
|
|
176
|
+
], userId, {
|
|
177
|
+
metadata: { type: "discussion", applyMode: "MEM_MANUAL", customTag: "manual" }
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
//
|
|
181
|
+
// Vérifier que getAll retourne les 3 mémoires
|
|
182
|
+
const allMemories = await memory.getAll(userId, { type: "discussion" });
|
|
183
|
+
expect(allMemories.results.length).toBe(3);
|
|
184
|
+
|
|
185
|
+
//
|
|
186
|
+
// Vérifier que retrieve() ne retourne que MEM_SMART
|
|
187
|
+
const searchResult = await memory.retrieve("procédures", userId, { limit: 10 });
|
|
188
|
+
|
|
189
|
+
//
|
|
190
|
+
// Seule la mémoire MEM_SMART devrait être retournée
|
|
191
|
+
const smartMemories = searchResult.results.filter(m => m.applyMode === "MEM_SMART");
|
|
192
|
+
const nonSmartMemories = searchResult.results.filter(m => m.applyMode !== "MEM_SMART");
|
|
193
|
+
|
|
194
|
+
expect(smartMemories.length).toBeGreaterThanOrEqual(0);
|
|
195
|
+
expect(nonSmartMemories.length).toBe(0);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("GetAll returns all ApplyModes", () => {
|
|
200
|
+
it("should return memories of all applyModes in getAll()", async () => {
|
|
201
|
+
//
|
|
202
|
+
// Créer des mémoires avec différents modes
|
|
203
|
+
await memory.capture([
|
|
204
|
+
{ role: "user", content: "Mémoire intelligente" },
|
|
205
|
+
{ role: "assistant", content: "Réponse intelligente" }
|
|
206
|
+
], userId, {
|
|
207
|
+
metadata: { type: "discussion" } // MEM_SMART par défaut
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
await memory.capture([
|
|
211
|
+
{ role: "user", content: "Mémoire toujours" },
|
|
212
|
+
{ role: "assistant", content: "Réponse toujours" }
|
|
213
|
+
], userId, {
|
|
214
|
+
metadata: { type: "discussion", applyMode: "MEM_ALWAYS" }
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
//
|
|
218
|
+
// getAll doit retourner les deux
|
|
219
|
+
const allMemories = await memory.getAll(userId, { type: "discussion" });
|
|
220
|
+
expect(allMemories.results.length).toBe(2);
|
|
221
|
+
|
|
222
|
+
const modes = allMemories.results.map(m => m.applyMode);
|
|
223
|
+
expect(modes).toContain("MEM_SMART");
|
|
224
|
+
expect(modes).toContain("MEM_ALWAYS");
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|