@mastra/memory 0.10.4-alpha.0 → 0.10.4-alpha.1
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 +2 -2
- package/CHANGELOG.md +12 -0
- package/dist/_tsup-dts-rollup.d.cts +11 -2
- package/dist/_tsup-dts-rollup.d.ts +11 -2
- package/dist/index.cjs +86 -19
- package/dist/index.js +85 -19
- package/package.json +4 -3
- package/src/index.ts +101 -19
- package/src/tools/working-memory.ts +15 -5
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @mastra/memory@0.10.4-alpha.
|
|
2
|
+
> @mastra/memory@0.10.4-alpha.1 build /home/runner/work/mastra/mastra/packages/memory
|
|
3
3
|
> pnpm run check && tsup --silent src/index.ts src/processors/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @mastra/memory@0.10.4-alpha.
|
|
6
|
+
> @mastra/memory@0.10.4-alpha.1 check /home/runner/work/mastra/mastra/packages/memory
|
|
7
7
|
> tsc --noEmit
|
|
8
8
|
|
|
9
9
|
Analysis will use the bundled TypeScript version 5.8.3
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @mastra/memory
|
|
2
2
|
|
|
3
|
+
## 0.10.4-alpha.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4051477: dependencies updates:
|
|
8
|
+
- Added dependency [`zod-to-json-schema@^3.24.5` ↗︎](https://www.npmjs.com/package/zod-to-json-schema/v/3.24.5) (to `dependencies`)
|
|
9
|
+
- d70c420: fix(core, memory): fix fetchMemory regression
|
|
10
|
+
- 2a16996: Working Memory Schema and Template
|
|
11
|
+
- Updated dependencies [d70c420]
|
|
12
|
+
- Updated dependencies [2a16996]
|
|
13
|
+
- @mastra/core@0.10.6-alpha.3
|
|
14
|
+
|
|
3
15
|
## 0.10.4-alpha.0
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -13,6 +13,8 @@ import type { StorageGetMessagesArg } from '@mastra/core/storage';
|
|
|
13
13
|
import type { StorageThreadType } from '@mastra/core/memory';
|
|
14
14
|
import type { TiktokenBPE } from 'js-tiktoken/lite';
|
|
15
15
|
import type { UIMessage } from 'ai';
|
|
16
|
+
import type { WorkingMemoryFormat } from '@mastra/core/memory';
|
|
17
|
+
import type { WorkingMemoryTemplate } from '@mastra/core/memory';
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Concrete implementation of MastraMemory that adds support for thread configuration
|
|
@@ -72,13 +74,18 @@ export declare class Memory extends MastraMemory {
|
|
|
72
74
|
protected updateMessageToHideWorkingMemory(message: MastraMessageV1): MastraMessageV1 | null;
|
|
73
75
|
protected updateMessageToHideWorkingMemoryV2(message: MastraMessageV2): MastraMessageV2 | null;
|
|
74
76
|
protected parseWorkingMemory(text: string): string | null;
|
|
75
|
-
getWorkingMemory({ threadId }: {
|
|
77
|
+
getWorkingMemory({ threadId, format, }: {
|
|
76
78
|
threadId: string;
|
|
79
|
+
format?: WorkingMemoryFormat;
|
|
77
80
|
}): Promise<string | null>;
|
|
81
|
+
getWorkingMemoryTemplate(): Promise<WorkingMemoryTemplate | null>;
|
|
78
82
|
getSystemMessage({ threadId, memoryConfig, }: {
|
|
79
83
|
threadId: string;
|
|
80
84
|
memoryConfig?: MemoryConfig;
|
|
81
85
|
}): Promise<string | null>;
|
|
86
|
+
getUserContextMessage({ threadId }: {
|
|
87
|
+
threadId: string;
|
|
88
|
+
}): Promise<string | null>;
|
|
82
89
|
defaultWorkingMemoryTemplate: string;
|
|
83
90
|
private getWorkingMemoryToolInstruction;
|
|
84
91
|
getTools(config?: MemoryConfig): Record<string, CoreTool>;
|
|
@@ -134,6 +141,8 @@ declare class ToolCallFilter extends MemoryProcessor_2 {
|
|
|
134
141
|
export { ToolCallFilter }
|
|
135
142
|
export { ToolCallFilter as ToolCallFilter_alias_1 }
|
|
136
143
|
|
|
137
|
-
export declare const updateWorkingMemoryTool:
|
|
144
|
+
export declare const updateWorkingMemoryTool: ({ format }: {
|
|
145
|
+
format: WorkingMemoryFormat;
|
|
146
|
+
}) => CoreTool;
|
|
138
147
|
|
|
139
148
|
export { }
|
|
@@ -13,6 +13,8 @@ import type { StorageGetMessagesArg } from '@mastra/core/storage';
|
|
|
13
13
|
import type { StorageThreadType } from '@mastra/core/memory';
|
|
14
14
|
import type { TiktokenBPE } from 'js-tiktoken/lite';
|
|
15
15
|
import type { UIMessage } from 'ai';
|
|
16
|
+
import type { WorkingMemoryFormat } from '@mastra/core/memory';
|
|
17
|
+
import type { WorkingMemoryTemplate } from '@mastra/core/memory';
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Concrete implementation of MastraMemory that adds support for thread configuration
|
|
@@ -72,13 +74,18 @@ export declare class Memory extends MastraMemory {
|
|
|
72
74
|
protected updateMessageToHideWorkingMemory(message: MastraMessageV1): MastraMessageV1 | null;
|
|
73
75
|
protected updateMessageToHideWorkingMemoryV2(message: MastraMessageV2): MastraMessageV2 | null;
|
|
74
76
|
protected parseWorkingMemory(text: string): string | null;
|
|
75
|
-
getWorkingMemory({ threadId }: {
|
|
77
|
+
getWorkingMemory({ threadId, format, }: {
|
|
76
78
|
threadId: string;
|
|
79
|
+
format?: WorkingMemoryFormat;
|
|
77
80
|
}): Promise<string | null>;
|
|
81
|
+
getWorkingMemoryTemplate(): Promise<WorkingMemoryTemplate | null>;
|
|
78
82
|
getSystemMessage({ threadId, memoryConfig, }: {
|
|
79
83
|
threadId: string;
|
|
80
84
|
memoryConfig?: MemoryConfig;
|
|
81
85
|
}): Promise<string | null>;
|
|
86
|
+
getUserContextMessage({ threadId }: {
|
|
87
|
+
threadId: string;
|
|
88
|
+
}): Promise<string | null>;
|
|
82
89
|
defaultWorkingMemoryTemplate: string;
|
|
83
90
|
private getWorkingMemoryToolInstruction;
|
|
84
91
|
getTools(config?: MemoryConfig): Record<string, CoreTool>;
|
|
@@ -134,6 +141,8 @@ declare class ToolCallFilter extends MemoryProcessor_2 {
|
|
|
134
141
|
export { ToolCallFilter }
|
|
135
142
|
export { ToolCallFilter as ToolCallFilter_alias_1 }
|
|
136
143
|
|
|
137
|
-
export declare const updateWorkingMemoryTool:
|
|
144
|
+
export declare const updateWorkingMemoryTool: ({ format }: {
|
|
145
|
+
format: WorkingMemoryFormat;
|
|
146
|
+
}) => CoreTool;
|
|
138
147
|
|
|
139
148
|
export { }
|
package/dist/index.cjs
CHANGED
|
@@ -5,20 +5,22 @@ var agent = require('@mastra/core/agent');
|
|
|
5
5
|
var memory = require('@mastra/core/memory');
|
|
6
6
|
var ai = require('ai');
|
|
7
7
|
var xxhash = require('xxhash-wasm');
|
|
8
|
+
var zodToJsonSchema = require('zod-to-json-schema');
|
|
8
9
|
var zod = require('zod');
|
|
9
10
|
|
|
10
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
12
|
|
|
12
13
|
var xxhash__default = /*#__PURE__*/_interopDefault(xxhash);
|
|
14
|
+
var zodToJsonSchema__default = /*#__PURE__*/_interopDefault(zodToJsonSchema);
|
|
13
15
|
|
|
14
16
|
// src/index.ts
|
|
15
|
-
var updateWorkingMemoryTool = {
|
|
17
|
+
var updateWorkingMemoryTool = ({ format }) => ({
|
|
16
18
|
description: "Update the working memory with new information",
|
|
17
19
|
parameters: zod.z.object({
|
|
18
|
-
memory: zod.z.string().describe(
|
|
20
|
+
memory: zod.z.string().describe(`The ${format === "json" ? "JSON" : "Markdown"} formatted working memory content to store`)
|
|
19
21
|
}),
|
|
20
22
|
execute: async (params) => {
|
|
21
|
-
const { context, threadId, memory } = params;
|
|
23
|
+
const { context, threadId, memory, resourceId } = params;
|
|
22
24
|
if (!threadId || !memory) {
|
|
23
25
|
throw new Error("Thread ID and Memory instance are required for working memory updates");
|
|
24
26
|
}
|
|
@@ -26,18 +28,22 @@ var updateWorkingMemoryTool = {
|
|
|
26
28
|
if (!thread) {
|
|
27
29
|
throw new Error(`Thread ${threadId} not found`);
|
|
28
30
|
}
|
|
31
|
+
if (thread.resourceId && thread.resourceId !== resourceId) {
|
|
32
|
+
throw new Error(`Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}`);
|
|
33
|
+
}
|
|
34
|
+
const workingMemory = context.memory;
|
|
29
35
|
await memory.saveThread({
|
|
30
36
|
thread: {
|
|
31
37
|
...thread,
|
|
32
38
|
metadata: {
|
|
33
39
|
...thread.metadata,
|
|
34
|
-
workingMemory
|
|
40
|
+
workingMemory
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
43
|
});
|
|
38
44
|
return { success: true };
|
|
39
45
|
}
|
|
40
|
-
};
|
|
46
|
+
});
|
|
41
47
|
|
|
42
48
|
// src/index.ts
|
|
43
49
|
var CHARS_PER_TOKEN = 4;
|
|
@@ -200,10 +206,14 @@ var Memory = class extends memory.MastraMemory {
|
|
|
200
206
|
}) {
|
|
201
207
|
const config = this.getMergedThreadConfig(memoryConfig || {});
|
|
202
208
|
if (config.workingMemory?.enabled && !thread?.metadata?.workingMemory) {
|
|
209
|
+
let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
|
|
210
|
+
if (config.workingMemory.schema) {
|
|
211
|
+
workingMemory = JSON.stringify(zodToJsonSchema__default.default(config.workingMemory.schema));
|
|
212
|
+
}
|
|
203
213
|
return this.storage.saveThread({
|
|
204
214
|
thread: core.deepMerge(thread, {
|
|
205
215
|
metadata: {
|
|
206
|
-
workingMemory
|
|
216
|
+
workingMemory
|
|
207
217
|
}
|
|
208
218
|
})
|
|
209
219
|
});
|
|
@@ -399,12 +409,41 @@ var Memory = class extends memory.MastraMemory {
|
|
|
399
409
|
}
|
|
400
410
|
return null;
|
|
401
411
|
}
|
|
402
|
-
async getWorkingMemory({
|
|
403
|
-
|
|
412
|
+
async getWorkingMemory({
|
|
413
|
+
threadId,
|
|
414
|
+
format
|
|
415
|
+
}) {
|
|
416
|
+
if (!this.threadConfig.workingMemory?.enabled) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
404
419
|
const thread = await this.storage.getThreadById({ threadId });
|
|
405
|
-
if (
|
|
406
|
-
|
|
407
|
-
|
|
420
|
+
if (format === "json") {
|
|
421
|
+
try {
|
|
422
|
+
return JSON.parse(thread?.metadata?.workingMemory) || null;
|
|
423
|
+
} catch (e) {
|
|
424
|
+
this.logger.error("Unable to parse working memory as JSON. Returning string.", e);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return thread?.metadata?.workingMemory ? JSON.stringify(thread?.metadata?.workingMemory) : null;
|
|
428
|
+
}
|
|
429
|
+
async getWorkingMemoryTemplate() {
|
|
430
|
+
if (!this.threadConfig.workingMemory?.enabled) {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
if (this.threadConfig?.workingMemory?.schema) {
|
|
434
|
+
try {
|
|
435
|
+
const schema = this.threadConfig.workingMemory.schema;
|
|
436
|
+
const convertedSchema = zodToJsonSchema__default.default(schema, {
|
|
437
|
+
$refStrategy: "none"
|
|
438
|
+
});
|
|
439
|
+
return { format: "json", content: JSON.stringify(convertedSchema) };
|
|
440
|
+
} catch (error) {
|
|
441
|
+
this.logger.error("Error converting schema", error);
|
|
442
|
+
throw error;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const memory = this.threadConfig.workingMemory.template || this.defaultWorkingMemoryTemplate;
|
|
446
|
+
return { format: "markdown", content: memory.trim() };
|
|
408
447
|
}
|
|
409
448
|
async getSystemMessage({
|
|
410
449
|
threadId,
|
|
@@ -414,11 +453,28 @@ var Memory = class extends memory.MastraMemory {
|
|
|
414
453
|
if (!config.workingMemory?.enabled) {
|
|
415
454
|
return null;
|
|
416
455
|
}
|
|
456
|
+
const workingMemoryTemplate = await this.getWorkingMemoryTemplate();
|
|
457
|
+
const workingMemoryData = await this.getWorkingMemory({ threadId });
|
|
458
|
+
if (!workingMemoryTemplate) {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
return this.getWorkingMemoryToolInstruction({
|
|
462
|
+
template: workingMemoryTemplate,
|
|
463
|
+
data: workingMemoryData
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
async getUserContextMessage({ threadId }) {
|
|
417
467
|
const workingMemory = await this.getWorkingMemory({ threadId });
|
|
418
468
|
if (!workingMemory) {
|
|
419
469
|
return null;
|
|
420
470
|
}
|
|
421
|
-
return
|
|
471
|
+
return `The following is the most up-to-date information about the user's state and context:
|
|
472
|
+
${JSON.stringify(workingMemory)}
|
|
473
|
+
Use this information as the source of truth when generating responses.
|
|
474
|
+
Do not reference or mention this memory directly to the user.
|
|
475
|
+
If conversation history shows information that is not in the working memory, use the working memory as the source of truth.
|
|
476
|
+
If there is a discrepancy between this information and conversation history, always rely on this information unless the user explicitly asks for an update.
|
|
477
|
+
`;
|
|
422
478
|
}
|
|
423
479
|
defaultWorkingMemoryTemplate = `
|
|
424
480
|
# User Information
|
|
@@ -432,33 +488,44 @@ var Memory = class extends memory.MastraMemory {
|
|
|
432
488
|
- **Facts**:
|
|
433
489
|
- **Projects**:
|
|
434
490
|
`;
|
|
435
|
-
getWorkingMemoryToolInstruction(
|
|
491
|
+
getWorkingMemoryToolInstruction({
|
|
492
|
+
template,
|
|
493
|
+
data
|
|
494
|
+
}) {
|
|
436
495
|
return `WORKING_MEMORY_SYSTEM_INSTRUCTION:
|
|
437
496
|
Store and update any conversation-relevant information by calling the updateWorkingMemory tool. If information might be referenced again - store it!
|
|
438
497
|
|
|
439
498
|
Guidelines:
|
|
440
499
|
1. Store anything that could be useful later in the conversation
|
|
441
500
|
2. Update proactively when information changes, no matter how small
|
|
442
|
-
3. Use Markdown format for all data
|
|
501
|
+
3. Use ${template.format === "json" ? "JSON" : "Markdown"} format for all data
|
|
443
502
|
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"
|
|
444
503
|
|
|
445
|
-
|
|
446
|
-
${
|
|
504
|
+
WORKING MEMORY TEMPLATE:
|
|
505
|
+
${template.content}
|
|
506
|
+
|
|
507
|
+
WORKING MEMORY DATA:
|
|
508
|
+
${data}
|
|
447
509
|
|
|
448
510
|
Notes:
|
|
449
511
|
- Update memory whenever referenced information changes
|
|
450
512
|
- If you're unsure whether to store something, store it (eg if the user tells you information about themselves, call updateWorkingMemory immediately to update it)
|
|
451
513
|
- 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
|
|
452
514
|
- Do not remove empty sections - you must include the empty sections along with the ones you're filling in
|
|
453
|
-
- REMEMBER: the way you update your working memory is by calling the updateWorkingMemory tool with the entire Markdown content. The system will store it for you. The user will not see it.
|
|
515
|
+
- REMEMBER: the way you update your working memory is by calling the updateWorkingMemory tool with the entire ${template.format === "json" ? "JSON" : "Markdown"} content. The system will store it for you. The user will not see it.
|
|
454
516
|
- IMPORTANT: You MUST call updateWorkingMemory in every response to a prompt where you received relevant information.
|
|
455
|
-
- IMPORTANT: Preserve the Markdown formatting structure above while updating the content.`;
|
|
517
|
+
- IMPORTANT: Preserve the ${template.format === "json" ? "JSON" : "Markdown"} formatting structure above while updating the content.`;
|
|
456
518
|
}
|
|
457
519
|
getTools(config) {
|
|
458
520
|
const mergedConfig = this.getMergedThreadConfig(config);
|
|
459
521
|
if (mergedConfig.workingMemory?.enabled) {
|
|
522
|
+
if (mergedConfig.workingMemory.schema) {
|
|
523
|
+
return {
|
|
524
|
+
updateWorkingMemory: updateWorkingMemoryTool({ format: "json" })
|
|
525
|
+
};
|
|
526
|
+
}
|
|
460
527
|
return {
|
|
461
|
-
updateWorkingMemory: updateWorkingMemoryTool
|
|
528
|
+
updateWorkingMemory: updateWorkingMemoryTool({ format: "markdown" })
|
|
462
529
|
};
|
|
463
530
|
}
|
|
464
531
|
return {};
|
package/dist/index.js
CHANGED
|
@@ -3,16 +3,17 @@ import { MessageList } from '@mastra/core/agent';
|
|
|
3
3
|
import { MastraMemory } from '@mastra/core/memory';
|
|
4
4
|
import { embedMany } from 'ai';
|
|
5
5
|
import xxhash from 'xxhash-wasm';
|
|
6
|
+
import zodToJsonSchema from 'zod-to-json-schema';
|
|
6
7
|
import { z } from 'zod';
|
|
7
8
|
|
|
8
9
|
// src/index.ts
|
|
9
|
-
var updateWorkingMemoryTool = {
|
|
10
|
+
var updateWorkingMemoryTool = ({ format }) => ({
|
|
10
11
|
description: "Update the working memory with new information",
|
|
11
12
|
parameters: z.object({
|
|
12
|
-
memory: z.string().describe(
|
|
13
|
+
memory: z.string().describe(`The ${format === "json" ? "JSON" : "Markdown"} formatted working memory content to store`)
|
|
13
14
|
}),
|
|
14
15
|
execute: async (params) => {
|
|
15
|
-
const { context, threadId, memory } = params;
|
|
16
|
+
const { context, threadId, memory, resourceId } = params;
|
|
16
17
|
if (!threadId || !memory) {
|
|
17
18
|
throw new Error("Thread ID and Memory instance are required for working memory updates");
|
|
18
19
|
}
|
|
@@ -20,18 +21,22 @@ var updateWorkingMemoryTool = {
|
|
|
20
21
|
if (!thread) {
|
|
21
22
|
throw new Error(`Thread ${threadId} not found`);
|
|
22
23
|
}
|
|
24
|
+
if (thread.resourceId && thread.resourceId !== resourceId) {
|
|
25
|
+
throw new Error(`Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}`);
|
|
26
|
+
}
|
|
27
|
+
const workingMemory = context.memory;
|
|
23
28
|
await memory.saveThread({
|
|
24
29
|
thread: {
|
|
25
30
|
...thread,
|
|
26
31
|
metadata: {
|
|
27
32
|
...thread.metadata,
|
|
28
|
-
workingMemory
|
|
33
|
+
workingMemory
|
|
29
34
|
}
|
|
30
35
|
}
|
|
31
36
|
});
|
|
32
37
|
return { success: true };
|
|
33
38
|
}
|
|
34
|
-
};
|
|
39
|
+
});
|
|
35
40
|
|
|
36
41
|
// src/index.ts
|
|
37
42
|
var CHARS_PER_TOKEN = 4;
|
|
@@ -194,10 +199,14 @@ var Memory = class extends MastraMemory {
|
|
|
194
199
|
}) {
|
|
195
200
|
const config = this.getMergedThreadConfig(memoryConfig || {});
|
|
196
201
|
if (config.workingMemory?.enabled && !thread?.metadata?.workingMemory) {
|
|
202
|
+
let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
|
|
203
|
+
if (config.workingMemory.schema) {
|
|
204
|
+
workingMemory = JSON.stringify(zodToJsonSchema(config.workingMemory.schema));
|
|
205
|
+
}
|
|
197
206
|
return this.storage.saveThread({
|
|
198
207
|
thread: deepMerge(thread, {
|
|
199
208
|
metadata: {
|
|
200
|
-
workingMemory
|
|
209
|
+
workingMemory
|
|
201
210
|
}
|
|
202
211
|
})
|
|
203
212
|
});
|
|
@@ -393,12 +402,41 @@ var Memory = class extends MastraMemory {
|
|
|
393
402
|
}
|
|
394
403
|
return null;
|
|
395
404
|
}
|
|
396
|
-
async getWorkingMemory({
|
|
397
|
-
|
|
405
|
+
async getWorkingMemory({
|
|
406
|
+
threadId,
|
|
407
|
+
format
|
|
408
|
+
}) {
|
|
409
|
+
if (!this.threadConfig.workingMemory?.enabled) {
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
398
412
|
const thread = await this.storage.getThreadById({ threadId });
|
|
399
|
-
if (
|
|
400
|
-
|
|
401
|
-
|
|
413
|
+
if (format === "json") {
|
|
414
|
+
try {
|
|
415
|
+
return JSON.parse(thread?.metadata?.workingMemory) || null;
|
|
416
|
+
} catch (e) {
|
|
417
|
+
this.logger.error("Unable to parse working memory as JSON. Returning string.", e);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return thread?.metadata?.workingMemory ? JSON.stringify(thread?.metadata?.workingMemory) : null;
|
|
421
|
+
}
|
|
422
|
+
async getWorkingMemoryTemplate() {
|
|
423
|
+
if (!this.threadConfig.workingMemory?.enabled) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
if (this.threadConfig?.workingMemory?.schema) {
|
|
427
|
+
try {
|
|
428
|
+
const schema = this.threadConfig.workingMemory.schema;
|
|
429
|
+
const convertedSchema = zodToJsonSchema(schema, {
|
|
430
|
+
$refStrategy: "none"
|
|
431
|
+
});
|
|
432
|
+
return { format: "json", content: JSON.stringify(convertedSchema) };
|
|
433
|
+
} catch (error) {
|
|
434
|
+
this.logger.error("Error converting schema", error);
|
|
435
|
+
throw error;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const memory = this.threadConfig.workingMemory.template || this.defaultWorkingMemoryTemplate;
|
|
439
|
+
return { format: "markdown", content: memory.trim() };
|
|
402
440
|
}
|
|
403
441
|
async getSystemMessage({
|
|
404
442
|
threadId,
|
|
@@ -408,11 +446,28 @@ var Memory = class extends MastraMemory {
|
|
|
408
446
|
if (!config.workingMemory?.enabled) {
|
|
409
447
|
return null;
|
|
410
448
|
}
|
|
449
|
+
const workingMemoryTemplate = await this.getWorkingMemoryTemplate();
|
|
450
|
+
const workingMemoryData = await this.getWorkingMemory({ threadId });
|
|
451
|
+
if (!workingMemoryTemplate) {
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
return this.getWorkingMemoryToolInstruction({
|
|
455
|
+
template: workingMemoryTemplate,
|
|
456
|
+
data: workingMemoryData
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
async getUserContextMessage({ threadId }) {
|
|
411
460
|
const workingMemory = await this.getWorkingMemory({ threadId });
|
|
412
461
|
if (!workingMemory) {
|
|
413
462
|
return null;
|
|
414
463
|
}
|
|
415
|
-
return
|
|
464
|
+
return `The following is the most up-to-date information about the user's state and context:
|
|
465
|
+
${JSON.stringify(workingMemory)}
|
|
466
|
+
Use this information as the source of truth when generating responses.
|
|
467
|
+
Do not reference or mention this memory directly to the user.
|
|
468
|
+
If conversation history shows information that is not in the working memory, use the working memory as the source of truth.
|
|
469
|
+
If there is a discrepancy between this information and conversation history, always rely on this information unless the user explicitly asks for an update.
|
|
470
|
+
`;
|
|
416
471
|
}
|
|
417
472
|
defaultWorkingMemoryTemplate = `
|
|
418
473
|
# User Information
|
|
@@ -426,33 +481,44 @@ var Memory = class extends MastraMemory {
|
|
|
426
481
|
- **Facts**:
|
|
427
482
|
- **Projects**:
|
|
428
483
|
`;
|
|
429
|
-
getWorkingMemoryToolInstruction(
|
|
484
|
+
getWorkingMemoryToolInstruction({
|
|
485
|
+
template,
|
|
486
|
+
data
|
|
487
|
+
}) {
|
|
430
488
|
return `WORKING_MEMORY_SYSTEM_INSTRUCTION:
|
|
431
489
|
Store and update any conversation-relevant information by calling the updateWorkingMemory tool. If information might be referenced again - store it!
|
|
432
490
|
|
|
433
491
|
Guidelines:
|
|
434
492
|
1. Store anything that could be useful later in the conversation
|
|
435
493
|
2. Update proactively when information changes, no matter how small
|
|
436
|
-
3. Use Markdown format for all data
|
|
494
|
+
3. Use ${template.format === "json" ? "JSON" : "Markdown"} format for all data
|
|
437
495
|
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"
|
|
438
496
|
|
|
439
|
-
|
|
440
|
-
${
|
|
497
|
+
WORKING MEMORY TEMPLATE:
|
|
498
|
+
${template.content}
|
|
499
|
+
|
|
500
|
+
WORKING MEMORY DATA:
|
|
501
|
+
${data}
|
|
441
502
|
|
|
442
503
|
Notes:
|
|
443
504
|
- Update memory whenever referenced information changes
|
|
444
505
|
- If you're unsure whether to store something, store it (eg if the user tells you information about themselves, call updateWorkingMemory immediately to update it)
|
|
445
506
|
- 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
|
|
446
507
|
- Do not remove empty sections - you must include the empty sections along with the ones you're filling in
|
|
447
|
-
- REMEMBER: the way you update your working memory is by calling the updateWorkingMemory tool with the entire Markdown content. The system will store it for you. The user will not see it.
|
|
508
|
+
- REMEMBER: the way you update your working memory is by calling the updateWorkingMemory tool with the entire ${template.format === "json" ? "JSON" : "Markdown"} content. The system will store it for you. The user will not see it.
|
|
448
509
|
- IMPORTANT: You MUST call updateWorkingMemory in every response to a prompt where you received relevant information.
|
|
449
|
-
- IMPORTANT: Preserve the Markdown formatting structure above while updating the content.`;
|
|
510
|
+
- IMPORTANT: Preserve the ${template.format === "json" ? "JSON" : "Markdown"} formatting structure above while updating the content.`;
|
|
450
511
|
}
|
|
451
512
|
getTools(config) {
|
|
452
513
|
const mergedConfig = this.getMergedThreadConfig(config);
|
|
453
514
|
if (mergedConfig.workingMemory?.enabled) {
|
|
515
|
+
if (mergedConfig.workingMemory.schema) {
|
|
516
|
+
return {
|
|
517
|
+
updateWorkingMemory: updateWorkingMemoryTool({ format: "json" })
|
|
518
|
+
};
|
|
519
|
+
}
|
|
454
520
|
return {
|
|
455
|
-
updateWorkingMemory: updateWorkingMemoryTool
|
|
521
|
+
updateWorkingMemory: updateWorkingMemoryTool({ format: "markdown" })
|
|
456
522
|
};
|
|
457
523
|
}
|
|
458
524
|
return {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/memory",
|
|
3
|
-
"version": "0.10.4-alpha.
|
|
3
|
+
"version": "0.10.4-alpha.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"postgres": "^3.4.7",
|
|
41
41
|
"redis": "^4.7.1",
|
|
42
42
|
"xxhash-wasm": "^1.1.0",
|
|
43
|
-
"zod": "^3.25.57"
|
|
43
|
+
"zod": "^3.25.57",
|
|
44
|
+
"zod-to-json-schema": "^3.24.5"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@ai-sdk/openai": "^1.3.22",
|
|
@@ -53,7 +54,7 @@
|
|
|
53
54
|
"typescript-eslint": "^8.34.0",
|
|
54
55
|
"vitest": "^3.2.3",
|
|
55
56
|
"@internal/lint": "0.0.12",
|
|
56
|
-
"@mastra/core": "0.10.6-alpha.
|
|
57
|
+
"@mastra/core": "0.10.6-alpha.3"
|
|
57
58
|
},
|
|
58
59
|
"peerDependencies": {
|
|
59
60
|
"@mastra/core": ">=0.10.4-0 <0.11.0"
|
package/src/index.ts
CHANGED
|
@@ -3,12 +3,19 @@ import type { CoreTool, MastraMessageV1 } from '@mastra/core';
|
|
|
3
3
|
import { MessageList } from '@mastra/core/agent';
|
|
4
4
|
import type { MastraMessageV2 } from '@mastra/core/agent';
|
|
5
5
|
import { MastraMemory } from '@mastra/core/memory';
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
MemoryConfig,
|
|
8
|
+
SharedMemoryConfig,
|
|
9
|
+
StorageThreadType,
|
|
10
|
+
WorkingMemoryFormat,
|
|
11
|
+
WorkingMemoryTemplate,
|
|
12
|
+
} from '@mastra/core/memory';
|
|
7
13
|
import type { StorageGetMessagesArg } from '@mastra/core/storage';
|
|
8
14
|
import { embedMany } from 'ai';
|
|
9
15
|
import type { CoreMessage, TextPart, UIMessage } from 'ai';
|
|
10
16
|
|
|
11
17
|
import xxhash from 'xxhash-wasm';
|
|
18
|
+
import zodToJsonSchema from 'zod-to-json-schema';
|
|
12
19
|
import { updateWorkingMemoryTool } from './tools/working-memory';
|
|
13
20
|
|
|
14
21
|
// Average characters per token based on OpenAI's tokenization
|
|
@@ -247,10 +254,16 @@ export class Memory extends MastraMemory {
|
|
|
247
254
|
|
|
248
255
|
if (config.workingMemory?.enabled && !thread?.metadata?.workingMemory) {
|
|
249
256
|
// if working memory is enabled but the thread doesn't have it, we need to set it
|
|
257
|
+
let workingMemory = config.workingMemory.template || this.defaultWorkingMemoryTemplate;
|
|
258
|
+
|
|
259
|
+
if (config.workingMemory.schema) {
|
|
260
|
+
workingMemory = JSON.stringify(zodToJsonSchema(config.workingMemory.schema));
|
|
261
|
+
}
|
|
262
|
+
|
|
250
263
|
return this.storage.saveThread({
|
|
251
264
|
thread: deepMerge(thread, {
|
|
252
265
|
metadata: {
|
|
253
|
-
workingMemory
|
|
266
|
+
workingMemory,
|
|
254
267
|
},
|
|
255
268
|
}),
|
|
256
269
|
});
|
|
@@ -540,20 +553,54 @@ export class Memory extends MastraMemory {
|
|
|
540
553
|
return null;
|
|
541
554
|
}
|
|
542
555
|
|
|
543
|
-
public async getWorkingMemory({
|
|
544
|
-
|
|
556
|
+
public async getWorkingMemory({
|
|
557
|
+
threadId,
|
|
558
|
+
format,
|
|
559
|
+
}: {
|
|
560
|
+
threadId: string;
|
|
561
|
+
format?: WorkingMemoryFormat;
|
|
562
|
+
}): Promise<string | null> {
|
|
563
|
+
if (!this.threadConfig.workingMemory?.enabled) {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
545
566
|
|
|
546
|
-
// Get thread from storage
|
|
547
567
|
const thread = await this.storage.getThreadById({ threadId });
|
|
548
|
-
|
|
568
|
+
|
|
569
|
+
if (format === 'json') {
|
|
570
|
+
try {
|
|
571
|
+
return JSON.parse(thread?.metadata?.workingMemory as string) || null;
|
|
572
|
+
} catch (e) {
|
|
573
|
+
this.logger.error('Unable to parse working memory as JSON. Returning string.', e);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return thread?.metadata?.workingMemory ? JSON.stringify(thread?.metadata?.workingMemory) : null;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
public async getWorkingMemoryTemplate(): Promise<WorkingMemoryTemplate | null> {
|
|
581
|
+
if (!this.threadConfig.workingMemory?.enabled) {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Get thread from storage
|
|
586
|
+
if (this.threadConfig?.workingMemory?.schema) {
|
|
587
|
+
try {
|
|
588
|
+
const schema = this.threadConfig.workingMemory.schema;
|
|
589
|
+
const convertedSchema = zodToJsonSchema(schema, {
|
|
590
|
+
$refStrategy: 'none',
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
return { format: 'json', content: JSON.stringify(convertedSchema) };
|
|
594
|
+
} catch (error) {
|
|
595
|
+
this.logger.error('Error converting schema', error);
|
|
596
|
+
throw error;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
549
599
|
|
|
550
600
|
// Return working memory from metadata
|
|
551
|
-
const memory =
|
|
552
|
-
(thread.metadata?.workingMemory as string) ||
|
|
553
|
-
this.threadConfig.workingMemory.template ||
|
|
554
|
-
this.defaultWorkingMemoryTemplate;
|
|
601
|
+
const memory = this.threadConfig.workingMemory.template || this.defaultWorkingMemoryTemplate;
|
|
555
602
|
|
|
556
|
-
return memory.trim();
|
|
603
|
+
return { format: 'markdown', content: memory.trim() };
|
|
557
604
|
}
|
|
558
605
|
|
|
559
606
|
public async getSystemMessage({
|
|
@@ -568,12 +615,32 @@ export class Memory extends MastraMemory {
|
|
|
568
615
|
return null;
|
|
569
616
|
}
|
|
570
617
|
|
|
618
|
+
const workingMemoryTemplate = await this.getWorkingMemoryTemplate();
|
|
619
|
+
const workingMemoryData = await this.getWorkingMemory({ threadId });
|
|
620
|
+
|
|
621
|
+
if (!workingMemoryTemplate) {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return this.getWorkingMemoryToolInstruction({
|
|
626
|
+
template: workingMemoryTemplate,
|
|
627
|
+
data: workingMemoryData,
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
public async getUserContextMessage({ threadId }: { threadId: string }) {
|
|
571
632
|
const workingMemory = await this.getWorkingMemory({ threadId });
|
|
572
633
|
if (!workingMemory) {
|
|
573
634
|
return null;
|
|
574
635
|
}
|
|
575
636
|
|
|
576
|
-
return
|
|
637
|
+
return `The following is the most up-to-date information about the user's state and context:
|
|
638
|
+
${JSON.stringify(workingMemory)}
|
|
639
|
+
Use this information as the source of truth when generating responses.
|
|
640
|
+
Do not reference or mention this memory directly to the user.
|
|
641
|
+
If conversation history shows information that is not in the working memory, use the working memory as the source of truth.
|
|
642
|
+
If there is a discrepancy between this information and conversation history, always rely on this information unless the user explicitly asks for an update.
|
|
643
|
+
`;
|
|
577
644
|
}
|
|
578
645
|
|
|
579
646
|
public defaultWorkingMemoryTemplate = `
|
|
@@ -589,34 +656,49 @@ export class Memory extends MastraMemory {
|
|
|
589
656
|
- **Projects**:
|
|
590
657
|
`;
|
|
591
658
|
|
|
592
|
-
private getWorkingMemoryToolInstruction(
|
|
659
|
+
private getWorkingMemoryToolInstruction({
|
|
660
|
+
template,
|
|
661
|
+
data,
|
|
662
|
+
}: {
|
|
663
|
+
template: WorkingMemoryTemplate;
|
|
664
|
+
data: string | null;
|
|
665
|
+
}) {
|
|
593
666
|
return `WORKING_MEMORY_SYSTEM_INSTRUCTION:
|
|
594
667
|
Store and update any conversation-relevant information by calling the updateWorkingMemory tool. If information might be referenced again - store it!
|
|
595
668
|
|
|
596
669
|
Guidelines:
|
|
597
670
|
1. Store anything that could be useful later in the conversation
|
|
598
671
|
2. Update proactively when information changes, no matter how small
|
|
599
|
-
3. Use Markdown format for all data
|
|
672
|
+
3. Use ${template.format === 'json' ? 'JSON' : 'Markdown'} format for all data
|
|
600
673
|
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"
|
|
601
674
|
|
|
602
|
-
|
|
603
|
-
${
|
|
675
|
+
WORKING MEMORY TEMPLATE:
|
|
676
|
+
${template.content}
|
|
677
|
+
|
|
678
|
+
WORKING MEMORY DATA:
|
|
679
|
+
${data}
|
|
604
680
|
|
|
605
681
|
Notes:
|
|
606
682
|
- Update memory whenever referenced information changes
|
|
607
683
|
- If you're unsure whether to store something, store it (eg if the user tells you information about themselves, call updateWorkingMemory immediately to update it)
|
|
608
684
|
- 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
|
|
609
685
|
- Do not remove empty sections - you must include the empty sections along with the ones you're filling in
|
|
610
|
-
- REMEMBER: the way you update your working memory is by calling the updateWorkingMemory tool with the entire Markdown content. The system will store it for you. The user will not see it.
|
|
686
|
+
- REMEMBER: the way you update your working memory is by calling the updateWorkingMemory tool with the entire ${template.format === 'json' ? 'JSON' : 'Markdown'} content. The system will store it for you. The user will not see it.
|
|
611
687
|
- IMPORTANT: You MUST call updateWorkingMemory in every response to a prompt where you received relevant information.
|
|
612
|
-
- IMPORTANT: Preserve the Markdown formatting structure above while updating the content.`;
|
|
688
|
+
- IMPORTANT: Preserve the ${template.format === 'json' ? 'JSON' : 'Markdown'} formatting structure above while updating the content.`;
|
|
613
689
|
}
|
|
614
690
|
|
|
615
691
|
public getTools(config?: MemoryConfig): Record<string, CoreTool> {
|
|
616
692
|
const mergedConfig = this.getMergedThreadConfig(config);
|
|
617
693
|
if (mergedConfig.workingMemory?.enabled) {
|
|
694
|
+
if (mergedConfig.workingMemory.schema) {
|
|
695
|
+
return {
|
|
696
|
+
updateWorkingMemory: updateWorkingMemoryTool({ format: 'json' }),
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
|
|
618
700
|
return {
|
|
619
|
-
updateWorkingMemory: updateWorkingMemoryTool,
|
|
701
|
+
updateWorkingMemory: updateWorkingMemoryTool({ format: 'markdown' }),
|
|
620
702
|
};
|
|
621
703
|
}
|
|
622
704
|
return {};
|
|
@@ -1,33 +1,43 @@
|
|
|
1
1
|
import type { CoreTool } from '@mastra/core';
|
|
2
|
+
import type { WorkingMemoryFormat } from '@mastra/core/memory';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
|
|
4
|
-
export const updateWorkingMemoryTool: CoreTool
|
|
5
|
+
export const updateWorkingMemoryTool = ({ format }: { format: WorkingMemoryFormat }): CoreTool => ({
|
|
5
6
|
description: 'Update the working memory with new information',
|
|
6
7
|
parameters: z.object({
|
|
7
|
-
memory: z
|
|
8
|
+
memory: z
|
|
9
|
+
.string()
|
|
10
|
+
.describe(`The ${format === 'json' ? 'JSON' : 'Markdown'} formatted working memory content to store`),
|
|
8
11
|
}),
|
|
9
12
|
execute: async (params: any) => {
|
|
10
|
-
const { context, threadId, memory } = params;
|
|
13
|
+
const { context, threadId, memory, resourceId } = params;
|
|
11
14
|
if (!threadId || !memory) {
|
|
12
15
|
throw new Error('Thread ID and Memory instance are required for working memory updates');
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
const thread = await memory.getThreadById({ threadId });
|
|
19
|
+
|
|
16
20
|
if (!thread) {
|
|
17
21
|
throw new Error(`Thread ${threadId} not found`);
|
|
18
22
|
}
|
|
19
23
|
|
|
24
|
+
if (thread.resourceId && thread.resourceId !== resourceId) {
|
|
25
|
+
throw new Error(`Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const workingMemory = context.memory;
|
|
29
|
+
|
|
20
30
|
// Update thread metadata with new working memory
|
|
21
31
|
await memory.saveThread({
|
|
22
32
|
thread: {
|
|
23
33
|
...thread,
|
|
24
34
|
metadata: {
|
|
25
35
|
...thread.metadata,
|
|
26
|
-
workingMemory:
|
|
36
|
+
workingMemory: workingMemory,
|
|
27
37
|
},
|
|
28
38
|
},
|
|
29
39
|
});
|
|
30
40
|
|
|
31
41
|
return { success: true };
|
|
32
42
|
},
|
|
33
|
-
};
|
|
43
|
+
});
|