@mastra/memory 0.2.10 → 0.2.11-alpha.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/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +22 -0
- package/dist/_tsup-dts-rollup.d.cts +6 -0
- package/dist/_tsup-dts-rollup.d.ts +6 -0
- package/dist/index.cjs +60 -16
- package/dist/index.js +60 -15
- package/package.json +2 -2
- package/src/index.ts +30 -17
- package/src/utils/index.ts +88 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
|
|
2
|
-
> @mastra/memory@0.2.
|
|
2
|
+
> @mastra/memory@0.2.11-alpha.2 build /home/runner/work/mastra/mastra/packages/memory
|
|
3
3
|
> pnpm run check && tsup src/index.ts src/processors/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @mastra/memory@0.2.
|
|
6
|
+
> @mastra/memory@0.2.11-alpha.2 check /home/runner/work/mastra/mastra/packages/memory
|
|
7
7
|
> tsc --noEmit
|
|
8
8
|
|
|
9
9
|
[34mCLI[39m Building entry: src/index.ts, src/processors/index.ts
|
|
10
10
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
11
11
|
[34mCLI[39m tsup v8.4.0
|
|
12
12
|
[34mTSC[39m Build start
|
|
13
|
-
[32mTSC[39m ⚡️ Build success in
|
|
13
|
+
[32mTSC[39m ⚡️ Build success in 10363ms
|
|
14
14
|
[34mDTS[39m Build start
|
|
15
15
|
[34mCLI[39m Target: es2022
|
|
16
16
|
Analysis will use the bundled TypeScript version 5.8.2
|
|
17
17
|
[36mWriting package typings: /home/runner/work/mastra/mastra/packages/memory/dist/_tsup-dts-rollup.d.ts[39m
|
|
18
18
|
Analysis will use the bundled TypeScript version 5.8.2
|
|
19
19
|
[36mWriting package typings: /home/runner/work/mastra/mastra/packages/memory/dist/_tsup-dts-rollup.d.cts[39m
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 12416ms
|
|
21
21
|
[34mCLI[39m Cleaning output folder
|
|
22
22
|
[34mESM[39m Build start
|
|
23
23
|
[34mCJS[39m Build start
|
|
24
|
-
[
|
|
25
|
-
[32mESM[39m [1mdist/processors/index.js [22m[32m5.33 KB[39m
|
|
26
|
-
[32mESM[39m ⚡️ Build success in 696ms
|
|
27
|
-
[32mCJS[39m [1mdist/index.cjs [22m[32m15.71 KB[39m
|
|
24
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m17.58 KB[39m
|
|
28
25
|
[32mCJS[39m [1mdist/processors/index.cjs [22m[32m5.54 KB[39m
|
|
29
|
-
[32mCJS[39m ⚡️ Build success in
|
|
26
|
+
[32mCJS[39m ⚡️ Build success in 946ms
|
|
27
|
+
[32mESM[39m [1mdist/index.js [22m[32m17.39 KB[39m
|
|
28
|
+
[32mESM[39m [1mdist/processors/index.js [22m[32m5.33 KB[39m
|
|
29
|
+
[32mESM[39m ⚡️ Build success in 950ms
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @mastra/memory
|
|
2
2
|
|
|
3
|
+
## 0.2.11-alpha.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5c6825c: [MASTRA-2782] removed tiktoken from memory chunktext
|
|
8
|
+
|
|
9
|
+
## 0.2.11-alpha.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 6f92295: Fixed an issue where some user messages and llm messages would have the exact same createdAt date, leading to incorrect message ordering. Added a fix for new messages as well as any that were saved before the fix in the wrong order
|
|
14
|
+
- Updated dependencies [8a8a73b]
|
|
15
|
+
- Updated dependencies [6f92295]
|
|
16
|
+
- @mastra/core@0.8.4-alpha.1
|
|
17
|
+
|
|
18
|
+
## 0.2.11-alpha.0
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [03f3cd0]
|
|
23
|
+
- @mastra/core@0.8.4-alpha.0
|
|
24
|
+
|
|
3
25
|
## 0.2.10
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
|
@@ -76,6 +76,12 @@ export declare class Memory extends MastraMemory {
|
|
|
76
76
|
getTools(config?: MemoryConfig): Record<string, CoreTool>;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Self-heals message ordering to ensure tool calls are directly before their matching tool results.
|
|
81
|
+
* This is needed due to a bug where messages were saved in the wrong order. That bug is fixed, but this code ensures any tool calls saved in the wrong order in the past will still be usable now.
|
|
82
|
+
*/
|
|
83
|
+
export declare function reorderToolCallsAndResults(messages: MessageType[]): MessageType[];
|
|
84
|
+
|
|
79
85
|
/**
|
|
80
86
|
* Limits the total number of tokens in the messages.
|
|
81
87
|
* Uses js-tiktoken with o200k_base encoding by default for accurate token counting with modern models.
|
|
@@ -76,6 +76,12 @@ export declare class Memory extends MastraMemory {
|
|
|
76
76
|
getTools(config?: MemoryConfig): Record<string, CoreTool>;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Self-heals message ordering to ensure tool calls are directly before their matching tool results.
|
|
81
|
+
* This is needed due to a bug where messages were saved in the wrong order. That bug is fixed, but this code ensures any tool calls saved in the wrong order in the past will still be usable now.
|
|
82
|
+
*/
|
|
83
|
+
export declare function reorderToolCallsAndResults(messages: MessageType[]): MessageType[];
|
|
84
|
+
|
|
79
85
|
/**
|
|
80
86
|
* Limits the total number of tokens in the messages.
|
|
81
87
|
* Uses js-tiktoken with o200k_base encoding by default for accurate token counting with modern models.
|
package/dist/index.cjs
CHANGED
|
@@ -3,14 +3,11 @@
|
|
|
3
3
|
var core = require('@mastra/core');
|
|
4
4
|
var memory = require('@mastra/core/memory');
|
|
5
5
|
var ai = require('ai');
|
|
6
|
-
var lite = require('js-tiktoken/lite');
|
|
7
|
-
var o200k_base = require('js-tiktoken/ranks/o200k_base');
|
|
8
6
|
var xxhash = require('xxhash-wasm');
|
|
9
7
|
var zod = require('zod');
|
|
10
8
|
|
|
11
9
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
10
|
|
|
13
|
-
var o200k_base__default = /*#__PURE__*/_interopDefault(o200k_base);
|
|
14
11
|
var xxhash__default = /*#__PURE__*/_interopDefault(xxhash);
|
|
15
12
|
|
|
16
13
|
// src/index.ts
|
|
@@ -41,8 +38,50 @@ var updateWorkingMemoryTool = {
|
|
|
41
38
|
}
|
|
42
39
|
};
|
|
43
40
|
|
|
41
|
+
// src/utils/index.ts
|
|
42
|
+
var isToolCallWithId = (message, targetToolCallId) => {
|
|
43
|
+
if (!message || !Array.isArray(message.content)) return false;
|
|
44
|
+
return message.content.some(
|
|
45
|
+
(part) => part && typeof part === "object" && "type" in part && part.type === "tool-call" && "toolCallId" in part && part.toolCallId === targetToolCallId
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
var getToolResultIndexById = (id, results) => results.findIndex((message) => {
|
|
49
|
+
if (!Array.isArray(message?.content)) return false;
|
|
50
|
+
return message.content.some(
|
|
51
|
+
(part) => part && typeof part === "object" && "type" in part && part.type === "tool-result" && "toolCallId" in part && part.toolCallId === id
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
function reorderToolCallsAndResults(messages) {
|
|
55
|
+
if (!messages.length) return messages;
|
|
56
|
+
const results = [...messages];
|
|
57
|
+
const toolCallIds = /* @__PURE__ */ new Set();
|
|
58
|
+
for (const message of results) {
|
|
59
|
+
if (!Array.isArray(message.content)) continue;
|
|
60
|
+
for (const part of message.content) {
|
|
61
|
+
if (part && typeof part === "object" && "type" in part && part.type === "tool-result" && "toolCallId" in part && part.toolCallId) {
|
|
62
|
+
toolCallIds.add(part.toolCallId);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const toolCallId of toolCallIds) {
|
|
67
|
+
const resultIndex = getToolResultIndexById(toolCallId, results);
|
|
68
|
+
const oneMessagePrev = results[resultIndex - 1];
|
|
69
|
+
if (isToolCallWithId(oneMessagePrev, toolCallId)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const toolCallIndex = results.findIndex((message) => isToolCallWithId(message, toolCallId));
|
|
73
|
+
if (toolCallIndex !== -1 && toolCallIndex !== resultIndex - 1) {
|
|
74
|
+
const toolCall = results[toolCallIndex];
|
|
75
|
+
if (!toolCall) continue;
|
|
76
|
+
results.splice(toolCallIndex, 1);
|
|
77
|
+
results.splice(getToolResultIndexById(toolCallId, results), 0, toolCall);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return results;
|
|
81
|
+
}
|
|
82
|
+
|
|
44
83
|
// src/index.ts
|
|
45
|
-
var
|
|
84
|
+
var CHARS_PER_TOKEN = 4;
|
|
46
85
|
var Memory = class extends memory.MastraMemory {
|
|
47
86
|
constructor(config = {}) {
|
|
48
87
|
super({ name: "Memory", ...config });
|
|
@@ -123,8 +162,10 @@ var Memory = class extends memory.MastraMemory {
|
|
|
123
162
|
},
|
|
124
163
|
threadConfig: config
|
|
125
164
|
});
|
|
126
|
-
const
|
|
127
|
-
const
|
|
165
|
+
const orderedByDate = rawMessages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
166
|
+
const reorderedToolCalls = reorderToolCallsAndResults(orderedByDate);
|
|
167
|
+
const messages = this.parseMessages(reorderedToolCalls);
|
|
168
|
+
const uiMessages = this.convertToUIMessages(reorderedToolCalls);
|
|
128
169
|
return { messages, uiMessages };
|
|
129
170
|
}
|
|
130
171
|
async rememberMessages({
|
|
@@ -193,19 +234,22 @@ var Memory = class extends memory.MastraMemory {
|
|
|
193
234
|
async deleteThread(threadId) {
|
|
194
235
|
await this.storage.__deleteThread({ threadId });
|
|
195
236
|
}
|
|
196
|
-
chunkText(text,
|
|
197
|
-
const
|
|
237
|
+
chunkText(text, tokenSize = 4096) {
|
|
238
|
+
const charSize = tokenSize * CHARS_PER_TOKEN;
|
|
198
239
|
const chunks = [];
|
|
199
|
-
let currentChunk =
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
currentChunk
|
|
240
|
+
let currentChunk = "";
|
|
241
|
+
const words = text.split(/\s+/);
|
|
242
|
+
for (const word of words) {
|
|
243
|
+
const wordWithSpace = currentChunk ? " " + word : word;
|
|
244
|
+
if (currentChunk.length + wordWithSpace.length > charSize) {
|
|
245
|
+
chunks.push(currentChunk);
|
|
246
|
+
currentChunk = word;
|
|
247
|
+
} else {
|
|
248
|
+
currentChunk += wordWithSpace;
|
|
205
249
|
}
|
|
206
250
|
}
|
|
207
|
-
if (currentChunk
|
|
208
|
-
chunks.push(
|
|
251
|
+
if (currentChunk) {
|
|
252
|
+
chunks.push(currentChunk);
|
|
209
253
|
}
|
|
210
254
|
return chunks;
|
|
211
255
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { deepMerge } from '@mastra/core';
|
|
2
2
|
import { MastraMemory } from '@mastra/core/memory';
|
|
3
3
|
import { embedMany } from 'ai';
|
|
4
|
-
import { Tiktoken } from 'js-tiktoken/lite';
|
|
5
|
-
import o200k_base from 'js-tiktoken/ranks/o200k_base';
|
|
6
4
|
import xxhash from 'xxhash-wasm';
|
|
7
5
|
import { z } from 'zod';
|
|
8
6
|
|
|
@@ -34,8 +32,50 @@ var updateWorkingMemoryTool = {
|
|
|
34
32
|
}
|
|
35
33
|
};
|
|
36
34
|
|
|
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
|
+
|
|
37
77
|
// src/index.ts
|
|
38
|
-
var
|
|
78
|
+
var CHARS_PER_TOKEN = 4;
|
|
39
79
|
var Memory = class extends MastraMemory {
|
|
40
80
|
constructor(config = {}) {
|
|
41
81
|
super({ name: "Memory", ...config });
|
|
@@ -116,8 +156,10 @@ var Memory = class extends MastraMemory {
|
|
|
116
156
|
},
|
|
117
157
|
threadConfig: config
|
|
118
158
|
});
|
|
119
|
-
const
|
|
120
|
-
const
|
|
159
|
+
const orderedByDate = rawMessages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
160
|
+
const reorderedToolCalls = reorderToolCallsAndResults(orderedByDate);
|
|
161
|
+
const messages = this.parseMessages(reorderedToolCalls);
|
|
162
|
+
const uiMessages = this.convertToUIMessages(reorderedToolCalls);
|
|
121
163
|
return { messages, uiMessages };
|
|
122
164
|
}
|
|
123
165
|
async rememberMessages({
|
|
@@ -186,19 +228,22 @@ var Memory = class extends MastraMemory {
|
|
|
186
228
|
async deleteThread(threadId) {
|
|
187
229
|
await this.storage.__deleteThread({ threadId });
|
|
188
230
|
}
|
|
189
|
-
chunkText(text,
|
|
190
|
-
const
|
|
231
|
+
chunkText(text, tokenSize = 4096) {
|
|
232
|
+
const charSize = tokenSize * CHARS_PER_TOKEN;
|
|
191
233
|
const chunks = [];
|
|
192
|
-
let currentChunk =
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
currentChunk
|
|
234
|
+
let currentChunk = "";
|
|
235
|
+
const words = text.split(/\s+/);
|
|
236
|
+
for (const word of words) {
|
|
237
|
+
const wordWithSpace = currentChunk ? " " + word : word;
|
|
238
|
+
if (currentChunk.length + wordWithSpace.length > charSize) {
|
|
239
|
+
chunks.push(currentChunk);
|
|
240
|
+
currentChunk = word;
|
|
241
|
+
} else {
|
|
242
|
+
currentChunk += wordWithSpace;
|
|
198
243
|
}
|
|
199
244
|
}
|
|
200
|
-
if (currentChunk
|
|
201
|
-
chunks.push(
|
|
245
|
+
if (currentChunk) {
|
|
246
|
+
chunks.push(currentChunk);
|
|
202
247
|
}
|
|
203
248
|
return chunks;
|
|
204
249
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/memory",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11-alpha.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"redis": "^4.7.0",
|
|
42
42
|
"xxhash-wasm": "^1.1.0",
|
|
43
43
|
"zod": "^3.24.2",
|
|
44
|
-
"@mastra/core": "^0.8.
|
|
44
|
+
"@mastra/core": "^0.8.4-alpha.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@ai-sdk/openai": "^1.3.3",
|
package/src/index.ts
CHANGED
|
@@ -4,12 +4,13 @@ import { MastraMemory } from '@mastra/core/memory';
|
|
|
4
4
|
import type { MessageType, MemoryConfig, SharedMemoryConfig, StorageThreadType } from '@mastra/core/memory';
|
|
5
5
|
import type { StorageGetMessagesArg } from '@mastra/core/storage';
|
|
6
6
|
import { embedMany } from 'ai';
|
|
7
|
-
|
|
8
|
-
import o200k_base from 'js-tiktoken/ranks/o200k_base';
|
|
7
|
+
|
|
9
8
|
import xxhash from 'xxhash-wasm';
|
|
10
9
|
import { updateWorkingMemoryTool } from './tools/working-memory';
|
|
10
|
+
import { reorderToolCallsAndResults } from './utils';
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// Average characters per token based on OpenAI's tokenization
|
|
13
|
+
const CHARS_PER_TOKEN = 4;
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Concrete implementation of MastraMemory that adds support for thread configuration
|
|
@@ -126,9 +127,14 @@ export class Memory extends MastraMemory {
|
|
|
126
127
|
threadConfig: config,
|
|
127
128
|
});
|
|
128
129
|
|
|
130
|
+
// First sort messages by date
|
|
131
|
+
const orderedByDate = rawMessages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
132
|
+
// Then reorder tool calls to be directly before their results
|
|
133
|
+
const reorderedToolCalls = reorderToolCallsAndResults(orderedByDate);
|
|
134
|
+
|
|
129
135
|
// Parse and convert messages
|
|
130
|
-
const messages = this.parseMessages(
|
|
131
|
-
const uiMessages = this.convertToUIMessages(
|
|
136
|
+
const messages = this.parseMessages(reorderedToolCalls);
|
|
137
|
+
const uiMessages = this.convertToUIMessages(reorderedToolCalls);
|
|
132
138
|
|
|
133
139
|
return { messages, uiMessages };
|
|
134
140
|
}
|
|
@@ -227,24 +233,31 @@ export class Memory extends MastraMemory {
|
|
|
227
233
|
await this.storage.__deleteThread({ threadId });
|
|
228
234
|
}
|
|
229
235
|
|
|
230
|
-
private chunkText(text: string,
|
|
231
|
-
|
|
236
|
+
private chunkText(text: string, tokenSize = 4096) {
|
|
237
|
+
// Convert token size to character size with some buffer
|
|
238
|
+
const charSize = tokenSize * CHARS_PER_TOKEN;
|
|
232
239
|
const chunks: string[] = [];
|
|
233
|
-
let currentChunk
|
|
240
|
+
let currentChunk = '';
|
|
241
|
+
|
|
242
|
+
// Split text into words to avoid breaking words
|
|
243
|
+
const words = text.split(/\s+/);
|
|
234
244
|
|
|
235
|
-
for (const
|
|
236
|
-
|
|
245
|
+
for (const word of words) {
|
|
246
|
+
// Add space before word unless it's the first word in the chunk
|
|
247
|
+
const wordWithSpace = currentChunk ? ' ' + word : word;
|
|
237
248
|
|
|
238
|
-
// If
|
|
239
|
-
if (currentChunk.length
|
|
240
|
-
chunks.push(
|
|
241
|
-
currentChunk =
|
|
249
|
+
// If adding this word would exceed the chunk size, start a new chunk
|
|
250
|
+
if (currentChunk.length + wordWithSpace.length > charSize) {
|
|
251
|
+
chunks.push(currentChunk);
|
|
252
|
+
currentChunk = word;
|
|
253
|
+
} else {
|
|
254
|
+
currentChunk += wordWithSpace;
|
|
242
255
|
}
|
|
243
256
|
}
|
|
244
257
|
|
|
245
|
-
// Add
|
|
246
|
-
if (currentChunk
|
|
247
|
-
chunks.push(
|
|
258
|
+
// Add the final chunk if not empty
|
|
259
|
+
if (currentChunk) {
|
|
260
|
+
chunks.push(currentChunk);
|
|
248
261
|
}
|
|
249
262
|
|
|
250
263
|
return chunks;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { MessageType } from '@mastra/core/memory';
|
|
2
|
+
|
|
3
|
+
const isToolCallWithId = (message: MessageType | undefined, targetToolCallId: string): boolean => {
|
|
4
|
+
if (!message || !Array.isArray(message.content)) return false;
|
|
5
|
+
return message.content.some(
|
|
6
|
+
part =>
|
|
7
|
+
part &&
|
|
8
|
+
typeof part === 'object' &&
|
|
9
|
+
'type' in part &&
|
|
10
|
+
part.type === 'tool-call' &&
|
|
11
|
+
'toolCallId' in part &&
|
|
12
|
+
part.toolCallId === targetToolCallId,
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const getToolResultIndexById = (id: string, results: MessageType[]) =>
|
|
17
|
+
results.findIndex(message => {
|
|
18
|
+
if (!Array.isArray(message?.content)) return false;
|
|
19
|
+
return message.content.some(
|
|
20
|
+
part =>
|
|
21
|
+
part &&
|
|
22
|
+
typeof part === 'object' &&
|
|
23
|
+
'type' in part &&
|
|
24
|
+
part.type === 'tool-result' &&
|
|
25
|
+
'toolCallId' in part &&
|
|
26
|
+
part.toolCallId === id,
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Self-heals message ordering to ensure tool calls are directly before their matching tool results.
|
|
32
|
+
* This is needed due to a bug where messages were saved in the wrong order. That bug is fixed, but this code ensures any tool calls saved in the wrong order in the past will still be usable now.
|
|
33
|
+
*/
|
|
34
|
+
export function reorderToolCallsAndResults(messages: MessageType[]): MessageType[] {
|
|
35
|
+
if (!messages.length) return messages;
|
|
36
|
+
|
|
37
|
+
// Create a copy of messages to avoid modifying the original
|
|
38
|
+
const results = [...messages];
|
|
39
|
+
|
|
40
|
+
const toolCallIds = new Set<string>();
|
|
41
|
+
|
|
42
|
+
// First loop: collect all tool result IDs in a set
|
|
43
|
+
for (const message of results) {
|
|
44
|
+
if (!Array.isArray(message.content)) continue;
|
|
45
|
+
|
|
46
|
+
for (const part of message.content) {
|
|
47
|
+
if (
|
|
48
|
+
part &&
|
|
49
|
+
typeof part === 'object' &&
|
|
50
|
+
'type' in part &&
|
|
51
|
+
part.type === 'tool-result' &&
|
|
52
|
+
'toolCallId' in part &&
|
|
53
|
+
part.toolCallId
|
|
54
|
+
) {
|
|
55
|
+
toolCallIds.add(part.toolCallId);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Second loop: for each tool ID, ensure tool calls come before tool results
|
|
61
|
+
for (const toolCallId of toolCallIds) {
|
|
62
|
+
// Find tool result index
|
|
63
|
+
const resultIndex = getToolResultIndexById(toolCallId, results);
|
|
64
|
+
|
|
65
|
+
// Check if tool call is at resultIndex - 1
|
|
66
|
+
const oneMessagePrev = results[resultIndex - 1];
|
|
67
|
+
if (isToolCallWithId(oneMessagePrev, toolCallId)) {
|
|
68
|
+
continue; // Tool call is already in the correct position
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Find the tool call anywhere in the array
|
|
72
|
+
const toolCallIndex = results.findIndex(message => isToolCallWithId(message, toolCallId));
|
|
73
|
+
|
|
74
|
+
if (toolCallIndex !== -1 && toolCallIndex !== resultIndex - 1) {
|
|
75
|
+
// Store the tool call message
|
|
76
|
+
const toolCall = results[toolCallIndex];
|
|
77
|
+
if (!toolCall) continue;
|
|
78
|
+
|
|
79
|
+
// Remove the tool call from its current position
|
|
80
|
+
results.splice(toolCallIndex, 1);
|
|
81
|
+
|
|
82
|
+
// Insert right before the tool result
|
|
83
|
+
results.splice(getToolResultIndexById(toolCallId, results), 0, toolCall);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return results;
|
|
88
|
+
}
|