@mastra/memory 0.1.0-alpha.83 → 0.1.0-alpha.85
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/CHANGELOG.md +14 -0
- package/dist/_tsup-dts-rollup.d.ts +63 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +282 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @mastra/memory
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.85
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [4534e77]
|
|
8
|
+
- @mastra/core@0.2.0-alpha.103
|
|
9
|
+
|
|
10
|
+
## 0.1.0-alpha.84
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies [a9345f9]
|
|
15
|
+
- @mastra/core@0.2.0-alpha.102
|
|
16
|
+
|
|
3
17
|
## 0.1.0-alpha.83
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { CoreMessage } from '@mastra/core';
|
|
2
|
+
import { MastraMemory } from '@mastra/core/memory';
|
|
3
|
+
import { MemoryConfig } from '@mastra/core/memory';
|
|
4
|
+
import { Message } from 'ai';
|
|
5
|
+
import { MessageType } from '@mastra/core/memory';
|
|
6
|
+
import { SharedMemoryConfig } from '@mastra/core/memory';
|
|
7
|
+
import { StorageGetMessagesArg } from '@mastra/core/storage';
|
|
8
|
+
import { StorageThreadType } from '@mastra/core/memory';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Concrete implementation of MastraMemory that adds support for thread configuration
|
|
12
|
+
* and message injection.
|
|
13
|
+
*/
|
|
14
|
+
export declare class Memory extends MastraMemory {
|
|
15
|
+
constructor(config: SharedMemoryConfig & {
|
|
16
|
+
embeddings?: any;
|
|
17
|
+
});
|
|
18
|
+
query({ threadId, selectBy, threadConfig, }: StorageGetMessagesArg): Promise<{
|
|
19
|
+
messages: CoreMessage[];
|
|
20
|
+
uiMessages: Message[];
|
|
21
|
+
}>;
|
|
22
|
+
rememberMessages({ threadId, vectorMessageSearch, config, }: {
|
|
23
|
+
threadId: string;
|
|
24
|
+
vectorMessageSearch?: string;
|
|
25
|
+
config?: MemoryConfig;
|
|
26
|
+
}): Promise<{
|
|
27
|
+
messages: CoreMessage[];
|
|
28
|
+
uiMessages: Message[];
|
|
29
|
+
}>;
|
|
30
|
+
getThreadById({ threadId }: {
|
|
31
|
+
threadId: string;
|
|
32
|
+
}): Promise<StorageThreadType | null>;
|
|
33
|
+
getThreadsByResourceId({ resourceId }: {
|
|
34
|
+
resourceId: string;
|
|
35
|
+
}): Promise<StorageThreadType[]>;
|
|
36
|
+
saveThread({ thread, memoryConfig, }: {
|
|
37
|
+
thread: StorageThreadType;
|
|
38
|
+
memoryConfig?: MemoryConfig;
|
|
39
|
+
}): Promise<StorageThreadType>;
|
|
40
|
+
updateThread({ id, title, metadata, }: {
|
|
41
|
+
id: string;
|
|
42
|
+
title: string;
|
|
43
|
+
metadata: Record<string, unknown>;
|
|
44
|
+
}): Promise<StorageThreadType>;
|
|
45
|
+
deleteThread(threadId: string): Promise<void>;
|
|
46
|
+
saveMessages({ messages }: {
|
|
47
|
+
messages: MessageType[];
|
|
48
|
+
}): Promise<MessageType[]>;
|
|
49
|
+
protected mutateMessagesToHideWorkingMemory(messages: MessageType[]): void;
|
|
50
|
+
protected parseWorkingMemory(text: string): string | null;
|
|
51
|
+
protected getWorkingMemory({ threadId }: {
|
|
52
|
+
threadId: string;
|
|
53
|
+
}): Promise<string | null>;
|
|
54
|
+
private saveWorkingMemory;
|
|
55
|
+
getSystemMessage({ threadId, memoryConfig, }: {
|
|
56
|
+
threadId: string;
|
|
57
|
+
memoryConfig?: MemoryConfig;
|
|
58
|
+
}): Promise<string | null>;
|
|
59
|
+
defaultWorkingMemoryTemplate: string;
|
|
60
|
+
private getWorkingMemoryWithInstruction;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { }
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Memory } from './_tsup-dts-rollup.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { deepMerge } from '@mastra/core';
|
|
2
|
+
import { MastraMemory } from '@mastra/core/memory';
|
|
3
|
+
import '@mastra/core/storage';
|
|
4
|
+
import { embed } from 'ai';
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
var Memory = class extends MastraMemory {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
const embedderExample = `
|
|
10
|
+
Example:
|
|
11
|
+
|
|
12
|
+
import { openai } from '@ai-sdk/openai';
|
|
13
|
+
|
|
14
|
+
new Memory({
|
|
15
|
+
embedder: openai.embedding(\`text-embedding-3-small\`)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
`;
|
|
19
|
+
if (config.embeddings) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`The \`embeddings\` option is deprecated. Please use \`embedder\` instead.
|
|
22
|
+
${embedderExample}
|
|
23
|
+
`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
if (config.vector && !config.embedder) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`The \`embedder\` option is required when a vector DB is attached to new Memory({ vector })
|
|
29
|
+
|
|
30
|
+
${embedderExample}`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
super({ name: "Memory", ...config });
|
|
34
|
+
const mergedConfig = this.getMergedThreadConfig({
|
|
35
|
+
workingMemory: config.options?.workingMemory || {
|
|
36
|
+
enabled: false,
|
|
37
|
+
template: this.defaultWorkingMemoryTemplate
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
this.threadConfig = mergedConfig;
|
|
41
|
+
}
|
|
42
|
+
async query({
|
|
43
|
+
threadId,
|
|
44
|
+
selectBy,
|
|
45
|
+
threadConfig
|
|
46
|
+
}) {
|
|
47
|
+
let vectorResults = null;
|
|
48
|
+
this.logger.info(`Memory query() with:`, {
|
|
49
|
+
threadId,
|
|
50
|
+
selectBy,
|
|
51
|
+
threadConfig
|
|
52
|
+
});
|
|
53
|
+
const config = this.getMergedThreadConfig(threadConfig || {});
|
|
54
|
+
const vectorConfig = typeof config?.semanticRecall === `boolean` ? {
|
|
55
|
+
topK: 2,
|
|
56
|
+
messageRange: { before: 2, after: 2 }
|
|
57
|
+
} : {
|
|
58
|
+
topK: config?.semanticRecall?.topK || 2,
|
|
59
|
+
messageRange: config?.semanticRecall?.messageRange || { before: 2, after: 2 }
|
|
60
|
+
};
|
|
61
|
+
if (selectBy?.vectorSearchString && this.vector) {
|
|
62
|
+
const embedder = this.getEmbedder();
|
|
63
|
+
const { embedding } = await embed({
|
|
64
|
+
value: selectBy.vectorSearchString,
|
|
65
|
+
model: embedder
|
|
66
|
+
});
|
|
67
|
+
const { indexName } = await this.createEmbeddingIndex();
|
|
68
|
+
vectorResults = await this.vector.query(indexName, embedding, vectorConfig.topK, {
|
|
69
|
+
thread_id: threadId
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const rawMessages = await this.storage.__getMessages({
|
|
73
|
+
threadId,
|
|
74
|
+
selectBy: {
|
|
75
|
+
...selectBy,
|
|
76
|
+
...vectorResults?.length ? {
|
|
77
|
+
include: vectorResults.map((r) => ({
|
|
78
|
+
id: r.metadata?.message_id,
|
|
79
|
+
withNextMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.after,
|
|
80
|
+
withPreviousMessages: typeof vectorConfig.messageRange === "number" ? vectorConfig.messageRange : vectorConfig.messageRange.before
|
|
81
|
+
}))
|
|
82
|
+
} : {}
|
|
83
|
+
},
|
|
84
|
+
threadConfig: config
|
|
85
|
+
});
|
|
86
|
+
const messages = this.parseMessages(rawMessages);
|
|
87
|
+
const uiMessages = this.convertToUIMessages(rawMessages);
|
|
88
|
+
return { messages, uiMessages };
|
|
89
|
+
}
|
|
90
|
+
async rememberMessages({
|
|
91
|
+
threadId,
|
|
92
|
+
vectorMessageSearch,
|
|
93
|
+
config
|
|
94
|
+
}) {
|
|
95
|
+
const threadConfig = this.getMergedThreadConfig(config || {});
|
|
96
|
+
if (!threadConfig.lastMessages && !threadConfig.semanticRecall) {
|
|
97
|
+
return {
|
|
98
|
+
messages: [],
|
|
99
|
+
uiMessages: []
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const messages = await this.query({
|
|
103
|
+
threadId,
|
|
104
|
+
selectBy: {
|
|
105
|
+
last: threadConfig.lastMessages,
|
|
106
|
+
vectorSearchString: threadConfig.semanticRecall && vectorMessageSearch ? vectorMessageSearch : undefined
|
|
107
|
+
},
|
|
108
|
+
threadConfig: config
|
|
109
|
+
});
|
|
110
|
+
this.logger.info(`Remembered message history includes ${messages.messages.length} messages.`);
|
|
111
|
+
return messages;
|
|
112
|
+
}
|
|
113
|
+
async getThreadById({ threadId }) {
|
|
114
|
+
return this.storage.__getThreadById({ threadId });
|
|
115
|
+
}
|
|
116
|
+
async getThreadsByResourceId({ resourceId }) {
|
|
117
|
+
return this.storage.__getThreadsByResourceId({ resourceId });
|
|
118
|
+
}
|
|
119
|
+
async saveThread({
|
|
120
|
+
thread,
|
|
121
|
+
memoryConfig
|
|
122
|
+
}) {
|
|
123
|
+
const config = this.getMergedThreadConfig(memoryConfig || {});
|
|
124
|
+
if (config.workingMemory?.enabled && !thread?.metadata?.workingMemory) {
|
|
125
|
+
return this.storage.__saveThread({
|
|
126
|
+
thread: deepMerge(thread, {
|
|
127
|
+
metadata: {
|
|
128
|
+
workingMemory: config.workingMemory.template || this.defaultWorkingMemoryTemplate
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return this.storage.__saveThread({ thread });
|
|
134
|
+
}
|
|
135
|
+
async updateThread({
|
|
136
|
+
id,
|
|
137
|
+
title,
|
|
138
|
+
metadata
|
|
139
|
+
}) {
|
|
140
|
+
return this.storage.__updateThread({
|
|
141
|
+
id,
|
|
142
|
+
title,
|
|
143
|
+
metadata
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async deleteThread(threadId) {
|
|
147
|
+
await this.storage.__deleteThread({ threadId });
|
|
148
|
+
}
|
|
149
|
+
async saveMessages({ messages }) {
|
|
150
|
+
await this.saveWorkingMemory(messages);
|
|
151
|
+
this.mutateMessagesToHideWorkingMemory(messages);
|
|
152
|
+
if (this.vector) {
|
|
153
|
+
const embedder = this.getEmbedder();
|
|
154
|
+
const { indexName } = await this.createEmbeddingIndex();
|
|
155
|
+
for (const message of messages) {
|
|
156
|
+
if (typeof message.content !== `string`) continue;
|
|
157
|
+
const { embedding } = await embed({ value: message.content, model: embedder, maxRetries: 3 });
|
|
158
|
+
await this.vector.upsert(
|
|
159
|
+
indexName,
|
|
160
|
+
[embedding],
|
|
161
|
+
[
|
|
162
|
+
{
|
|
163
|
+
text: message.content,
|
|
164
|
+
message_id: message.id,
|
|
165
|
+
thread_id: message.threadId
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return this.storage.__saveMessages({ messages });
|
|
172
|
+
}
|
|
173
|
+
mutateMessagesToHideWorkingMemory(messages) {
|
|
174
|
+
const workingMemoryRegex = /<working_memory>([^]*?)<\/working_memory>/g;
|
|
175
|
+
for (const message of messages) {
|
|
176
|
+
if (typeof message?.content === `string`) {
|
|
177
|
+
message.content = message.content.replace(workingMemoryRegex, ``).trim();
|
|
178
|
+
} else if (Array.isArray(message?.content)) {
|
|
179
|
+
for (const content of message.content) {
|
|
180
|
+
if (content.type === `text`) {
|
|
181
|
+
content.text = content.text.replace(workingMemoryRegex, ``).trim();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
parseWorkingMemory(text) {
|
|
188
|
+
if (!this.threadConfig.workingMemory?.enabled) return null;
|
|
189
|
+
const workingMemoryRegex = /<working_memory>([^]*?)<\/working_memory>/g;
|
|
190
|
+
const matches = text.match(workingMemoryRegex);
|
|
191
|
+
const match = matches?.[0];
|
|
192
|
+
if (match) {
|
|
193
|
+
return match.replace(/<\/?working_memory>/g, "").trim();
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
async getWorkingMemory({ threadId }) {
|
|
198
|
+
if (!this.threadConfig.workingMemory?.enabled) return null;
|
|
199
|
+
const thread = await this.storage.__getThreadById({ threadId });
|
|
200
|
+
if (!thread) return this.threadConfig?.workingMemory?.template || this.defaultWorkingMemoryTemplate;
|
|
201
|
+
const memory = thread.metadata?.workingMemory || this.threadConfig.workingMemory.template || this.defaultWorkingMemoryTemplate;
|
|
202
|
+
return memory.split(`>
|
|
203
|
+
`).map((c) => c.trim()).join(`>`);
|
|
204
|
+
}
|
|
205
|
+
async saveWorkingMemory(messages) {
|
|
206
|
+
const latestMessage = messages[messages.length - 1];
|
|
207
|
+
if (!latestMessage || !this.threadConfig.workingMemory?.enabled) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const latestContent = !latestMessage?.content ? null : typeof latestMessage.content === "string" ? latestMessage.content : latestMessage.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
|
|
211
|
+
const threadId = latestMessage?.threadId;
|
|
212
|
+
if (!latestContent || !threadId) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const newMemory = this.parseWorkingMemory(latestContent);
|
|
216
|
+
if (!newMemory) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const thread = await this.storage.__getThreadById({ threadId });
|
|
220
|
+
if (!thread) return;
|
|
221
|
+
await this.storage.__updateThread({
|
|
222
|
+
id: thread.id,
|
|
223
|
+
title: thread.title || "",
|
|
224
|
+
metadata: deepMerge(thread.metadata || {}, {
|
|
225
|
+
workingMemory: newMemory
|
|
226
|
+
})
|
|
227
|
+
});
|
|
228
|
+
return newMemory;
|
|
229
|
+
}
|
|
230
|
+
async getSystemMessage({
|
|
231
|
+
threadId,
|
|
232
|
+
memoryConfig
|
|
233
|
+
}) {
|
|
234
|
+
const config = this.getMergedThreadConfig(memoryConfig);
|
|
235
|
+
if (!config.workingMemory?.enabled) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
const workingMemory = await this.getWorkingMemory({ threadId });
|
|
239
|
+
if (!workingMemory) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
return this.getWorkingMemoryWithInstruction(workingMemory);
|
|
243
|
+
}
|
|
244
|
+
defaultWorkingMemoryTemplate = `
|
|
245
|
+
<user>
|
|
246
|
+
<first_name></first_name>
|
|
247
|
+
<last_name></last_name>
|
|
248
|
+
<location></location>
|
|
249
|
+
<occupation></occupation>
|
|
250
|
+
<interests></interests>
|
|
251
|
+
<goals></goals>
|
|
252
|
+
<events></events>
|
|
253
|
+
<facts></facts>
|
|
254
|
+
<projects></projects>
|
|
255
|
+
</user>
|
|
256
|
+
`;
|
|
257
|
+
getWorkingMemoryWithInstruction(workingMemoryBlock) {
|
|
258
|
+
return `WORKING_MEMORY_SYSTEM_INSTRUCTION:
|
|
259
|
+
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!
|
|
260
|
+
|
|
261
|
+
Guidelines:
|
|
262
|
+
1. Store anything that could be useful later in the conversation
|
|
263
|
+
2. Update proactively when information changes, no matter how small
|
|
264
|
+
3. Use nested tags for all data
|
|
265
|
+
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"
|
|
266
|
+
|
|
267
|
+
Memory Structure:
|
|
268
|
+
<working_memory>
|
|
269
|
+
${workingMemoryBlock}
|
|
270
|
+
</working_memory>
|
|
271
|
+
|
|
272
|
+
Notes:
|
|
273
|
+
- Update memory whenever referenced information changes
|
|
274
|
+
- If you're unsure whether to store something, store it (eg if the user tells you their name or the value of another empty section in your working memory, output the <working_memory> block immediately to update it)
|
|
275
|
+
- 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
|
|
276
|
+
- Do not remove empty sections - you must output the empty sections along with the ones you're filling in
|
|
277
|
+
- 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.
|
|
278
|
+
- IMPORTANT: You MUST output the <working_memory> block in every response to a prompt where you received relevant information. `;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
export { Memory };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/memory",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.85",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"pg-pool": "^3.7.0",
|
|
37
37
|
"postgres": "^3.4.5",
|
|
38
38
|
"redis": "^4.7.0",
|
|
39
|
-
"@mastra/core": "^0.2.0-alpha.
|
|
39
|
+
"@mastra/core": "^0.2.0-alpha.103"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@microsoft/api-extractor": "^7.49.2",
|