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.
@@ -40,22 +40,20 @@ import { zodResponseFormat } from "openai/helpers/zod";
40
40
 
41
41
  export class MemoriesLite {
42
42
  private config: MemoryConfig;
43
- private customPrompt: string | undefined;
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.customPrompt = this.config.customPrompt;
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.customPrompt
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 summary (pour recherche sémantique)
216
- const embedding = await this.embedder.embed(summary);
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
- id: memoryId,
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(memoryId: string, data: string, userId: string): Promise<{ message: string }> {
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 }, {}, userId);
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
- const newMetadata = {
610
- ...metadata,
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
- ...(existingMemory.payload.agentId && {
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);
@@ -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 (6-10 mots) qui capture l'essence de la demande
25
- 2. SUMMARY: Les points clés du chemin de résolution (50-100 mots)
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.
@@ -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
- customPrompt?: string;
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
- customPrompt: z.string().optional(),
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(),
@@ -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; // Store the threshold
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
- results.push({
250
- id: memoryVector.id,
251
- payload: memoryVector.payload,
252
- score: hybridScore,
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 customPrompt Optional prompt to inject into the memory config.
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({customPrompt, dimension, rootPath, secure}:any) {
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
- ...(customPrompt ? { customPrompt } : {}),
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",
@@ -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
- const cosineScore = 0.9;
135
+ const cosineScore = 0.9;
136
136
  const expectedRecency = 1.0; // Infinity halfLife → recency = 1 even for old dates
137
- const hybridScore = (store as any).calculateHybridScore(cosineScore, payload);
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
- 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);
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
- 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);
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
- const cosineScore = -0.5;
162
- const hybridScore = (store as any).calculateHybridScore(cosineScore, payload);
163
- expect(hybridScore).toBeGreaterThanOrEqual(0);
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
  });