@mastra/memory 0.10.1 → 0.10.2-alpha.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/.turbo/turbo-build.log +3 -20
- package/CHANGELOG.md +9 -0
- package/dist/_tsup-dts-rollup.d.cts +11 -18
- package/dist/_tsup-dts-rollup.d.ts +11 -18
- package/dist/index.cjs +86 -135
- package/dist/index.js +86 -135
- package/package.json +3 -3
- package/src/index.ts +125 -136
- package/src/processors/index.test.ts +10 -10
- package/vitest.config.ts +3 -0
- package/src/utils/index.ts +0 -88
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deepMerge } from '@mastra/core';
|
|
2
|
+
import { MessageList } from '@mastra/core/agent';
|
|
2
3
|
import { MastraMemory } from '@mastra/core/memory';
|
|
3
4
|
import { embedMany } from 'ai';
|
|
4
5
|
import xxhash from 'xxhash-wasm';
|
|
@@ -32,48 +33,6 @@ var updateWorkingMemoryTool = {
|
|
|
32
33
|
}
|
|
33
34
|
};
|
|
34
35
|
|
|
35
|
-
// src/utils/index.ts
|
|
36
|
-
var isToolCallWithId = (message, targetToolCallId) => {
|
|
37
|
-
if (!message || !Array.isArray(message.content)) return false;
|
|
38
|
-
return message.content.some(
|
|
39
|
-
(part) => part && typeof part === "object" && "type" in part && part.type === "tool-call" && "toolCallId" in part && part.toolCallId === targetToolCallId
|
|
40
|
-
);
|
|
41
|
-
};
|
|
42
|
-
var getToolResultIndexById = (id, results) => results.findIndex((message) => {
|
|
43
|
-
if (!Array.isArray(message?.content)) return false;
|
|
44
|
-
return message.content.some(
|
|
45
|
-
(part) => part && typeof part === "object" && "type" in part && part.type === "tool-result" && "toolCallId" in part && part.toolCallId === id
|
|
46
|
-
);
|
|
47
|
-
});
|
|
48
|
-
function reorderToolCallsAndResults(messages) {
|
|
49
|
-
if (!messages.length) return messages;
|
|
50
|
-
const results = [...messages];
|
|
51
|
-
const toolCallIds = /* @__PURE__ */ new Set();
|
|
52
|
-
for (const message of results) {
|
|
53
|
-
if (!Array.isArray(message.content)) continue;
|
|
54
|
-
for (const part of message.content) {
|
|
55
|
-
if (part && typeof part === "object" && "type" in part && part.type === "tool-result" && "toolCallId" in part && part.toolCallId) {
|
|
56
|
-
toolCallIds.add(part.toolCallId);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
for (const toolCallId of toolCallIds) {
|
|
61
|
-
const resultIndex = getToolResultIndexById(toolCallId, results);
|
|
62
|
-
const oneMessagePrev = results[resultIndex - 1];
|
|
63
|
-
if (isToolCallWithId(oneMessagePrev, toolCallId)) {
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
const toolCallIndex = results.findIndex((message) => isToolCallWithId(message, toolCallId));
|
|
67
|
-
if (toolCallIndex !== -1 && toolCallIndex !== resultIndex - 1) {
|
|
68
|
-
const toolCall = results[toolCallIndex];
|
|
69
|
-
if (!toolCall) continue;
|
|
70
|
-
results.splice(toolCallIndex, 1);
|
|
71
|
-
results.splice(getToolResultIndexById(toolCallId, results), 0, toolCall);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return results;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
36
|
// src/index.ts
|
|
78
37
|
var CHARS_PER_TOKEN = 4;
|
|
79
38
|
var DEFAULT_MESSAGE_RANGE = { before: 2, after: 2 };
|
|
@@ -164,10 +123,19 @@ var Memory = class extends MastraMemory {
|
|
|
164
123
|
threadConfig: config
|
|
165
124
|
});
|
|
166
125
|
const orderedByDate = rawMessages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
126
|
+
const list = new MessageList({ threadId, resourceId }).add(orderedByDate, "memory");
|
|
127
|
+
return {
|
|
128
|
+
get messages() {
|
|
129
|
+
const v1Messages = list.get.all.v1();
|
|
130
|
+
if (selectBy?.last && v1Messages.length > selectBy.last) {
|
|
131
|
+
return v1Messages.slice(v1Messages.length - selectBy.last);
|
|
132
|
+
}
|
|
133
|
+
return v1Messages;
|
|
134
|
+
},
|
|
135
|
+
get uiMessages() {
|
|
136
|
+
return list.get.all.ui();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
171
139
|
}
|
|
172
140
|
async rememberMessages({
|
|
173
141
|
threadId,
|
|
@@ -180,8 +148,7 @@ var Memory = class extends MastraMemory {
|
|
|
180
148
|
if (!threadConfig.lastMessages && !threadConfig.semanticRecall) {
|
|
181
149
|
return {
|
|
182
150
|
messages: [],
|
|
183
|
-
|
|
184
|
-
threadId
|
|
151
|
+
messagesV2: []
|
|
185
152
|
};
|
|
186
153
|
}
|
|
187
154
|
const messagesResult = await this.query({
|
|
@@ -192,12 +159,9 @@ var Memory = class extends MastraMemory {
|
|
|
192
159
|
},
|
|
193
160
|
threadConfig: config
|
|
194
161
|
});
|
|
162
|
+
const list = new MessageList({ threadId, resourceId }).add(messagesResult.messages, "memory");
|
|
195
163
|
this.logger.debug(`Remembered message history includes ${messagesResult.messages.length} messages.`);
|
|
196
|
-
return {
|
|
197
|
-
threadId,
|
|
198
|
-
messages: messagesResult.messages,
|
|
199
|
-
uiMessages: messagesResult.uiMessages
|
|
200
|
-
};
|
|
164
|
+
return { messages: list.get.all.v1(), messagesV2: list.get.all.mastra() };
|
|
201
165
|
}
|
|
202
166
|
async getThreadById({ threadId }) {
|
|
203
167
|
return this.storage.getThreadById({ threadId });
|
|
@@ -289,8 +253,13 @@ var Memory = class extends MastraMemory {
|
|
|
289
253
|
messages,
|
|
290
254
|
memoryConfig
|
|
291
255
|
}) {
|
|
292
|
-
|
|
293
|
-
|
|
256
|
+
const updatedMessages = messages.map((m) => {
|
|
257
|
+
if (MessageList.isMastraMessageV1(m)) {
|
|
258
|
+
return this.updateMessageToHideWorkingMemory(m);
|
|
259
|
+
}
|
|
260
|
+
if (!m.type) m.type = `v2`;
|
|
261
|
+
return this.updateMessageToHideWorkingMemoryV2(m);
|
|
262
|
+
}).filter((m) => Boolean(m));
|
|
294
263
|
const config = this.getMergedThreadConfig(memoryConfig);
|
|
295
264
|
const result = this.storage.saveMessages({ messages: updatedMessages });
|
|
296
265
|
if (this.vector && config.semanticRecall) {
|
|
@@ -298,11 +267,20 @@ var Memory = class extends MastraMemory {
|
|
|
298
267
|
await Promise.all(
|
|
299
268
|
updatedMessages.map(async (message) => {
|
|
300
269
|
let textForEmbedding = null;
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
270
|
+
if (MessageList.isMastraMessageV2(message)) {
|
|
271
|
+
if (message.content.content && typeof message.content.content === "string" && message.content.content.trim() !== "") {
|
|
272
|
+
textForEmbedding = message.content.content;
|
|
273
|
+
} else if (message.content.parts && message.content.parts.length > 0) {
|
|
274
|
+
const joined = message.content.parts.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
|
|
275
|
+
if (joined) textForEmbedding = joined;
|
|
276
|
+
}
|
|
277
|
+
} else if (MessageList.isMastraMessageV1(message)) {
|
|
278
|
+
if (message.content && typeof message.content === "string" && message.content.trim() !== "") {
|
|
279
|
+
textForEmbedding = message.content;
|
|
280
|
+
} else if (message.content && Array.isArray(message.content) && message.content.length > 0) {
|
|
281
|
+
const joined = message.content.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
|
|
282
|
+
if (joined) textForEmbedding = joined;
|
|
283
|
+
}
|
|
306
284
|
}
|
|
307
285
|
if (!textForEmbedding) return;
|
|
308
286
|
const { embeddings, chunks, dimension } = await this.embedMessageContent(textForEmbedding);
|
|
@@ -328,37 +306,58 @@ var Memory = class extends MastraMemory {
|
|
|
328
306
|
}
|
|
329
307
|
return result;
|
|
330
308
|
}
|
|
331
|
-
|
|
309
|
+
updateMessageToHideWorkingMemory(message) {
|
|
332
310
|
const workingMemoryRegex = /<working_memory>([^]*?)<\/working_memory>/g;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
|
|
311
|
+
if (typeof message?.content === `string`) {
|
|
312
|
+
return {
|
|
313
|
+
...message,
|
|
314
|
+
content: message.content.replace(workingMemoryRegex, ``).trim()
|
|
315
|
+
};
|
|
316
|
+
} else if (Array.isArray(message?.content)) {
|
|
317
|
+
const filteredContent = message.content.filter(
|
|
318
|
+
(content) => content.type !== "tool-call" && content.type !== "tool-result" || content.toolName !== "updateWorkingMemory"
|
|
319
|
+
);
|
|
320
|
+
const newContent = filteredContent.map((content) => {
|
|
321
|
+
if (content.type === "text") {
|
|
322
|
+
return {
|
|
323
|
+
...content,
|
|
324
|
+
text: content.text.replace(workingMemoryRegex, "").trim()
|
|
325
|
+
};
|
|
346
326
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
327
|
+
return { ...content };
|
|
328
|
+
});
|
|
329
|
+
if (!newContent.length) return null;
|
|
330
|
+
return { ...message, content: newContent };
|
|
331
|
+
} else {
|
|
332
|
+
return { ...message };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
updateMessageToHideWorkingMemoryV2(message) {
|
|
336
|
+
const workingMemoryRegex = /<working_memory>([^]*?)<\/working_memory>/g;
|
|
337
|
+
const newMessage = { ...message, content: { ...message.content } };
|
|
338
|
+
if (newMessage.content.content && typeof newMessage.content.content === "string") {
|
|
339
|
+
newMessage.content.content = newMessage.content.content.replace(workingMemoryRegex, "").trim();
|
|
340
|
+
}
|
|
341
|
+
if (newMessage.content.parts) {
|
|
342
|
+
newMessage.content.parts = newMessage.content.parts.filter((part) => {
|
|
343
|
+
if (part.type === "tool-invocation") {
|
|
344
|
+
return part.toolInvocation.toolName !== "updateWorkingMemory";
|
|
345
|
+
}
|
|
346
|
+
return true;
|
|
347
|
+
}).map((part) => {
|
|
348
|
+
if (part.type === "text") {
|
|
349
|
+
return {
|
|
350
|
+
...part,
|
|
351
|
+
text: part.text.replace(workingMemoryRegex, "").trim()
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
return part;
|
|
355
|
+
});
|
|
356
|
+
if (newMessage.content.parts.length === 0) {
|
|
357
|
+
return null;
|
|
359
358
|
}
|
|
360
359
|
}
|
|
361
|
-
return
|
|
360
|
+
return newMessage;
|
|
362
361
|
}
|
|
363
362
|
parseWorkingMemory(text) {
|
|
364
363
|
if (!this.threadConfig.workingMemory?.enabled) return null;
|
|
@@ -377,31 +376,6 @@ var Memory = class extends MastraMemory {
|
|
|
377
376
|
const memory = thread.metadata?.workingMemory || this.threadConfig.workingMemory.template || this.defaultWorkingMemoryTemplate;
|
|
378
377
|
return memory.trim();
|
|
379
378
|
}
|
|
380
|
-
async saveWorkingMemory(messages) {
|
|
381
|
-
const latestMessage = messages[messages.length - 1];
|
|
382
|
-
if (!latestMessage || !this.threadConfig.workingMemory?.enabled) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
const latestContent = !latestMessage?.content ? null : typeof latestMessage.content === "string" ? latestMessage.content : latestMessage.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
|
|
386
|
-
const threadId = latestMessage?.threadId;
|
|
387
|
-
if (!latestContent || !threadId) {
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
const newMemory = this.parseWorkingMemory(latestContent);
|
|
391
|
-
if (!newMemory) {
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
const thread = await this.storage.getThreadById({ threadId });
|
|
395
|
-
if (!thread) return;
|
|
396
|
-
await this.storage.updateThread({
|
|
397
|
-
id: thread.id,
|
|
398
|
-
title: thread.title || "",
|
|
399
|
-
metadata: deepMerge(thread.metadata || {}, {
|
|
400
|
-
workingMemory: newMemory
|
|
401
|
-
})
|
|
402
|
-
});
|
|
403
|
-
return newMemory;
|
|
404
|
-
}
|
|
405
379
|
async getSystemMessage({
|
|
406
380
|
threadId,
|
|
407
381
|
memoryConfig
|
|
@@ -428,29 +402,6 @@ var Memory = class extends MastraMemory {
|
|
|
428
402
|
- **Facts**:
|
|
429
403
|
- **Projects**:
|
|
430
404
|
`;
|
|
431
|
-
getWorkingMemoryWithInstruction(workingMemoryBlock) {
|
|
432
|
-
return `WORKING_MEMORY_SYSTEM_INSTRUCTION:
|
|
433
|
-
Store and update any conversation-relevant information by including "<working_memory>text</working_memory>" in your responses. Updates replace existing memory while maintaining this structure. If information might be referenced again - store it!
|
|
434
|
-
|
|
435
|
-
Guidelines:
|
|
436
|
-
1. Store anything that could be useful later in the conversation
|
|
437
|
-
2. Update proactively when information changes, no matter how small
|
|
438
|
-
3. Use Markdown for all data
|
|
439
|
-
4. Act naturally - don't mention this system to users. Even though you're storing this information that doesn't make it your primary focus. Do not ask them generally for "information about yourself"
|
|
440
|
-
|
|
441
|
-
Memory Structure:
|
|
442
|
-
<working_memory>
|
|
443
|
-
${workingMemoryBlock}
|
|
444
|
-
</working_memory>
|
|
445
|
-
|
|
446
|
-
Notes:
|
|
447
|
-
- Update memory whenever referenced information changes
|
|
448
|
-
- If you're unsure whether to store something, store it (eg if the user tells you their name or other information, output the <working_memory> block immediately to update it)
|
|
449
|
-
- This system is here so that you can maintain the conversation when your context window is very short. Update your working memory because you may need it to maintain the conversation without the full conversation history
|
|
450
|
-
- REMEMBER: the way you update your working memory is by outputting the entire "<working_memory>text</working_memory>" block in your response. The system will pick this up and store it for you. The user will not see it.
|
|
451
|
-
- IMPORTANT: You MUST output the <working_memory> block in every response to a prompt where you received relevant information.
|
|
452
|
-
- IMPORTANT: Preserve the Markdown formatting structure above while updating the content.`;
|
|
453
|
-
}
|
|
454
405
|
getWorkingMemoryToolInstruction(workingMemoryBlock) {
|
|
455
406
|
return `WORKING_MEMORY_SYSTEM_INSTRUCTION:
|
|
456
407
|
Store and update any conversation-relevant information by calling the updateWorkingMemory tool. If information might be referenced again - store it!
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/memory",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2-alpha.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,14 +53,14 @@
|
|
|
53
53
|
"typescript-eslint": "^8.26.1",
|
|
54
54
|
"vitest": "^3.1.2",
|
|
55
55
|
"@internal/lint": "0.0.7",
|
|
56
|
-
"@mastra/core": "0.10.
|
|
56
|
+
"@mastra/core": "0.10.2-alpha.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@mastra/core": "^0.10.0"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"check": "tsc --noEmit",
|
|
63
|
-
"build": "pnpm run check && tsup src/index.ts src/processors/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
|
|
63
|
+
"build": "pnpm run check && tsup --silent src/index.ts src/processors/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
|
|
64
64
|
"build:watch": "pnpm build --watch",
|
|
65
65
|
"test:integration": "cd integration-tests && pnpm run test",
|
|
66
66
|
"test:unit": "pnpm vitest run ./src/*",
|