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
package/src/memory/index.ts
CHANGED
|
@@ -40,22 +40,20 @@ import { zodResponseFormat } from "openai/helpers/zod";
|
|
|
40
40
|
|
|
41
41
|
export class MemoriesLite {
|
|
42
42
|
private config: MemoryConfig;
|
|
43
|
-
private
|
|
43
|
+
private capturePrompt: string | undefined;
|
|
44
44
|
private embedder: Embedder;
|
|
45
45
|
private vectorStoreConfig: VectorStoreConfig;
|
|
46
46
|
private llm: LLM;
|
|
47
47
|
private db: HistoryManager;
|
|
48
48
|
private collectionName: string | undefined;
|
|
49
49
|
private apiVersion: string;
|
|
50
|
-
private graphMemory?: any;
|
|
51
|
-
private enableGraph: boolean;
|
|
52
50
|
telemetryId: string;
|
|
53
51
|
|
|
54
52
|
constructor(config: Partial<MemoryConfig> = {}) {
|
|
55
53
|
// Merge and validate config
|
|
56
54
|
this.config = ConfigManager.mergeConfig(config);
|
|
57
55
|
|
|
58
|
-
this.
|
|
56
|
+
this.capturePrompt = this.config.capturePrompt;
|
|
59
57
|
this.embedder = EmbedderFactory.create(
|
|
60
58
|
this.config.embedder.provider,
|
|
61
59
|
this.config.embedder.config,
|
|
@@ -88,14 +86,8 @@ export class MemoriesLite {
|
|
|
88
86
|
|
|
89
87
|
this.collectionName = this.config.vectorStore.config.collectionName;
|
|
90
88
|
this.apiVersion = this.config.version || "v1.0";
|
|
91
|
-
this.enableGraph = this.config.enableGraph || false;
|
|
92
89
|
this.telemetryId = "anonymous";
|
|
93
90
|
|
|
94
|
-
// Initialize graph memory if configured
|
|
95
|
-
if (this.enableGraph && this.config.graphStore) {
|
|
96
|
-
// this.graphMemory = new MemoryGraph(this.config);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
91
|
// Initialize telemetry if vector store is initialized
|
|
100
92
|
// this._initializeTelemetry();
|
|
101
93
|
}
|
|
@@ -109,7 +101,6 @@ export class MemoriesLite {
|
|
|
109
101
|
api_version: this.apiVersion,
|
|
110
102
|
client_type: "Memory",
|
|
111
103
|
collection_name: this.collectionName,
|
|
112
|
-
enable_graph: this.enableGraph,
|
|
113
104
|
});
|
|
114
105
|
} catch (error) {}
|
|
115
106
|
}
|
|
@@ -176,7 +167,7 @@ export class MemoriesLite {
|
|
|
176
167
|
// Générer la synthèse via LLM
|
|
177
168
|
const [systemPrompt, userPrompt] = getDiscussionSynthesisMessages(
|
|
178
169
|
parsedMessages,
|
|
179
|
-
capturePrompt || this.
|
|
170
|
+
capturePrompt || this.capturePrompt
|
|
180
171
|
);
|
|
181
172
|
|
|
182
173
|
const response = await this.llm.generateResponse(
|
|
@@ -212,8 +203,8 @@ export class MemoriesLite {
|
|
|
212
203
|
}
|
|
213
204
|
|
|
214
205
|
//
|
|
215
|
-
// Créer l'embedding sur le
|
|
216
|
-
const embedding = await this.embedder.embed(
|
|
206
|
+
// Créer l'embedding sur le title seul (TEST COMPARATIF)
|
|
207
|
+
const embedding = await this.embedder.embed(title);
|
|
217
208
|
|
|
218
209
|
//
|
|
219
210
|
// Préparer les métadonnées
|
|
@@ -237,7 +228,7 @@ export class MemoriesLite {
|
|
|
237
228
|
console.log(`-- 🧠 Memory created: "${title}" (${memoryType})`);
|
|
238
229
|
|
|
239
230
|
return [{
|
|
240
|
-
|
|
231
|
+
id: memoryId,
|
|
241
232
|
memory: summary,
|
|
242
233
|
type: memoryType,
|
|
243
234
|
metadata: { title, event: "ADD" },
|
|
@@ -301,25 +292,10 @@ export class MemoriesLite {
|
|
|
301
292
|
userId,
|
|
302
293
|
filters,
|
|
303
294
|
capturePrompt,
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
//
|
|
307
|
-
// Graph store (si configuré)
|
|
308
|
-
let graphResult;
|
|
309
|
-
if (this.graphMemory) {
|
|
310
|
-
try {
|
|
311
|
-
graphResult = await this.graphMemory.add(
|
|
312
|
-
final_parsedMessages.map((m) => m.content).join("\n"),
|
|
313
|
-
filters,
|
|
314
295
|
);
|
|
315
|
-
} catch (error) {
|
|
316
|
-
console.error("Error adding to graph memory:", error);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
296
|
|
|
320
297
|
return {
|
|
321
298
|
results: vectorStoreResult,
|
|
322
|
-
relations: graphResult?.relations,
|
|
323
299
|
};
|
|
324
300
|
}
|
|
325
301
|
|
|
@@ -398,16 +374,6 @@ export class MemoriesLite {
|
|
|
398
374
|
);
|
|
399
375
|
|
|
400
376
|
|
|
401
|
-
// Search graph store if available
|
|
402
|
-
let graphResults =[];
|
|
403
|
-
if (this.graphMemory) {
|
|
404
|
-
try {
|
|
405
|
-
graphResults = await this.graphMemory.search(query, filters);
|
|
406
|
-
} catch (error) {
|
|
407
|
-
console.error("Error searching graph memory:", error);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
377
|
const excludedKeys = new Set([
|
|
412
378
|
"userId",
|
|
413
379
|
"agentId",
|
|
@@ -434,14 +400,18 @@ export class MemoriesLite {
|
|
|
434
400
|
|
|
435
401
|
return {
|
|
436
402
|
results,
|
|
437
|
-
relations: graphResults,
|
|
438
403
|
};
|
|
439
404
|
}
|
|
440
405
|
|
|
441
|
-
async update(
|
|
406
|
+
async update(
|
|
407
|
+
memoryId: string,
|
|
408
|
+
data: string,
|
|
409
|
+
userId: string,
|
|
410
|
+
metadata: Record<string, any> = {},
|
|
411
|
+
): Promise<{ message: string }> {
|
|
442
412
|
// await this._captureEvent("update", { memory_id: memoryId });
|
|
443
413
|
const embedding = await this.embedder.embed(data);
|
|
444
|
-
await this.updateMemory(memoryId, data, { [data]: embedding },
|
|
414
|
+
await this.updateMemory(memoryId, data, { [data]: embedding }, metadata, userId);
|
|
445
415
|
return { message: "Memory updated successfully!" };
|
|
446
416
|
}
|
|
447
417
|
|
|
@@ -496,10 +466,6 @@ export class MemoriesLite {
|
|
|
496
466
|
// Check provider before attempting deleteCol
|
|
497
467
|
await vectorStore.deleteCol();
|
|
498
468
|
|
|
499
|
-
if (this.graphMemory) {
|
|
500
|
-
await this.graphMemory.deleteAll({ userId: "default" }); // Assuming this is okay, or needs similar check?
|
|
501
|
-
}
|
|
502
|
-
|
|
503
469
|
// Re-initialize factories/clients based on the original config
|
|
504
470
|
this.embedder = EmbedderFactory.create(
|
|
505
471
|
this.config.embedder.provider,
|
|
@@ -603,22 +569,24 @@ export class MemoriesLite {
|
|
|
603
569
|
}
|
|
604
570
|
|
|
605
571
|
const prevValue = existingMemory.payload.data;
|
|
572
|
+
const originalPayload: Record<string, any> = (existingMemory.payload ?? {}) as Record<string, any>;
|
|
606
573
|
const embedding =
|
|
607
574
|
existingEmbeddings[data] || (await this.embedder.embed(data));
|
|
608
575
|
|
|
609
|
-
|
|
610
|
-
|
|
576
|
+
//
|
|
577
|
+
// Preserve origin fields to avoid corrupting ownership/linkage (discussionId/userId/agentId).
|
|
578
|
+
// - title: keep original unless explicitly changed
|
|
579
|
+
// - discussionId, userId, agentId/runId: always keep original
|
|
580
|
+
const title =
|
|
581
|
+
(typeof metadata.title === "string" && metadata.title.trim())
|
|
582
|
+
? metadata.title.trim()
|
|
583
|
+
: originalPayload.title;
|
|
584
|
+
const newMetadata: Record<string, any> = {
|
|
585
|
+
...originalPayload,
|
|
611
586
|
data,
|
|
612
587
|
hash: createHash("md5").update(data).digest("hex"),
|
|
613
|
-
type: existingMemory.payload.type,
|
|
614
|
-
createdAt: existingMemory.payload.createdAt,
|
|
615
588
|
updatedAt: new Date().toISOString(),
|
|
616
|
-
|
|
617
|
-
agentId: existingMemory.payload.agentId,
|
|
618
|
-
}),
|
|
619
|
-
...(existingMemory.payload.runId && {
|
|
620
|
-
runId: existingMemory.payload.runId,
|
|
621
|
-
}),
|
|
589
|
+
title: title,
|
|
622
590
|
};
|
|
623
591
|
|
|
624
592
|
await vectorStore.update(memoryId, embedding, newMetadata);
|
package/src/prompts/index.ts
CHANGED
|
@@ -21,8 +21,8 @@ export const DiscussionSynthesisSchema = z.object({
|
|
|
21
21
|
export const DEFAULT_DISCUSSION_PROMPT = `Tu es un expert en synthèse opérationnelle.
|
|
22
22
|
|
|
23
23
|
À partir de cette discussion, génère :
|
|
24
|
-
1. TITRE: Un titre court et descriptif (
|
|
25
|
-
2. SUMMARY: Les points clés du chemin de résolution (
|
|
24
|
+
1. TITRE: Un titre court et descriptif (10-20 mots) qui capture l'essence de la demande
|
|
25
|
+
2. SUMMARY: Les points clés du chemin de résolution en markdown (max 150 mots)
|
|
26
26
|
|
|
27
27
|
Cette synthèse servira à retrouver et réappliquer ce pattern de résolution similaire.
|
|
28
28
|
Utilise la même langue que la discussion.
|
package/src/types/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ export interface VectorStoreConfig {
|
|
|
30
30
|
cacheTtl?: number;
|
|
31
31
|
scoring?: MemoryScoringConfig;
|
|
32
32
|
recencyCleanupThreshold?: number;
|
|
33
|
+
searchThreshold?: number; // Seuil minimum de score pour retourner un résultat (0-1)
|
|
33
34
|
[key: string]: any;
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -51,19 +52,6 @@ export interface LLMConfig {
|
|
|
51
52
|
modelProperties?: Record<string, any>;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
export interface Neo4jConfig {
|
|
55
|
-
url: string;
|
|
56
|
-
username: string;
|
|
57
|
-
password: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface GraphStoreConfig {
|
|
61
|
-
provider: string;
|
|
62
|
-
config: Neo4jConfig;
|
|
63
|
-
llm?: LLMConfig;
|
|
64
|
-
customPrompt?: string;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
55
|
export interface MemoryTypeConfig {
|
|
68
56
|
alpha: number; // Weight for cosine similarity
|
|
69
57
|
beta: number; // Weight for recency
|
|
@@ -95,9 +83,7 @@ export interface MemoryConfig {
|
|
|
95
83
|
historyStore?: HistoryStoreConfig;
|
|
96
84
|
disableHistory?: boolean;
|
|
97
85
|
historyDbPath?: string;
|
|
98
|
-
|
|
99
|
-
graphStore?: GraphStoreConfig;
|
|
100
|
-
enableGraph?: boolean;
|
|
86
|
+
capturePrompt?: string; // Prompt par défaut pour la synthèse de discussion
|
|
101
87
|
}
|
|
102
88
|
|
|
103
89
|
export type MemoryType = 'assistant_preference' | 'discussion';
|
|
@@ -181,6 +167,7 @@ export const MemoryConfigSchema = z.object({
|
|
|
181
167
|
}),
|
|
182
168
|
}).optional(),
|
|
183
169
|
recencyCleanupThreshold: z.number().min(0).max(1).optional(),
|
|
170
|
+
searchThreshold: z.number().min(0).max(1).optional(),
|
|
184
171
|
})
|
|
185
172
|
.passthrough(),
|
|
186
173
|
}),
|
|
@@ -193,25 +180,7 @@ export const MemoryConfigSchema = z.object({
|
|
|
193
180
|
}),
|
|
194
181
|
}),
|
|
195
182
|
historyDbPath: z.string().optional(),
|
|
196
|
-
|
|
197
|
-
enableGraph: z.boolean().optional(),
|
|
198
|
-
graphStore: z
|
|
199
|
-
.object({
|
|
200
|
-
provider: z.string(),
|
|
201
|
-
config: z.object({
|
|
202
|
-
url: z.string(),
|
|
203
|
-
username: z.string(),
|
|
204
|
-
password: z.string(),
|
|
205
|
-
}),
|
|
206
|
-
llm: z
|
|
207
|
-
.object({
|
|
208
|
-
provider: z.string(),
|
|
209
|
-
config: z.record(z.string(), z.any()),
|
|
210
|
-
})
|
|
211
|
-
.optional(),
|
|
212
|
-
customPrompt: z.string().optional(),
|
|
213
|
-
})
|
|
214
|
-
.optional(),
|
|
183
|
+
capturePrompt: z.string().optional(),
|
|
215
184
|
historyStore: z
|
|
216
185
|
.object({
|
|
217
186
|
provider: z.string(),
|
package/src/vectorstores/lite.ts
CHANGED
|
@@ -31,7 +31,8 @@ export class LiteVectorStore implements VectorStore {
|
|
|
31
31
|
private dimension: number;
|
|
32
32
|
private currentUserId: string;
|
|
33
33
|
private scoringConfig: MemoryScoringConfig;
|
|
34
|
-
private cleanupThreshold?: number; //
|
|
34
|
+
private cleanupThreshold?: number; // Seuil de recency pour cleanup
|
|
35
|
+
private searchThreshold: number; // Seuil minimum de score pour retourner un résultat
|
|
35
36
|
private static cache: Map<string, LiteVectorStore>;
|
|
36
37
|
constructor(config: VectorStoreConfig, currentUserId: string) {
|
|
37
38
|
if (!config.scoring) {
|
|
@@ -41,7 +42,8 @@ export class LiteVectorStore implements VectorStore {
|
|
|
41
42
|
this.currentUserId = currentUserId;
|
|
42
43
|
this.isSecure = config.secure || false;
|
|
43
44
|
this.scoringConfig = config.scoring;
|
|
44
|
-
this.cleanupThreshold = config.recencyCleanupThreshold || 0.25; // (default 0.25 means 2 times the half-life
|
|
45
|
+
this.cleanupThreshold = config.recencyCleanupThreshold || 0.25; // (default 0.25 means 2 times the half-life)
|
|
46
|
+
this.searchThreshold = config.searchThreshold ?? 0; // Seuil de score (default 0 = pas de filtrage)
|
|
45
47
|
config.rootPath = config.rootPath || process.cwd();
|
|
46
48
|
const filename = this.isSecure ? `memories-lite-${currentUserId}.db` : `memories-lite-global.db`;
|
|
47
49
|
this.dbPath = (config.rootPath == ':memory:') ? ':memory:' : path.join(config.rootPath, filename);
|
|
@@ -246,11 +248,15 @@ export class LiteVectorStore implements VectorStore {
|
|
|
246
248
|
const cosineScore = this.cosineSimilarity(query, vector);
|
|
247
249
|
const hybridScore = this.calculateHybridScore(cosineScore, payload);
|
|
248
250
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
251
|
+
//
|
|
252
|
+
// Filtrer par searchThreshold - ne retourner que les résultats au-dessus du seuil
|
|
253
|
+
if (hybridScore >= this.searchThreshold) {
|
|
254
|
+
results.push({
|
|
255
|
+
id: memoryVector.id,
|
|
256
|
+
payload: memoryVector.payload,
|
|
257
|
+
score: hybridScore,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
254
260
|
}
|
|
255
261
|
}
|
|
256
262
|
|
package/tests/init.mem.ts
CHANGED
|
@@ -6,9 +6,10 @@ dotenv.config();
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Helper to initialize MemoriesLite instance and generate a random userId.
|
|
9
|
-
* @param
|
|
9
|
+
* @param capturePrompt Optional prompt to inject into the memory config.
|
|
10
|
+
* @param searchThreshold Optional minimum score threshold for search results (0-1)
|
|
10
11
|
*/
|
|
11
|
-
export function createTestMemory({
|
|
12
|
+
export function createTestMemory({capturePrompt, dimension, rootPath, secure, searchThreshold}:any) {
|
|
12
13
|
dimension = dimension || 768;
|
|
13
14
|
const userId =
|
|
14
15
|
Math.random().toString(36).substring(2, 15) +
|
|
@@ -17,7 +18,7 @@ export function createTestMemory({customPrompt, dimension, rootPath, secure}:any
|
|
|
17
18
|
const memory = new MemoriesLite({
|
|
18
19
|
version: "v1.1",
|
|
19
20
|
disableHistory: true,
|
|
20
|
-
...(
|
|
21
|
+
...(capturePrompt ? { capturePrompt } : {}),
|
|
21
22
|
embedder: {
|
|
22
23
|
provider: "openai",
|
|
23
24
|
config: { dimension, apiKey: process.env.OPENAI_API_KEY!, model: "text-embedding-3-small" }
|
|
@@ -27,7 +28,9 @@ export function createTestMemory({customPrompt, dimension, rootPath, secure}:any
|
|
|
27
28
|
config: {
|
|
28
29
|
dimension,
|
|
29
30
|
rootPath: (rootPath || ":memory:"),
|
|
30
|
-
secure: secure || false
|
|
31
|
+
secure: secure || false,
|
|
32
|
+
searchThreshold: searchThreshold ?? 0 // Par défaut 0 (pas de filtrage) pour les tests existants
|
|
33
|
+
}
|
|
31
34
|
},
|
|
32
35
|
llm: {
|
|
33
36
|
provider: "openai",
|
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
|
});
|