memories-lite 0.99.1 → 0.99.2
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 +1 -3
- package/dist/memory/index.js +4 -36
- 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/package.json +1 -1
- package/src/config/defaults.ts +5 -19
- package/src/config/manager.ts +1 -6
- package/src/memory/index.ts +5 -44
- 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/memory.discussion.search.test.ts +377 -0
package/dist/config/defaults.js
CHANGED
|
@@ -7,18 +7,17 @@ const DEFAULT_SCORING_CONFIG = {
|
|
|
7
7
|
assistant_preference: { alpha: 0.60, beta: 0.05, gamma: 0.35, halfLifeDays: Infinity },
|
|
8
8
|
//
|
|
9
9
|
// discussion: mémoires de discussions (synthèses opérationnelles)
|
|
10
|
-
// - alpha=
|
|
11
|
-
// - beta=
|
|
10
|
+
// - alpha=1: score basé uniquement sur la similarité cosinus
|
|
11
|
+
// - beta=0: pas de recency
|
|
12
12
|
// - gamma=0: pas d'importance base
|
|
13
|
-
discussion: { alpha:
|
|
13
|
+
discussion: { alpha: 1, beta: 0, gamma: 0, halfLifeDays: Infinity },
|
|
14
14
|
//
|
|
15
15
|
// default: fallback si type manquant ou inconnu
|
|
16
16
|
default: { alpha: 0.5, beta: 0.3, gamma: 0.1, halfLifeDays: 30 }
|
|
17
17
|
};
|
|
18
18
|
exports.DEFAULT_MEMORY_CONFIG = {
|
|
19
19
|
disableHistory: true,
|
|
20
|
-
|
|
21
|
-
version: "v1.1",
|
|
20
|
+
version: "v2.0",
|
|
22
21
|
embedder: {
|
|
23
22
|
provider: "openai",
|
|
24
23
|
config: {
|
|
@@ -33,6 +32,7 @@ exports.DEFAULT_MEMORY_CONFIG = {
|
|
|
33
32
|
collectionName: "memories",
|
|
34
33
|
dimension: 768,
|
|
35
34
|
scoring: DEFAULT_SCORING_CONFIG,
|
|
35
|
+
searchThreshold: 0.50, // Seuil minimum de score pour retourner un résultat
|
|
36
36
|
},
|
|
37
37
|
},
|
|
38
38
|
llm: {
|
|
@@ -43,20 +43,6 @@ exports.DEFAULT_MEMORY_CONFIG = {
|
|
|
43
43
|
modelProperties: undefined,
|
|
44
44
|
},
|
|
45
45
|
},
|
|
46
|
-
graphStore: {
|
|
47
|
-
provider: "neo4j",
|
|
48
|
-
config: {
|
|
49
|
-
url: process.env.NEO4J_URL || "neo4j://localhost:7687",
|
|
50
|
-
username: process.env.NEO4J_USERNAME || "neo4j",
|
|
51
|
-
password: process.env.NEO4J_PASSWORD || "password",
|
|
52
|
-
},
|
|
53
|
-
llm: {
|
|
54
|
-
provider: "openai",
|
|
55
|
-
config: {
|
|
56
|
-
model: "gpt-5-mini",
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
46
|
historyStore: {
|
|
61
47
|
provider: "dummy",
|
|
62
48
|
config: {
|
package/dist/config/manager.js
CHANGED
|
@@ -96,17 +96,12 @@ class ConfigManager {
|
|
|
96
96
|
})(),
|
|
97
97
|
},
|
|
98
98
|
historyDbPath: userConfig.historyDbPath || defaults_1.DEFAULT_MEMORY_CONFIG.historyDbPath,
|
|
99
|
-
|
|
100
|
-
graphStore: {
|
|
101
|
-
...defaults_1.DEFAULT_MEMORY_CONFIG.graphStore,
|
|
102
|
-
...userConfig.graphStore,
|
|
103
|
-
},
|
|
99
|
+
capturePrompt: userConfig.capturePrompt,
|
|
104
100
|
historyStore: {
|
|
105
101
|
...defaults_1.DEFAULT_MEMORY_CONFIG.historyStore,
|
|
106
102
|
...userConfig.historyStore,
|
|
107
103
|
},
|
|
108
104
|
disableHistory: userConfig.disableHistory || defaults_1.DEFAULT_MEMORY_CONFIG.disableHistory,
|
|
109
|
-
enableGraph: userConfig.enableGraph || defaults_1.DEFAULT_MEMORY_CONFIG.enableGraph,
|
|
110
105
|
};
|
|
111
106
|
// Validate the merged config
|
|
112
107
|
return types_1.MemoryConfigSchema.parse(mergedConfig);
|
package/dist/memory/index.d.ts
CHANGED
|
@@ -3,15 +3,13 @@ import { VectorStore } from "../vectorstores/base";
|
|
|
3
3
|
import { AddMemoryOptions, SearchMemoryOptions, DeleteAllMemoryOptions, GetAllMemoryOptions } from "./memory.types";
|
|
4
4
|
export declare class MemoriesLite {
|
|
5
5
|
private config;
|
|
6
|
-
private
|
|
6
|
+
private capturePrompt;
|
|
7
7
|
private embedder;
|
|
8
8
|
private vectorStoreConfig;
|
|
9
9
|
private llm;
|
|
10
10
|
private db;
|
|
11
11
|
private collectionName;
|
|
12
12
|
private apiVersion;
|
|
13
|
-
private graphMemory?;
|
|
14
|
-
private enableGraph;
|
|
15
13
|
telemetryId: string;
|
|
16
14
|
constructor(config?: Partial<MemoryConfig>);
|
|
17
15
|
private _initializeTelemetry;
|
package/dist/memory/index.js
CHANGED
|
@@ -16,7 +16,7 @@ class MemoriesLite {
|
|
|
16
16
|
constructor(config = {}) {
|
|
17
17
|
// Merge and validate config
|
|
18
18
|
this.config = manager_1.ConfigManager.mergeConfig(config);
|
|
19
|
-
this.
|
|
19
|
+
this.capturePrompt = this.config.capturePrompt;
|
|
20
20
|
this.embedder = factory_1.EmbedderFactory.create(this.config.embedder.provider, this.config.embedder.config);
|
|
21
21
|
//
|
|
22
22
|
// vectorStore.provider is "lite"
|
|
@@ -39,12 +39,7 @@ class MemoriesLite {
|
|
|
39
39
|
}
|
|
40
40
|
this.collectionName = this.config.vectorStore.config.collectionName;
|
|
41
41
|
this.apiVersion = this.config.version || "v1.0";
|
|
42
|
-
this.enableGraph = this.config.enableGraph || false;
|
|
43
42
|
this.telemetryId = "anonymous";
|
|
44
|
-
// Initialize graph memory if configured
|
|
45
|
-
if (this.enableGraph && this.config.graphStore) {
|
|
46
|
-
// this.graphMemory = new MemoryGraph(this.config);
|
|
47
|
-
}
|
|
48
43
|
// Initialize telemetry if vector store is initialized
|
|
49
44
|
// this._initializeTelemetry();
|
|
50
45
|
}
|
|
@@ -56,7 +51,6 @@ class MemoriesLite {
|
|
|
56
51
|
api_version: this.apiVersion,
|
|
57
52
|
client_type: "Memory",
|
|
58
53
|
collection_name: this.collectionName,
|
|
59
|
-
enable_graph: this.enableGraph,
|
|
60
54
|
});
|
|
61
55
|
}
|
|
62
56
|
catch (error) { }
|
|
@@ -108,7 +102,7 @@ class MemoriesLite {
|
|
|
108
102
|
.join("\n\n");
|
|
109
103
|
//
|
|
110
104
|
// Générer la synthèse via LLM
|
|
111
|
-
const [systemPrompt, userPrompt] = (0, prompts_1.getDiscussionSynthesisMessages)(parsedMessages, capturePrompt || this.
|
|
105
|
+
const [systemPrompt, userPrompt] = (0, prompts_1.getDiscussionSynthesisMessages)(parsedMessages, capturePrompt || this.capturePrompt);
|
|
112
106
|
const response = await this.llm.generateResponse([
|
|
113
107
|
{ role: "system", content: systemPrompt },
|
|
114
108
|
{ role: "user", content: userPrompt },
|
|
@@ -134,8 +128,8 @@ class MemoriesLite {
|
|
|
134
128
|
return [];
|
|
135
129
|
}
|
|
136
130
|
//
|
|
137
|
-
// Créer l'embedding sur le
|
|
138
|
-
const embedding = await this.embedder.embed(
|
|
131
|
+
// Créer l'embedding sur le title seul (TEST COMPARATIF)
|
|
132
|
+
const embedding = await this.embedder.embed(title);
|
|
139
133
|
//
|
|
140
134
|
// Préparer les métadonnées
|
|
141
135
|
const memoryType = metadata.type || 'discussion';
|
|
@@ -192,20 +186,8 @@ class MemoriesLite {
|
|
|
192
186
|
//
|
|
193
187
|
// Générer synthèse et stocker
|
|
194
188
|
const vectorStoreResult = await this.addToVectorStore(final_parsedMessages, metadata, userId, filters, capturePrompt);
|
|
195
|
-
//
|
|
196
|
-
// Graph store (si configuré)
|
|
197
|
-
let graphResult;
|
|
198
|
-
if (this.graphMemory) {
|
|
199
|
-
try {
|
|
200
|
-
graphResult = await this.graphMemory.add(final_parsedMessages.map((m) => m.content).join("\n"), filters);
|
|
201
|
-
}
|
|
202
|
-
catch (error) {
|
|
203
|
-
console.error("Error adding to graph memory:", error);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
189
|
return {
|
|
207
190
|
results: vectorStoreResult,
|
|
208
|
-
relations: graphResult?.relations,
|
|
209
191
|
};
|
|
210
192
|
}
|
|
211
193
|
async get(memoryId, userId) {
|
|
@@ -262,16 +244,6 @@ class MemoriesLite {
|
|
|
262
244
|
// Search vector store
|
|
263
245
|
const queryEmbedding = await this.embedder.embed(query);
|
|
264
246
|
const memories = await vectorStore.search(queryEmbedding, limit, filters);
|
|
265
|
-
// Search graph store if available
|
|
266
|
-
let graphResults = [];
|
|
267
|
-
if (this.graphMemory) {
|
|
268
|
-
try {
|
|
269
|
-
graphResults = await this.graphMemory.search(query, filters);
|
|
270
|
-
}
|
|
271
|
-
catch (error) {
|
|
272
|
-
console.error("Error searching graph memory:", error);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
247
|
const excludedKeys = new Set([
|
|
276
248
|
"userId",
|
|
277
249
|
"agentId",
|
|
@@ -297,7 +269,6 @@ class MemoriesLite {
|
|
|
297
269
|
}));
|
|
298
270
|
return {
|
|
299
271
|
results,
|
|
300
|
-
relations: graphResults,
|
|
301
272
|
};
|
|
302
273
|
}
|
|
303
274
|
async update(memoryId, data, userId) {
|
|
@@ -346,9 +317,6 @@ class MemoriesLite {
|
|
|
346
317
|
await this.db.reset();
|
|
347
318
|
// Check provider before attempting deleteCol
|
|
348
319
|
await vectorStore.deleteCol();
|
|
349
|
-
if (this.graphMemory) {
|
|
350
|
-
await this.graphMemory.deleteAll({ userId: "default" }); // Assuming this is okay, or needs similar check?
|
|
351
|
-
}
|
|
352
320
|
// Re-initialize factories/clients based on the original config
|
|
353
321
|
this.embedder = factory_1.EmbedderFactory.create(this.config.embedder.provider, this.config.embedder.config);
|
|
354
322
|
this.llm = factory_1.LLMFactory.create(this.config.llm.provider, this.config.llm.config);
|
package/dist/prompts/index.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare const DiscussionSynthesisSchema: z.ZodObject<{
|
|
|
18
18
|
* Prompt par défaut pour la synthèse de discussion
|
|
19
19
|
* Peut être remplacé via capturePrompt dans AddMemoryOptions
|
|
20
20
|
*/
|
|
21
|
-
export declare const DEFAULT_DISCUSSION_PROMPT = "Tu es un expert en synth\u00E8se op\u00E9rationnelle.\n\n\u00C0 partir de cette discussion, g\u00E9n\u00E8re :\n1. TITRE: Un titre court et descriptif (
|
|
21
|
+
export declare const DEFAULT_DISCUSSION_PROMPT = "Tu es un expert en synth\u00E8se op\u00E9rationnelle.\n\n\u00C0 partir de cette discussion, g\u00E9n\u00E8re :\n1. TITRE: Un titre court et descriptif (10-20 mots) qui capture l'essence de la demande\n2. SUMMARY: Les points cl\u00E9s du chemin de r\u00E9solution en markdown (max 150 mots)\n\nCette synth\u00E8se servira \u00E0 retrouver et r\u00E9appliquer ce pattern de r\u00E9solution similaire.\nUtilise la m\u00EAme langue que la discussion.\n\nDiscussion \u00E0 synth\u00E9tiser:\n";
|
|
22
22
|
/**
|
|
23
23
|
* Génère les messages pour la synthèse de discussion
|
|
24
24
|
* @param discussion - Contenu de la discussion formatée
|
package/dist/prompts/index.js
CHANGED
|
@@ -25,8 +25,8 @@ exports.DiscussionSynthesisSchema = zod_1.z.object({
|
|
|
25
25
|
exports.DEFAULT_DISCUSSION_PROMPT = `Tu es un expert en synthèse opérationnelle.
|
|
26
26
|
|
|
27
27
|
À partir de cette discussion, génère :
|
|
28
|
-
1. TITRE: Un titre court et descriptif (
|
|
29
|
-
2. SUMMARY: Les points clés du chemin de résolution (
|
|
28
|
+
1. TITRE: Un titre court et descriptif (10-20 mots) qui capture l'essence de la demande
|
|
29
|
+
2. SUMMARY: Les points clés du chemin de résolution en markdown (max 150 mots)
|
|
30
30
|
|
|
31
31
|
Cette synthèse servira à retrouver et réappliquer ce pattern de résolution similaire.
|
|
32
32
|
Utilise la même langue que la discussion.
|
package/dist/types/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export interface VectorStoreConfig {
|
|
|
26
26
|
cacheTtl?: number;
|
|
27
27
|
scoring?: MemoryScoringConfig;
|
|
28
28
|
recencyCleanupThreshold?: number;
|
|
29
|
+
searchThreshold?: number;
|
|
29
30
|
[key: string]: any;
|
|
30
31
|
}
|
|
31
32
|
export interface HistoryStoreConfig {
|
|
@@ -44,17 +45,6 @@ export interface LLMConfig {
|
|
|
44
45
|
model?: string | any;
|
|
45
46
|
modelProperties?: Record<string, any>;
|
|
46
47
|
}
|
|
47
|
-
export interface Neo4jConfig {
|
|
48
|
-
url: string;
|
|
49
|
-
username: string;
|
|
50
|
-
password: string;
|
|
51
|
-
}
|
|
52
|
-
export interface GraphStoreConfig {
|
|
53
|
-
provider: string;
|
|
54
|
-
config: Neo4jConfig;
|
|
55
|
-
llm?: LLMConfig;
|
|
56
|
-
customPrompt?: string;
|
|
57
|
-
}
|
|
58
48
|
export interface MemoryTypeConfig {
|
|
59
49
|
alpha: number;
|
|
60
50
|
beta: number;
|
|
@@ -84,9 +74,7 @@ export interface MemoryConfig {
|
|
|
84
74
|
historyStore?: HistoryStoreConfig;
|
|
85
75
|
disableHistory?: boolean;
|
|
86
76
|
historyDbPath?: string;
|
|
87
|
-
|
|
88
|
-
graphStore?: GraphStoreConfig;
|
|
89
|
-
enableGraph?: boolean;
|
|
77
|
+
capturePrompt?: string;
|
|
90
78
|
}
|
|
91
79
|
export type MemoryType = 'assistant_preference' | 'discussion';
|
|
92
80
|
export interface MemoryItem {
|
|
@@ -256,6 +244,7 @@ export declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
256
244
|
};
|
|
257
245
|
}>>;
|
|
258
246
|
recencyCleanupThreshold: z.ZodOptional<z.ZodNumber>;
|
|
247
|
+
searchThreshold: z.ZodOptional<z.ZodNumber>;
|
|
259
248
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
260
249
|
collectionName: z.ZodOptional<z.ZodString>;
|
|
261
250
|
dimension: z.ZodOptional<z.ZodNumber>;
|
|
@@ -349,6 +338,7 @@ export declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
349
338
|
};
|
|
350
339
|
}>>;
|
|
351
340
|
recencyCleanupThreshold: z.ZodOptional<z.ZodNumber>;
|
|
341
|
+
searchThreshold: z.ZodOptional<z.ZodNumber>;
|
|
352
342
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
353
343
|
collectionName: z.ZodOptional<z.ZodString>;
|
|
354
344
|
dimension: z.ZodOptional<z.ZodNumber>;
|
|
@@ -442,6 +432,7 @@ export declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
442
432
|
};
|
|
443
433
|
}>>;
|
|
444
434
|
recencyCleanupThreshold: z.ZodOptional<z.ZodNumber>;
|
|
435
|
+
searchThreshold: z.ZodOptional<z.ZodNumber>;
|
|
445
436
|
}, z.ZodTypeAny, "passthrough">>;
|
|
446
437
|
}, "strip", z.ZodTypeAny, {
|
|
447
438
|
provider: string;
|
|
@@ -470,6 +461,7 @@ export declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
470
461
|
};
|
|
471
462
|
} | undefined;
|
|
472
463
|
recencyCleanupThreshold?: number | undefined;
|
|
464
|
+
searchThreshold?: number | undefined;
|
|
473
465
|
} & {
|
|
474
466
|
[k: string]: unknown;
|
|
475
467
|
};
|
|
@@ -500,6 +492,7 @@ export declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
500
492
|
};
|
|
501
493
|
} | undefined;
|
|
502
494
|
recencyCleanupThreshold?: number | undefined;
|
|
495
|
+
searchThreshold?: number | undefined;
|
|
503
496
|
} & {
|
|
504
497
|
[k: string]: unknown;
|
|
505
498
|
};
|
|
@@ -535,59 +528,7 @@ export declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
535
528
|
};
|
|
536
529
|
}>;
|
|
537
530
|
historyDbPath: z.ZodOptional<z.ZodString>;
|
|
538
|
-
|
|
539
|
-
enableGraph: z.ZodOptional<z.ZodBoolean>;
|
|
540
|
-
graphStore: z.ZodOptional<z.ZodObject<{
|
|
541
|
-
provider: z.ZodString;
|
|
542
|
-
config: z.ZodObject<{
|
|
543
|
-
url: z.ZodString;
|
|
544
|
-
username: z.ZodString;
|
|
545
|
-
password: z.ZodString;
|
|
546
|
-
}, "strip", z.ZodTypeAny, {
|
|
547
|
-
url: string;
|
|
548
|
-
username: string;
|
|
549
|
-
password: string;
|
|
550
|
-
}, {
|
|
551
|
-
url: string;
|
|
552
|
-
username: string;
|
|
553
|
-
password: string;
|
|
554
|
-
}>;
|
|
555
|
-
llm: z.ZodOptional<z.ZodObject<{
|
|
556
|
-
provider: z.ZodString;
|
|
557
|
-
config: z.ZodRecord<z.ZodString, z.ZodAny>;
|
|
558
|
-
}, "strip", z.ZodTypeAny, {
|
|
559
|
-
provider: string;
|
|
560
|
-
config: Record<string, any>;
|
|
561
|
-
}, {
|
|
562
|
-
provider: string;
|
|
563
|
-
config: Record<string, any>;
|
|
564
|
-
}>>;
|
|
565
|
-
customPrompt: z.ZodOptional<z.ZodString>;
|
|
566
|
-
}, "strip", z.ZodTypeAny, {
|
|
567
|
-
provider: string;
|
|
568
|
-
config: {
|
|
569
|
-
url: string;
|
|
570
|
-
username: string;
|
|
571
|
-
password: string;
|
|
572
|
-
};
|
|
573
|
-
llm?: {
|
|
574
|
-
provider: string;
|
|
575
|
-
config: Record<string, any>;
|
|
576
|
-
} | undefined;
|
|
577
|
-
customPrompt?: string | undefined;
|
|
578
|
-
}, {
|
|
579
|
-
provider: string;
|
|
580
|
-
config: {
|
|
581
|
-
url: string;
|
|
582
|
-
username: string;
|
|
583
|
-
password: string;
|
|
584
|
-
};
|
|
585
|
-
llm?: {
|
|
586
|
-
provider: string;
|
|
587
|
-
config: Record<string, any>;
|
|
588
|
-
} | undefined;
|
|
589
|
-
customPrompt?: string | undefined;
|
|
590
|
-
}>>;
|
|
531
|
+
capturePrompt: z.ZodOptional<z.ZodString>;
|
|
591
532
|
historyStore: z.ZodOptional<z.ZodObject<{
|
|
592
533
|
provider: z.ZodString;
|
|
593
534
|
config: z.ZodRecord<z.ZodString, z.ZodAny>;
|
|
@@ -636,6 +577,7 @@ export declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
636
577
|
};
|
|
637
578
|
} | undefined;
|
|
638
579
|
recencyCleanupThreshold?: number | undefined;
|
|
580
|
+
searchThreshold?: number | undefined;
|
|
639
581
|
} & {
|
|
640
582
|
[k: string]: unknown;
|
|
641
583
|
};
|
|
@@ -650,21 +592,7 @@ export declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
650
592
|
};
|
|
651
593
|
version?: string | undefined;
|
|
652
594
|
historyDbPath?: string | undefined;
|
|
653
|
-
|
|
654
|
-
enableGraph?: boolean | undefined;
|
|
655
|
-
graphStore?: {
|
|
656
|
-
provider: string;
|
|
657
|
-
config: {
|
|
658
|
-
url: string;
|
|
659
|
-
username: string;
|
|
660
|
-
password: string;
|
|
661
|
-
};
|
|
662
|
-
llm?: {
|
|
663
|
-
provider: string;
|
|
664
|
-
config: Record<string, any>;
|
|
665
|
-
} | undefined;
|
|
666
|
-
customPrompt?: string | undefined;
|
|
667
|
-
} | undefined;
|
|
595
|
+
capturePrompt?: string | undefined;
|
|
668
596
|
historyStore?: {
|
|
669
597
|
provider: string;
|
|
670
598
|
config: Record<string, any>;
|
|
@@ -707,6 +635,7 @@ export declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
707
635
|
};
|
|
708
636
|
} | undefined;
|
|
709
637
|
recencyCleanupThreshold?: number | undefined;
|
|
638
|
+
searchThreshold?: number | undefined;
|
|
710
639
|
} & {
|
|
711
640
|
[k: string]: unknown;
|
|
712
641
|
};
|
|
@@ -721,21 +650,7 @@ export declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
721
650
|
};
|
|
722
651
|
version?: string | undefined;
|
|
723
652
|
historyDbPath?: string | undefined;
|
|
724
|
-
|
|
725
|
-
enableGraph?: boolean | undefined;
|
|
726
|
-
graphStore?: {
|
|
727
|
-
provider: string;
|
|
728
|
-
config: {
|
|
729
|
-
url: string;
|
|
730
|
-
username: string;
|
|
731
|
-
password: string;
|
|
732
|
-
};
|
|
733
|
-
llm?: {
|
|
734
|
-
provider: string;
|
|
735
|
-
config: Record<string, any>;
|
|
736
|
-
} | undefined;
|
|
737
|
-
customPrompt?: string | undefined;
|
|
738
|
-
} | undefined;
|
|
653
|
+
capturePrompt?: string | undefined;
|
|
739
654
|
historyStore?: {
|
|
740
655
|
provider: string;
|
|
741
656
|
config: Record<string, any>;
|
package/dist/types/index.js
CHANGED
|
@@ -41,6 +41,7 @@ exports.MemoryConfigSchema = zod_1.z.object({
|
|
|
41
41
|
}),
|
|
42
42
|
}).optional(),
|
|
43
43
|
recencyCleanupThreshold: zod_1.z.number().min(0).max(1).optional(),
|
|
44
|
+
searchThreshold: zod_1.z.number().min(0).max(1).optional(),
|
|
44
45
|
})
|
|
45
46
|
.passthrough(),
|
|
46
47
|
}),
|
|
@@ -53,25 +54,7 @@ exports.MemoryConfigSchema = zod_1.z.object({
|
|
|
53
54
|
}),
|
|
54
55
|
}),
|
|
55
56
|
historyDbPath: zod_1.z.string().optional(),
|
|
56
|
-
|
|
57
|
-
enableGraph: zod_1.z.boolean().optional(),
|
|
58
|
-
graphStore: zod_1.z
|
|
59
|
-
.object({
|
|
60
|
-
provider: zod_1.z.string(),
|
|
61
|
-
config: zod_1.z.object({
|
|
62
|
-
url: zod_1.z.string(),
|
|
63
|
-
username: zod_1.z.string(),
|
|
64
|
-
password: zod_1.z.string(),
|
|
65
|
-
}),
|
|
66
|
-
llm: zod_1.z
|
|
67
|
-
.object({
|
|
68
|
-
provider: zod_1.z.string(),
|
|
69
|
-
config: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
|
|
70
|
-
})
|
|
71
|
-
.optional(),
|
|
72
|
-
customPrompt: zod_1.z.string().optional(),
|
|
73
|
-
})
|
|
74
|
-
.optional(),
|
|
57
|
+
capturePrompt: zod_1.z.string().optional(),
|
|
75
58
|
historyStore: zod_1.z
|
|
76
59
|
.object({
|
|
77
60
|
provider: zod_1.z.string(),
|
|
@@ -18,6 +18,7 @@ export declare class LiteVectorStore implements VectorStore {
|
|
|
18
18
|
private currentUserId;
|
|
19
19
|
private scoringConfig;
|
|
20
20
|
private cleanupThreshold?;
|
|
21
|
+
private searchThreshold;
|
|
21
22
|
private static cache;
|
|
22
23
|
constructor(config: VectorStoreConfig, currentUserId: string);
|
|
23
24
|
private init;
|
|
@@ -28,7 +28,8 @@ class LiteVectorStore {
|
|
|
28
28
|
this.currentUserId = currentUserId;
|
|
29
29
|
this.isSecure = config.secure || false;
|
|
30
30
|
this.scoringConfig = config.scoring;
|
|
31
|
-
this.cleanupThreshold = config.recencyCleanupThreshold || 0.25; // (default 0.25 means 2 times the half-life
|
|
31
|
+
this.cleanupThreshold = config.recencyCleanupThreshold || 0.25; // (default 0.25 means 2 times the half-life)
|
|
32
|
+
this.searchThreshold = config.searchThreshold ?? 0; // Seuil de score (default 0 = pas de filtrage)
|
|
32
33
|
config.rootPath = config.rootPath || process.cwd();
|
|
33
34
|
const filename = this.isSecure ? `memories-lite-${currentUserId}.db` : `memories-lite-global.db`;
|
|
34
35
|
this.dbPath = (config.rootPath == ':memory:') ? ':memory:' : path_1.default.join(config.rootPath, filename);
|
|
@@ -195,11 +196,15 @@ class LiteVectorStore {
|
|
|
195
196
|
if (this.filterVector(memoryVector, filters)) {
|
|
196
197
|
const cosineScore = this.cosineSimilarity(query, vector);
|
|
197
198
|
const hybridScore = this.calculateHybridScore(cosineScore, payload);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
199
|
+
//
|
|
200
|
+
// Filtrer par searchThreshold - ne retourner que les résultats au-dessus du seuil
|
|
201
|
+
if (hybridScore >= this.searchThreshold) {
|
|
202
|
+
results.push({
|
|
203
|
+
id: memoryVector.id,
|
|
204
|
+
payload: memoryVector.payload,
|
|
205
|
+
score: hybridScore,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
203
208
|
}
|
|
204
209
|
}
|
|
205
210
|
results.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
|
package/package.json
CHANGED
package/src/config/defaults.ts
CHANGED
|
@@ -6,10 +6,10 @@ const DEFAULT_SCORING_CONFIG: MemoryScoringConfig = {
|
|
|
6
6
|
assistant_preference: { alpha: 0.60, beta: 0.05, gamma: 0.35, halfLifeDays: Infinity },
|
|
7
7
|
//
|
|
8
8
|
// discussion: mémoires de discussions (synthèses opérationnelles)
|
|
9
|
-
// - alpha=
|
|
10
|
-
// - beta=
|
|
9
|
+
// - alpha=1: score basé uniquement sur la similarité cosinus
|
|
10
|
+
// - beta=0: pas de recency
|
|
11
11
|
// - gamma=0: pas d'importance base
|
|
12
|
-
discussion: { alpha:
|
|
12
|
+
discussion: { alpha: 1, beta: 0, gamma: 0, halfLifeDays: Infinity },
|
|
13
13
|
//
|
|
14
14
|
// default: fallback si type manquant ou inconnu
|
|
15
15
|
default: { alpha: 0.5, beta: 0.3, gamma: 0.1, halfLifeDays: 30 }
|
|
@@ -17,8 +17,7 @@ const DEFAULT_SCORING_CONFIG: MemoryScoringConfig = {
|
|
|
17
17
|
|
|
18
18
|
export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
|
|
19
19
|
disableHistory: true,
|
|
20
|
-
|
|
21
|
-
version: "v1.1",
|
|
20
|
+
version: "v2.0",
|
|
22
21
|
embedder: {
|
|
23
22
|
provider: "openai",
|
|
24
23
|
config: {
|
|
@@ -33,6 +32,7 @@ export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
|
|
|
33
32
|
collectionName: "memories",
|
|
34
33
|
dimension: 768,
|
|
35
34
|
scoring: DEFAULT_SCORING_CONFIG,
|
|
35
|
+
searchThreshold: 0.50, // Seuil minimum de score pour retourner un résultat
|
|
36
36
|
},
|
|
37
37
|
},
|
|
38
38
|
llm: {
|
|
@@ -43,20 +43,6 @@ export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
|
|
|
43
43
|
modelProperties: undefined,
|
|
44
44
|
},
|
|
45
45
|
},
|
|
46
|
-
graphStore: {
|
|
47
|
-
provider: "neo4j",
|
|
48
|
-
config: {
|
|
49
|
-
url: process.env.NEO4J_URL || "neo4j://localhost:7687",
|
|
50
|
-
username: process.env.NEO4J_USERNAME || "neo4j",
|
|
51
|
-
password: process.env.NEO4J_PASSWORD || "password",
|
|
52
|
-
},
|
|
53
|
-
llm: {
|
|
54
|
-
provider: "openai",
|
|
55
|
-
config: {
|
|
56
|
-
model: "gpt-5-mini",
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
46
|
historyStore: {
|
|
61
47
|
provider: "dummy",
|
|
62
48
|
config: {
|
package/src/config/manager.ts
CHANGED
|
@@ -106,18 +106,13 @@ export class ConfigManager {
|
|
|
106
106
|
},
|
|
107
107
|
historyDbPath:
|
|
108
108
|
userConfig.historyDbPath || DEFAULT_MEMORY_CONFIG.historyDbPath,
|
|
109
|
-
|
|
110
|
-
graphStore: {
|
|
111
|
-
...DEFAULT_MEMORY_CONFIG.graphStore,
|
|
112
|
-
...userConfig.graphStore,
|
|
113
|
-
},
|
|
109
|
+
capturePrompt: userConfig.capturePrompt,
|
|
114
110
|
historyStore: {
|
|
115
111
|
...DEFAULT_MEMORY_CONFIG.historyStore,
|
|
116
112
|
...userConfig.historyStore,
|
|
117
113
|
},
|
|
118
114
|
disableHistory:
|
|
119
115
|
userConfig.disableHistory || DEFAULT_MEMORY_CONFIG.disableHistory,
|
|
120
|
-
enableGraph: userConfig.enableGraph || DEFAULT_MEMORY_CONFIG.enableGraph,
|
|
121
116
|
};
|
|
122
117
|
|
|
123
118
|
// Validate the merged config
|
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
|
|
@@ -303,23 +294,8 @@ export class MemoriesLite {
|
|
|
303
294
|
capturePrompt,
|
|
304
295
|
);
|
|
305
296
|
|
|
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
|
-
);
|
|
315
|
-
} catch (error) {
|
|
316
|
-
console.error("Error adding to graph memory:", error);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
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,7 +400,6 @@ export class MemoriesLite {
|
|
|
434
400
|
|
|
435
401
|
return {
|
|
436
402
|
results,
|
|
437
|
-
relations: graphResults,
|
|
438
403
|
};
|
|
439
404
|
}
|
|
440
405
|
|
|
@@ -496,10 +461,6 @@ export class MemoriesLite {
|
|
|
496
461
|
// Check provider before attempting deleteCol
|
|
497
462
|
await vectorStore.deleteCol();
|
|
498
463
|
|
|
499
|
-
if (this.graphMemory) {
|
|
500
|
-
await this.graphMemory.deleteAll({ userId: "default" }); // Assuming this is okay, or needs similar check?
|
|
501
|
-
}
|
|
502
|
-
|
|
503
464
|
// Re-initialize factories/clients based on the original config
|
|
504
465
|
this.embedder = EmbedderFactory.create(
|
|
505
466
|
this.config.embedder.provider,
|
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",
|
|
@@ -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
|
+
|