memory-braid 0.4.7 → 0.6.0
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/README.md +120 -4
- package/openclaw.plugin.json +32 -0
- package/package.json +1 -1
- package/src/capture.ts +315 -0
- package/src/config.ts +127 -2
- package/src/extract.ts +8 -1
- package/src/index.ts +1288 -188
- package/src/local-memory.ts +9 -4
- package/src/logger.ts +6 -1
- package/src/mem0-client.ts +295 -45
- package/src/observability.ts +269 -0
- package/src/remediation.ts +257 -0
- package/src/state.ts +39 -0
- package/src/types.ts +74 -0
package/src/config.ts
CHANGED
|
@@ -11,6 +11,16 @@ export type MemoryBraidConfig = {
|
|
|
11
11
|
recall: {
|
|
12
12
|
maxResults: number;
|
|
13
13
|
injectTopK: number;
|
|
14
|
+
user: {
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
injectTopK: number;
|
|
17
|
+
};
|
|
18
|
+
agent: {
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
injectTopK: number;
|
|
21
|
+
minScore: number;
|
|
22
|
+
onlyPlanning: boolean;
|
|
23
|
+
};
|
|
14
24
|
merge: {
|
|
15
25
|
strategy: "rrf";
|
|
16
26
|
rrfK: number;
|
|
@@ -23,6 +33,16 @@ export type MemoryBraidConfig = {
|
|
|
23
33
|
mode: "local" | "hybrid" | "ml";
|
|
24
34
|
includeAssistant: boolean;
|
|
25
35
|
maxItemsPerRun: number;
|
|
36
|
+
assistant: {
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
autoCapture: boolean;
|
|
39
|
+
explicitTool: boolean;
|
|
40
|
+
maxItemsPerRun: number;
|
|
41
|
+
minUtilityScore: number;
|
|
42
|
+
minNoveltyScore: number;
|
|
43
|
+
maxWritesPerSessionWindow: number;
|
|
44
|
+
cooldownMinutes: number;
|
|
45
|
+
};
|
|
26
46
|
ml: {
|
|
27
47
|
provider?: "openai" | "anthropic" | "gemini";
|
|
28
48
|
model?: string;
|
|
@@ -80,6 +100,16 @@ const DEFAULTS: MemoryBraidConfig = {
|
|
|
80
100
|
recall: {
|
|
81
101
|
maxResults: 8,
|
|
82
102
|
injectTopK: 5,
|
|
103
|
+
user: {
|
|
104
|
+
enabled: true,
|
|
105
|
+
injectTopK: 5,
|
|
106
|
+
},
|
|
107
|
+
agent: {
|
|
108
|
+
enabled: true,
|
|
109
|
+
injectTopK: 2,
|
|
110
|
+
minScore: 0.78,
|
|
111
|
+
onlyPlanning: true,
|
|
112
|
+
},
|
|
83
113
|
merge: {
|
|
84
114
|
strategy: "rrf",
|
|
85
115
|
rrfK: 60,
|
|
@@ -92,6 +122,16 @@ const DEFAULTS: MemoryBraidConfig = {
|
|
|
92
122
|
mode: "local",
|
|
93
123
|
includeAssistant: false,
|
|
94
124
|
maxItemsPerRun: 6,
|
|
125
|
+
assistant: {
|
|
126
|
+
enabled: true,
|
|
127
|
+
autoCapture: false,
|
|
128
|
+
explicitTool: true,
|
|
129
|
+
maxItemsPerRun: 2,
|
|
130
|
+
minUtilityScore: 0.8,
|
|
131
|
+
minNoveltyScore: 0.85,
|
|
132
|
+
maxWritesPerSessionWindow: 3,
|
|
133
|
+
cooldownMinutes: 5,
|
|
134
|
+
},
|
|
95
135
|
ml: {
|
|
96
136
|
provider: undefined,
|
|
97
137
|
model: undefined,
|
|
@@ -168,8 +208,11 @@ export function parseConfig(raw: unknown): MemoryBraidConfig {
|
|
|
168
208
|
const root = asRecord(raw);
|
|
169
209
|
const mem0 = asRecord(root.mem0);
|
|
170
210
|
const recall = asRecord(root.recall);
|
|
211
|
+
const recallUser = asRecord(recall.user);
|
|
212
|
+
const recallAgent = asRecord(recall.agent);
|
|
171
213
|
const merge = asRecord(recall.merge);
|
|
172
214
|
const capture = asRecord(root.capture);
|
|
215
|
+
const captureAssistant = asRecord(capture.assistant);
|
|
173
216
|
const entityExtraction = asRecord(root.entityExtraction);
|
|
174
217
|
const entityStartup = asRecord(entityExtraction.startup);
|
|
175
218
|
const ml = asRecord(capture.ml);
|
|
@@ -194,6 +237,16 @@ export function parseConfig(raw: unknown): MemoryBraidConfig {
|
|
|
194
237
|
? parsedEntityModel
|
|
195
238
|
: "gpt-4o-mini"
|
|
196
239
|
: parsedEntityModel ?? DEFAULTS.entityExtraction.model;
|
|
240
|
+
const includeAssistant = asBoolean(
|
|
241
|
+
capture.includeAssistant,
|
|
242
|
+
DEFAULTS.capture.includeAssistant,
|
|
243
|
+
);
|
|
244
|
+
const legacyInjectTopK = asInt(
|
|
245
|
+
recall.injectTopK,
|
|
246
|
+
DEFAULTS.recall.injectTopK,
|
|
247
|
+
1,
|
|
248
|
+
20,
|
|
249
|
+
);
|
|
197
250
|
|
|
198
251
|
return {
|
|
199
252
|
enabled: asBoolean(root.enabled, DEFAULTS.enabled),
|
|
@@ -207,7 +260,35 @@ export function parseConfig(raw: unknown): MemoryBraidConfig {
|
|
|
207
260
|
},
|
|
208
261
|
recall: {
|
|
209
262
|
maxResults: asInt(recall.maxResults, DEFAULTS.recall.maxResults, 1, 50),
|
|
210
|
-
injectTopK:
|
|
263
|
+
injectTopK: legacyInjectTopK,
|
|
264
|
+
user: {
|
|
265
|
+
enabled: asBoolean(recallUser.enabled, DEFAULTS.recall.user.enabled),
|
|
266
|
+
injectTopK: asInt(
|
|
267
|
+
recallUser.injectTopK,
|
|
268
|
+
legacyInjectTopK,
|
|
269
|
+
1,
|
|
270
|
+
20,
|
|
271
|
+
),
|
|
272
|
+
},
|
|
273
|
+
agent: {
|
|
274
|
+
enabled: asBoolean(recallAgent.enabled, DEFAULTS.recall.agent.enabled),
|
|
275
|
+
injectTopK: asInt(
|
|
276
|
+
recallAgent.injectTopK,
|
|
277
|
+
DEFAULTS.recall.agent.injectTopK,
|
|
278
|
+
1,
|
|
279
|
+
20,
|
|
280
|
+
),
|
|
281
|
+
minScore: asNumber(
|
|
282
|
+
recallAgent.minScore,
|
|
283
|
+
DEFAULTS.recall.agent.minScore,
|
|
284
|
+
0,
|
|
285
|
+
1,
|
|
286
|
+
),
|
|
287
|
+
onlyPlanning: asBoolean(
|
|
288
|
+
recallAgent.onlyPlanning,
|
|
289
|
+
DEFAULTS.recall.agent.onlyPlanning,
|
|
290
|
+
),
|
|
291
|
+
},
|
|
211
292
|
merge: {
|
|
212
293
|
strategy: "rrf",
|
|
213
294
|
rrfK: asInt(merge.rrfK, DEFAULTS.recall.merge.rrfK, 1, 500),
|
|
@@ -218,8 +299,52 @@ export function parseConfig(raw: unknown): MemoryBraidConfig {
|
|
|
218
299
|
capture: {
|
|
219
300
|
enabled: asBoolean(capture.enabled, DEFAULTS.capture.enabled),
|
|
220
301
|
mode: captureMode,
|
|
221
|
-
includeAssistant
|
|
302
|
+
includeAssistant,
|
|
222
303
|
maxItemsPerRun: asInt(capture.maxItemsPerRun, DEFAULTS.capture.maxItemsPerRun, 1, 50),
|
|
304
|
+
assistant: {
|
|
305
|
+
enabled: asBoolean(
|
|
306
|
+
captureAssistant.enabled,
|
|
307
|
+
DEFAULTS.capture.assistant.enabled,
|
|
308
|
+
),
|
|
309
|
+
autoCapture: asBoolean(
|
|
310
|
+
captureAssistant.autoCapture,
|
|
311
|
+
includeAssistant,
|
|
312
|
+
),
|
|
313
|
+
explicitTool: asBoolean(
|
|
314
|
+
captureAssistant.explicitTool,
|
|
315
|
+
DEFAULTS.capture.assistant.explicitTool,
|
|
316
|
+
),
|
|
317
|
+
maxItemsPerRun: asInt(
|
|
318
|
+
captureAssistant.maxItemsPerRun,
|
|
319
|
+
DEFAULTS.capture.assistant.maxItemsPerRun,
|
|
320
|
+
1,
|
|
321
|
+
10,
|
|
322
|
+
),
|
|
323
|
+
minUtilityScore: asNumber(
|
|
324
|
+
captureAssistant.minUtilityScore,
|
|
325
|
+
DEFAULTS.capture.assistant.minUtilityScore,
|
|
326
|
+
0,
|
|
327
|
+
1,
|
|
328
|
+
),
|
|
329
|
+
minNoveltyScore: asNumber(
|
|
330
|
+
captureAssistant.minNoveltyScore,
|
|
331
|
+
DEFAULTS.capture.assistant.minNoveltyScore,
|
|
332
|
+
0,
|
|
333
|
+
1,
|
|
334
|
+
),
|
|
335
|
+
maxWritesPerSessionWindow: asInt(
|
|
336
|
+
captureAssistant.maxWritesPerSessionWindow,
|
|
337
|
+
DEFAULTS.capture.assistant.maxWritesPerSessionWindow,
|
|
338
|
+
1,
|
|
339
|
+
20,
|
|
340
|
+
),
|
|
341
|
+
cooldownMinutes: asInt(
|
|
342
|
+
captureAssistant.cooldownMinutes,
|
|
343
|
+
DEFAULTS.capture.assistant.cooldownMinutes,
|
|
344
|
+
0,
|
|
345
|
+
240,
|
|
346
|
+
),
|
|
347
|
+
},
|
|
223
348
|
ml: {
|
|
224
349
|
provider:
|
|
225
350
|
ml.provider === "openai" || ml.provider === "anthropic" || ml.provider === "gemini"
|
package/src/extract.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { normalizeForHash, normalizeWhitespace, sha256 } from "./chunking.js";
|
|
2
2
|
import type { MemoryBraidConfig } from "./config.js";
|
|
3
|
+
import { isLikelyTranscriptLikeText, isOversizedAtomicMemory } from "./capture.js";
|
|
3
4
|
import { MemoryBraidLogger } from "./logger.js";
|
|
4
5
|
import type { ExtractedCandidate } from "./types.js";
|
|
5
6
|
|
|
@@ -18,6 +19,9 @@ const FEED_TAG_PATTERN = /\[(?:n8n|rss|alert|news|cron|slack|discord|telegram|em
|
|
|
18
19
|
const ROLE_LABEL_PATTERN = /\b(?:assistant|system|tool|developer)\s*:/gi;
|
|
19
20
|
|
|
20
21
|
function isLikelyFeedOrImportedText(text: string): boolean {
|
|
22
|
+
if (isLikelyTranscriptLikeText(text) || isOversizedAtomicMemory(text)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
21
25
|
if (FEED_TAG_PATTERN.test(text)) {
|
|
22
26
|
return true;
|
|
23
27
|
}
|
|
@@ -398,6 +402,9 @@ function applyMlExtractionResult(
|
|
|
398
402
|
if (!text || text.length < 20 || text.length > 3000) {
|
|
399
403
|
continue;
|
|
400
404
|
}
|
|
405
|
+
if (isLikelyFeedOrImportedText(text)) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
401
408
|
const key = sha256(normalizeForHash(text));
|
|
402
409
|
if (seen.has(key)) {
|
|
403
410
|
continue;
|
|
@@ -425,7 +432,7 @@ export async function extractCandidates(params: {
|
|
|
425
432
|
runId?: string;
|
|
426
433
|
}): Promise<ExtractedCandidate[]> {
|
|
427
434
|
const normalized = normalizeMessages(params.messages);
|
|
428
|
-
const captureFromAssistant = params.cfg.capture.
|
|
435
|
+
const captureFromAssistant = params.cfg.capture.assistant.autoCapture;
|
|
429
436
|
const candidatesInput = normalized.filter((message) =>
|
|
430
437
|
captureFromAssistant
|
|
431
438
|
? message.role === "user" || message.role === "assistant"
|