@mastra/upstash 0.10.3-alpha.1 → 0.10.3-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 +15 -15
- package/CHANGELOG.md +8 -0
- package/dist/_tsup-dts-rollup.d.cts +3 -0
- package/dist/_tsup-dts-rollup.d.ts +3 -0
- package/dist/index.cjs +95 -43
- package/dist/index.js +95 -43
- package/package.json +3 -3
- package/src/storage/index.ts +64 -23
- package/src/storage/upstash.test.ts +114 -15
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
|
|
2
|
-
> @mastra/upstash@0.10.3-alpha.
|
|
2
|
+
> @mastra/upstash@0.10.3-alpha.2 build /home/runner/work/mastra/mastra/stores/upstash
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
7
|
[34mCLI[39m tsup v8.5.0
|
|
8
8
|
[34mTSC[39m Build start
|
|
9
|
-
[32mTSC[39m ⚡️ Build success in
|
|
9
|
+
[32mTSC[39m ⚡️ Build success in 10231ms
|
|
10
10
|
[34mDTS[39m Build start
|
|
11
11
|
[34mCLI[39m Target: es2022
|
|
12
12
|
Analysis will use the bundled TypeScript version 5.8.3
|
|
13
13
|
[36mWriting package typings: /home/runner/work/mastra/mastra/stores/upstash/dist/_tsup-dts-rollup.d.ts[39m
|
|
14
14
|
Analysis will use the bundled TypeScript version 5.8.3
|
|
15
15
|
[36mWriting package typings: /home/runner/work/mastra/mastra/stores/upstash/dist/_tsup-dts-rollup.d.cts[39m
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 10756ms
|
|
17
17
|
[34mCLI[39m Cleaning output folder
|
|
18
18
|
[34mESM[39m Build start
|
|
19
19
|
[34mCJS[39m Build start
|
|
20
|
-
dist/index.cjs (1651:18): Use of eval in "dist/index.cjs" is strongly discouraged as it poses security risks and may cause issues with minification.
|
|
21
20
|
dist/index.js (1651:18): Use of eval in "dist/index.js" is strongly discouraged as it poses security risks and may cause issues with minification.
|
|
21
|
+
dist/index.cjs (1651:18): Use of eval in "dist/index.cjs" is strongly discouraged as it poses security risks and may cause issues with minification.
|
|
22
|
+
[32mESM[39m [1mdist/getMachineId-darwin-UTKBTJ2U.js [22m[32m1.08 KB[39m
|
|
23
|
+
[32mESM[39m [1mdist/getMachineId-linux-K3QXQYAB.js [22m[32m740.00 B[39m
|
|
24
|
+
[32mESM[39m [1mdist/getMachineId-bsd-KKIDU47O.js [22m[32m896.00 B[39m
|
|
25
|
+
[32mESM[39m [1mdist/getMachineId-win-L2EYIM5A.js [22m[32m1.04 KB[39m
|
|
26
|
+
[32mESM[39m [1mdist/chunk-IGKEDEDE.js [22m[32m452.00 B[39m
|
|
27
|
+
[32mESM[39m [1mdist/getMachineId-unsupported-VPWBQCK7.js [22m[32m700.00 B[39m
|
|
28
|
+
[32mESM[39m [1mdist/chunk-HSTZWXH7.js [22m[32m61.52 KB[39m
|
|
29
|
+
[32mESM[39m [1mdist/index.js [22m[32m1.64 MB[39m
|
|
30
|
+
[32mESM[39m ⚡️ Build success in 23117ms
|
|
22
31
|
[32mCJS[39m [1mdist/getMachineId-darwin-3PL23DL6.cjs [22m[32m1.19 KB[39m
|
|
23
32
|
[32mCJS[39m [1mdist/getMachineId-linux-KYLPK3HC.cjs [22m[32m813.00 B[39m
|
|
24
33
|
[32mCJS[39m [1mdist/getMachineId-bsd-HDZ73WR7.cjs [22m[32m1015.00 B[39m
|
|
25
|
-
[32mCJS[39m [1mdist/chunk-N2CPQVE3.cjs [22m[32m1.09 KB[39m
|
|
26
34
|
[32mCJS[39m [1mdist/getMachineId-win-ZTI2LRDJ.cjs [22m[32m1.66 KB[39m
|
|
35
|
+
[32mCJS[39m [1mdist/chunk-N2CPQVE3.cjs [22m[32m1.09 KB[39m
|
|
27
36
|
[32mCJS[39m [1mdist/getMachineId-unsupported-DEDJN4ZS.cjs [22m[32m777.00 B[39m
|
|
28
37
|
[32mCJS[39m [1mdist/chunk-U74OJRHU.cjs [22m[32m62.08 KB[39m
|
|
29
38
|
[32mCJS[39m [1mdist/index.cjs [22m[32m1.65 MB[39m
|
|
30
|
-
[32mCJS[39m ⚡️ Build success in
|
|
31
|
-
[32mESM[39m [1mdist/getMachineId-linux-K3QXQYAB.js [22m[32m740.00 B[39m
|
|
32
|
-
[32mESM[39m [1mdist/getMachineId-darwin-UTKBTJ2U.js [22m[32m1.08 KB[39m
|
|
33
|
-
[32mESM[39m [1mdist/getMachineId-win-L2EYIM5A.js [22m[32m1.04 KB[39m
|
|
34
|
-
[32mESM[39m [1mdist/getMachineId-bsd-KKIDU47O.js [22m[32m896.00 B[39m
|
|
35
|
-
[32mESM[39m [1mdist/chunk-IGKEDEDE.js [22m[32m452.00 B[39m
|
|
36
|
-
[32mESM[39m [1mdist/getMachineId-unsupported-VPWBQCK7.js [22m[32m700.00 B[39m
|
|
37
|
-
[32mESM[39m [1mdist/chunk-HSTZWXH7.js [22m[32m61.52 KB[39m
|
|
38
|
-
[32mESM[39m [1mdist/index.js [22m[32m1.64 MB[39m
|
|
39
|
-
[32mESM[39m ⚡️ Build success in 21983ms
|
|
39
|
+
[32mCJS[39m ⚡️ Build success in 23122ms
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @mastra/upstash
|
|
2
2
|
|
|
3
|
+
## 0.10.3-alpha.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 48eddb9: update filter logic in Memory class to support semantic recall search scope
|
|
8
|
+
- Updated dependencies [48eddb9]
|
|
9
|
+
- @mastra/core@0.10.4-alpha.2
|
|
10
|
+
|
|
3
11
|
## 0.10.3-alpha.1
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -55,6 +55,9 @@ export declare class UpstashFilterTranslator extends BaseFilterTranslator {
|
|
|
55
55
|
declare class UpstashStore extends MastraStorage {
|
|
56
56
|
private redis;
|
|
57
57
|
constructor(config: UpstashConfig);
|
|
58
|
+
get supports(): {
|
|
59
|
+
selectByIncludeResourceScope: boolean;
|
|
60
|
+
};
|
|
58
61
|
private transformEvalRecord;
|
|
59
62
|
private parseJSON;
|
|
60
63
|
private getKey;
|
|
@@ -55,6 +55,9 @@ export declare class UpstashFilterTranslator extends BaseFilterTranslator {
|
|
|
55
55
|
declare class UpstashStore extends MastraStorage {
|
|
56
56
|
private redis;
|
|
57
57
|
constructor(config: UpstashConfig);
|
|
58
|
+
get supports(): {
|
|
59
|
+
selectByIncludeResourceScope: boolean;
|
|
60
|
+
};
|
|
58
61
|
private transformEvalRecord;
|
|
59
62
|
private parseJSON;
|
|
60
63
|
private getKey;
|
package/dist/index.cjs
CHANGED
|
@@ -31172,9 +31172,9 @@ var require_chunk_QVROTSA5 = chunkU74OJRHU_cjs.__commonJS({
|
|
|
31172
31172
|
}
|
|
31173
31173
|
});
|
|
31174
31174
|
|
|
31175
|
-
// ../../packages/core/dist/chunk-
|
|
31176
|
-
var
|
|
31177
|
-
"../../packages/core/dist/chunk-
|
|
31175
|
+
// ../../packages/core/dist/chunk-XUODQRSL.cjs
|
|
31176
|
+
var require_chunk_XUODQRSL = chunkU74OJRHU_cjs.__commonJS({
|
|
31177
|
+
"../../packages/core/dist/chunk-XUODQRSL.cjs"(exports2) {
|
|
31178
31178
|
var chunkQVROTSA5_cjs = require_chunk_QVROTSA5();
|
|
31179
31179
|
var crypto2 = chunkU74OJRHU_cjs.__require("crypto");
|
|
31180
31180
|
var ai = require_dist4();
|
|
@@ -31746,7 +31746,7 @@ ${JSON.stringify(message, null, 2)}`
|
|
|
31746
31746
|
}
|
|
31747
31747
|
const latestMessagePartType = latestMessage?.content?.parts?.filter((p) => p.type !== `step-start`)?.at?.(-1)?.type;
|
|
31748
31748
|
const newMessageFirstPartType = messageV2.content.parts.filter((p) => p.type !== `step-start`).at(0)?.type;
|
|
31749
|
-
const shouldAppendToLastAssistantMessage = latestMessage?.role === "assistant" && messageV2.role === "assistant";
|
|
31749
|
+
const shouldAppendToLastAssistantMessage = latestMessage?.role === "assistant" && messageV2.role === "assistant" && latestMessage.threadId === messageV2.threadId;
|
|
31750
31750
|
const shouldAppendToLastAssistantMessageParts = shouldAppendToLastAssistantMessage && newMessageFirstPartType && (newMessageFirstPartType === `tool-invocation` && latestMessagePartType !== `text` || newMessageFirstPartType === latestMessagePartType);
|
|
31751
31751
|
if (
|
|
31752
31752
|
// backwards compat check!
|
|
@@ -31824,7 +31824,11 @@ ${JSON.stringify(message, null, 2)}`
|
|
|
31824
31824
|
return this;
|
|
31825
31825
|
}
|
|
31826
31826
|
inputToMastraMessageV2(message, messageSource) {
|
|
31827
|
-
if (
|
|
31827
|
+
if (
|
|
31828
|
+
// we can't throw if the threadId doesn't match and this message came from memory
|
|
31829
|
+
// this is because per-user semantic recall can retrieve messages from other threads
|
|
31830
|
+
messageSource !== `memory` && `threadId` in message && message.threadId && this.memoryInfo && message.threadId !== this.memoryInfo.threadId
|
|
31831
|
+
) {
|
|
31828
31832
|
throw new Error(
|
|
31829
31833
|
`Received input message with wrong threadId. Input ${message.threadId}, expected ${this.memoryInfo.threadId}`
|
|
31830
31834
|
);
|
|
@@ -38437,11 +38441,11 @@ ${err.message}`);
|
|
|
38437
38441
|
}
|
|
38438
38442
|
});
|
|
38439
38443
|
|
|
38440
|
-
// ../../packages/core/dist/chunk-
|
|
38441
|
-
var
|
|
38442
|
-
"../../packages/core/dist/chunk-
|
|
38444
|
+
// ../../packages/core/dist/chunk-ZWYZGIV3.cjs
|
|
38445
|
+
var require_chunk_ZWYZGIV3 = chunkU74OJRHU_cjs.__commonJS({
|
|
38446
|
+
"../../packages/core/dist/chunk-ZWYZGIV3.cjs"(exports2) {
|
|
38443
38447
|
var chunkJYLH5IY3_cjs = require_chunk_JYLH5IY3();
|
|
38444
|
-
var
|
|
38448
|
+
var chunkXUODQRSL_cjs = require_chunk_XUODQRSL();
|
|
38445
38449
|
var chunkST5RMVLG_cjs = require_chunk_ST5RMVLG();
|
|
38446
38450
|
var chunkRO5VPM3P_cjs = require_chunk_RO5VPM3P();
|
|
38447
38451
|
var chunkZAXPU6F2_cjs = require_chunk_ZAXPU6F2();
|
|
@@ -38847,7 +38851,7 @@ var require_chunk_OKHKG5C7 = chunkU74OJRHU_cjs.__commonJS({
|
|
|
38847
38851
|
memoryConfig,
|
|
38848
38852
|
resourceId,
|
|
38849
38853
|
runId,
|
|
38850
|
-
messageList = new
|
|
38854
|
+
messageList = new chunkXUODQRSL_cjs.MessageList({
|
|
38851
38855
|
threadId,
|
|
38852
38856
|
resourceId
|
|
38853
38857
|
})
|
|
@@ -38862,14 +38866,11 @@ var require_chunk_OKHKG5C7 = chunkU74OJRHU_cjs.__commonJS({
|
|
|
38862
38866
|
threadId: threadId || ""
|
|
38863
38867
|
};
|
|
38864
38868
|
}
|
|
38865
|
-
const allUIMessages = messageList.get.all.ui();
|
|
38866
|
-
const currentUserMessages = allUIMessages.filter((m) => m.role === "user");
|
|
38867
|
-
const lastUserMessageContent = currentUserMessages.at(-1)?.content ?? "";
|
|
38868
38869
|
const [memoryMessages, memorySystemMessage] = threadId && memory ? await Promise.all([memory.rememberMessages({
|
|
38869
38870
|
threadId,
|
|
38870
38871
|
resourceId,
|
|
38871
38872
|
config: memoryConfig,
|
|
38872
|
-
vectorMessageSearch:
|
|
38873
|
+
vectorMessageSearch: messageList.getLatestUserContent() || ""
|
|
38873
38874
|
}).then((r) => r.messagesV2), memory.getSystemMessage({
|
|
38874
38875
|
threadId,
|
|
38875
38876
|
memoryConfig
|
|
@@ -39218,7 +39219,7 @@ var require_chunk_OKHKG5C7 = chunkU74OJRHU_cjs.__commonJS({
|
|
|
39218
39219
|
runId,
|
|
39219
39220
|
runtimeContext
|
|
39220
39221
|
});
|
|
39221
|
-
const messageList = new
|
|
39222
|
+
const messageList = new chunkXUODQRSL_cjs.MessageList({
|
|
39222
39223
|
threadId,
|
|
39223
39224
|
resourceId,
|
|
39224
39225
|
generateMessageId
|
|
@@ -39251,12 +39252,13 @@ var require_chunk_OKHKG5C7 = chunkU74OJRHU_cjs.__commonJS({
|
|
|
39251
39252
|
resourceId,
|
|
39252
39253
|
memoryConfig
|
|
39253
39254
|
});
|
|
39254
|
-
|
|
39255
|
+
let [memoryMessages, memorySystemMessage] = threadId && memory ? await Promise.all([memory.rememberMessages({
|
|
39255
39256
|
threadId,
|
|
39256
39257
|
resourceId,
|
|
39257
39258
|
config: memoryConfig,
|
|
39258
|
-
|
|
39259
|
-
|
|
39259
|
+
// The new user messages aren't in the list yet cause we add memory messages first to try to make sure ordering is correct (memory comes before new user messages)
|
|
39260
|
+
vectorMessageSearch: new chunkXUODQRSL_cjs.MessageList().add(messages, `user`).getLatestUserContent() || ""
|
|
39261
|
+
}).then((r) => r.messagesV2), memory.getSystemMessage({
|
|
39260
39262
|
threadId,
|
|
39261
39263
|
memoryConfig
|
|
39262
39264
|
})]) : [[], null];
|
|
@@ -39265,10 +39267,28 @@ var require_chunk_OKHKG5C7 = chunkU74OJRHU_cjs.__commonJS({
|
|
|
39265
39267
|
runId,
|
|
39266
39268
|
fetchedCount: memoryMessages.length
|
|
39267
39269
|
});
|
|
39270
|
+
const resultsFromOtherThreads = memoryMessages.filter((m) => m.threadId !== threadId);
|
|
39271
|
+
if (resultsFromOtherThreads.length && !memorySystemMessage) {
|
|
39272
|
+
memorySystemMessage = ``;
|
|
39273
|
+
}
|
|
39274
|
+
if (resultsFromOtherThreads.length) {
|
|
39275
|
+
memorySystemMessage += `
|
|
39276
|
+
The following messages were remembered from a different conversation:
|
|
39277
|
+
<remembered_from_other_conversation>
|
|
39278
|
+
${JSON.stringify(
|
|
39279
|
+
// get v1 since they're closer to CoreMessages (which get sent to the LLM) but also include timestamps
|
|
39280
|
+
new chunkXUODQRSL_cjs.MessageList().add(resultsFromOtherThreads, "memory").get.all.v1()
|
|
39281
|
+
)}
|
|
39282
|
+
<end_remembered_from_other_conversation>`;
|
|
39283
|
+
}
|
|
39268
39284
|
if (memorySystemMessage) {
|
|
39269
39285
|
messageList.addSystem(memorySystemMessage, "memory");
|
|
39270
39286
|
}
|
|
39271
|
-
messageList.add(
|
|
39287
|
+
messageList.add(
|
|
39288
|
+
memoryMessages.filter((m) => m.threadId === threadId),
|
|
39289
|
+
// filter out messages from other threads. those are added to system message above
|
|
39290
|
+
"memory"
|
|
39291
|
+
).add(messages, "user");
|
|
39272
39292
|
const systemMessage = messageList.getSystemMessages()?.map((m) => m.content)?.join(`
|
|
39273
39293
|
`) ?? void 0;
|
|
39274
39294
|
const processedMemoryMessages = memory.processMessages({
|
|
@@ -39280,7 +39300,7 @@ var require_chunk_OKHKG5C7 = chunkU74OJRHU_cjs.__commonJS({
|
|
|
39280
39300
|
systemMessage,
|
|
39281
39301
|
memorySystemMessage: memorySystemMessage || void 0
|
|
39282
39302
|
});
|
|
39283
|
-
const processedList = new
|
|
39303
|
+
const processedList = new chunkXUODQRSL_cjs.MessageList({
|
|
39284
39304
|
threadId,
|
|
39285
39305
|
resourceId
|
|
39286
39306
|
}).addSystem(instructions || `${this.instructions}.`).addSystem(memorySystemMessage).add(context2 || [], "context").add(processedMemoryMessages, "memory").add(messageList.get.input.v2(), "user").get.all.prompt();
|
|
@@ -42755,18 +42775,18 @@ var require_chunk_OKHKG5C7 = chunkU74OJRHU_cjs.__commonJS({
|
|
|
42755
42775
|
// ../../packages/core/dist/agent/index.cjs
|
|
42756
42776
|
var require_agent = chunkU74OJRHU_cjs.__commonJS({
|
|
42757
42777
|
"../../packages/core/dist/agent/index.cjs"(exports2) {
|
|
42758
|
-
var
|
|
42759
|
-
var
|
|
42778
|
+
var chunkZWYZGIV3_cjs = require_chunk_ZWYZGIV3();
|
|
42779
|
+
var chunkXUODQRSL_cjs = require_chunk_XUODQRSL();
|
|
42760
42780
|
Object.defineProperty(exports2, "Agent", {
|
|
42761
42781
|
enumerable: true,
|
|
42762
42782
|
get: function() {
|
|
42763
|
-
return
|
|
42783
|
+
return chunkZWYZGIV3_cjs.Agent;
|
|
42764
42784
|
}
|
|
42765
42785
|
});
|
|
42766
42786
|
Object.defineProperty(exports2, "MessageList", {
|
|
42767
42787
|
enumerable: true,
|
|
42768
42788
|
get: function() {
|
|
42769
|
-
return
|
|
42789
|
+
return chunkXUODQRSL_cjs.MessageList;
|
|
42770
42790
|
}
|
|
42771
42791
|
});
|
|
42772
42792
|
}
|
|
@@ -42783,6 +42803,11 @@ var UpstashStore = class extends storage.MastraStorage {
|
|
|
42783
42803
|
token: config.token
|
|
42784
42804
|
});
|
|
42785
42805
|
}
|
|
42806
|
+
get supports() {
|
|
42807
|
+
return {
|
|
42808
|
+
selectByIncludeResourceScope: true
|
|
42809
|
+
};
|
|
42810
|
+
}
|
|
42786
42811
|
transformEvalRecord(record) {
|
|
42787
42812
|
let result = record.result;
|
|
42788
42813
|
if (typeof result === "string") {
|
|
@@ -42867,7 +42892,8 @@ var UpstashStore = class extends storage.MastraStorage {
|
|
|
42867
42892
|
return totalDeleted;
|
|
42868
42893
|
}
|
|
42869
42894
|
getMessageKey(threadId, messageId) {
|
|
42870
|
-
|
|
42895
|
+
const key = this.getKey(storage.TABLE_MESSAGES, { threadId, id: messageId });
|
|
42896
|
+
return key;
|
|
42871
42897
|
}
|
|
42872
42898
|
getThreadMessagesKey(threadId) {
|
|
42873
42899
|
return `thread:${threadId}:messages`;
|
|
@@ -43240,6 +43266,14 @@ var UpstashStore = class extends storage.MastraStorage {
|
|
|
43240
43266
|
async saveMessages(args) {
|
|
43241
43267
|
const { messages, format = "v1" } = args;
|
|
43242
43268
|
if (messages.length === 0) return [];
|
|
43269
|
+
const threadId = messages[0]?.threadId;
|
|
43270
|
+
if (!threadId) {
|
|
43271
|
+
throw new Error("Thread ID is required");
|
|
43272
|
+
}
|
|
43273
|
+
const thread = await this.getThreadById({ threadId });
|
|
43274
|
+
if (!thread) {
|
|
43275
|
+
throw new Error(`Thread ${threadId} not found`);
|
|
43276
|
+
}
|
|
43243
43277
|
const messagesWithIndex = messages.map((message, index) => ({
|
|
43244
43278
|
...message,
|
|
43245
43279
|
_index: index
|
|
@@ -43250,7 +43284,8 @@ var UpstashStore = class extends storage.MastraStorage {
|
|
|
43250
43284
|
const pipeline = this.redis.pipeline();
|
|
43251
43285
|
for (const message of batch) {
|
|
43252
43286
|
const key = this.getMessageKey(message.threadId, message.id);
|
|
43253
|
-
const
|
|
43287
|
+
const createdAtScore = new Date(message.createdAt).getTime();
|
|
43288
|
+
const score = message._index !== void 0 ? message._index : createdAtScore;
|
|
43254
43289
|
pipeline.set(key, message);
|
|
43255
43290
|
pipeline.zadd(this.getThreadMessagesKey(message.threadId), {
|
|
43256
43291
|
score,
|
|
@@ -43345,38 +43380,55 @@ var UpstashStore = class extends storage.MastraStorage {
|
|
|
43345
43380
|
limit = Number.MAX_SAFE_INTEGER;
|
|
43346
43381
|
}
|
|
43347
43382
|
const messageIds = /* @__PURE__ */ new Set();
|
|
43383
|
+
const messageIdToThreadIds = {};
|
|
43348
43384
|
if (limit === 0 && !selectBy?.include) {
|
|
43349
43385
|
return [];
|
|
43350
43386
|
}
|
|
43351
43387
|
if (selectBy?.include?.length) {
|
|
43352
43388
|
for (const item of selectBy.include) {
|
|
43353
43389
|
messageIds.add(item.id);
|
|
43354
|
-
|
|
43355
|
-
|
|
43356
|
-
|
|
43357
|
-
|
|
43358
|
-
|
|
43359
|
-
|
|
43360
|
-
|
|
43361
|
-
|
|
43362
|
-
|
|
43363
|
-
|
|
43364
|
-
|
|
43365
|
-
}
|
|
43390
|
+
const itemThreadId = item.threadId || threadId;
|
|
43391
|
+
messageIdToThreadIds[item.id] = itemThreadId;
|
|
43392
|
+
const itemThreadMessagesKey = this.getThreadMessagesKey(itemThreadId);
|
|
43393
|
+
const rank = await this.redis.zrank(itemThreadMessagesKey, item.id);
|
|
43394
|
+
if (rank === null) continue;
|
|
43395
|
+
if (item.withPreviousMessages) {
|
|
43396
|
+
const start = Math.max(0, rank - item.withPreviousMessages);
|
|
43397
|
+
const prevIds = rank === 0 ? [] : await this.redis.zrange(itemThreadMessagesKey, start, rank - 1);
|
|
43398
|
+
prevIds.forEach((id) => {
|
|
43399
|
+
messageIds.add(id);
|
|
43400
|
+
messageIdToThreadIds[id] = itemThreadId;
|
|
43401
|
+
});
|
|
43402
|
+
}
|
|
43403
|
+
if (item.withNextMessages) {
|
|
43404
|
+
const nextIds = await this.redis.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
|
|
43405
|
+
nextIds.forEach((id) => {
|
|
43406
|
+
messageIds.add(id);
|
|
43407
|
+
messageIdToThreadIds[id] = itemThreadId;
|
|
43408
|
+
});
|
|
43366
43409
|
}
|
|
43367
43410
|
}
|
|
43368
43411
|
}
|
|
43369
43412
|
if (limit === Number.MAX_SAFE_INTEGER) {
|
|
43370
43413
|
const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
43371
|
-
allIds.forEach((id) =>
|
|
43414
|
+
allIds.forEach((id) => {
|
|
43415
|
+
messageIds.add(id);
|
|
43416
|
+
messageIdToThreadIds[id] = threadId;
|
|
43417
|
+
});
|
|
43372
43418
|
} else if (limit > 0) {
|
|
43373
43419
|
const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
|
|
43374
|
-
latestIds.forEach((id) =>
|
|
43420
|
+
latestIds.forEach((id) => {
|
|
43421
|
+
messageIds.add(id);
|
|
43422
|
+
messageIdToThreadIds[id] = threadId;
|
|
43423
|
+
});
|
|
43375
43424
|
}
|
|
43376
43425
|
const messages = (await Promise.all(
|
|
43377
|
-
Array.from(messageIds).map(
|
|
43378
|
-
|
|
43379
|
-
|
|
43426
|
+
Array.from(messageIds).map(async (id) => {
|
|
43427
|
+
const tId = messageIdToThreadIds[id] || threadId;
|
|
43428
|
+
const byThreadId = await this.redis.get(this.getMessageKey(tId, id));
|
|
43429
|
+
if (byThreadId) return byThreadId;
|
|
43430
|
+
return null;
|
|
43431
|
+
})
|
|
43380
43432
|
)).filter((msg) => msg !== null);
|
|
43381
43433
|
messages.sort((a, b) => allMessageIds.indexOf(a.id) - allMessageIds.indexOf(b.id));
|
|
43382
43434
|
const prepared = messages.filter((message) => message !== null && message !== void 0).map((message) => {
|
package/dist/index.js
CHANGED
|
@@ -31170,9 +31170,9 @@ var require_chunk_QVROTSA5 = __commonJS({
|
|
|
31170
31170
|
}
|
|
31171
31171
|
});
|
|
31172
31172
|
|
|
31173
|
-
// ../../packages/core/dist/chunk-
|
|
31174
|
-
var
|
|
31175
|
-
"../../packages/core/dist/chunk-
|
|
31173
|
+
// ../../packages/core/dist/chunk-XUODQRSL.cjs
|
|
31174
|
+
var require_chunk_XUODQRSL = __commonJS({
|
|
31175
|
+
"../../packages/core/dist/chunk-XUODQRSL.cjs"(exports2) {
|
|
31176
31176
|
var chunkQVROTSA5_cjs = require_chunk_QVROTSA5();
|
|
31177
31177
|
var crypto2 = __require("crypto");
|
|
31178
31178
|
var ai = require_dist4();
|
|
@@ -31744,7 +31744,7 @@ ${JSON.stringify(message, null, 2)}`
|
|
|
31744
31744
|
}
|
|
31745
31745
|
const latestMessagePartType = latestMessage?.content?.parts?.filter((p) => p.type !== `step-start`)?.at?.(-1)?.type;
|
|
31746
31746
|
const newMessageFirstPartType = messageV2.content.parts.filter((p) => p.type !== `step-start`).at(0)?.type;
|
|
31747
|
-
const shouldAppendToLastAssistantMessage = latestMessage?.role === "assistant" && messageV2.role === "assistant";
|
|
31747
|
+
const shouldAppendToLastAssistantMessage = latestMessage?.role === "assistant" && messageV2.role === "assistant" && latestMessage.threadId === messageV2.threadId;
|
|
31748
31748
|
const shouldAppendToLastAssistantMessageParts = shouldAppendToLastAssistantMessage && newMessageFirstPartType && (newMessageFirstPartType === `tool-invocation` && latestMessagePartType !== `text` || newMessageFirstPartType === latestMessagePartType);
|
|
31749
31749
|
if (
|
|
31750
31750
|
// backwards compat check!
|
|
@@ -31822,7 +31822,11 @@ ${JSON.stringify(message, null, 2)}`
|
|
|
31822
31822
|
return this;
|
|
31823
31823
|
}
|
|
31824
31824
|
inputToMastraMessageV2(message, messageSource) {
|
|
31825
|
-
if (
|
|
31825
|
+
if (
|
|
31826
|
+
// we can't throw if the threadId doesn't match and this message came from memory
|
|
31827
|
+
// this is because per-user semantic recall can retrieve messages from other threads
|
|
31828
|
+
messageSource !== `memory` && `threadId` in message && message.threadId && this.memoryInfo && message.threadId !== this.memoryInfo.threadId
|
|
31829
|
+
) {
|
|
31826
31830
|
throw new Error(
|
|
31827
31831
|
`Received input message with wrong threadId. Input ${message.threadId}, expected ${this.memoryInfo.threadId}`
|
|
31828
31832
|
);
|
|
@@ -38435,11 +38439,11 @@ ${err.message}`);
|
|
|
38435
38439
|
}
|
|
38436
38440
|
});
|
|
38437
38441
|
|
|
38438
|
-
// ../../packages/core/dist/chunk-
|
|
38439
|
-
var
|
|
38440
|
-
"../../packages/core/dist/chunk-
|
|
38442
|
+
// ../../packages/core/dist/chunk-ZWYZGIV3.cjs
|
|
38443
|
+
var require_chunk_ZWYZGIV3 = __commonJS({
|
|
38444
|
+
"../../packages/core/dist/chunk-ZWYZGIV3.cjs"(exports2) {
|
|
38441
38445
|
var chunkJYLH5IY3_cjs = require_chunk_JYLH5IY3();
|
|
38442
|
-
var
|
|
38446
|
+
var chunkXUODQRSL_cjs = require_chunk_XUODQRSL();
|
|
38443
38447
|
var chunkST5RMVLG_cjs = require_chunk_ST5RMVLG();
|
|
38444
38448
|
var chunkRO5VPM3P_cjs = require_chunk_RO5VPM3P();
|
|
38445
38449
|
var chunkZAXPU6F2_cjs = require_chunk_ZAXPU6F2();
|
|
@@ -38845,7 +38849,7 @@ var require_chunk_OKHKG5C7 = __commonJS({
|
|
|
38845
38849
|
memoryConfig,
|
|
38846
38850
|
resourceId,
|
|
38847
38851
|
runId,
|
|
38848
|
-
messageList = new
|
|
38852
|
+
messageList = new chunkXUODQRSL_cjs.MessageList({
|
|
38849
38853
|
threadId,
|
|
38850
38854
|
resourceId
|
|
38851
38855
|
})
|
|
@@ -38860,14 +38864,11 @@ var require_chunk_OKHKG5C7 = __commonJS({
|
|
|
38860
38864
|
threadId: threadId || ""
|
|
38861
38865
|
};
|
|
38862
38866
|
}
|
|
38863
|
-
const allUIMessages = messageList.get.all.ui();
|
|
38864
|
-
const currentUserMessages = allUIMessages.filter((m) => m.role === "user");
|
|
38865
|
-
const lastUserMessageContent = currentUserMessages.at(-1)?.content ?? "";
|
|
38866
38867
|
const [memoryMessages, memorySystemMessage] = threadId && memory ? await Promise.all([memory.rememberMessages({
|
|
38867
38868
|
threadId,
|
|
38868
38869
|
resourceId,
|
|
38869
38870
|
config: memoryConfig,
|
|
38870
|
-
vectorMessageSearch:
|
|
38871
|
+
vectorMessageSearch: messageList.getLatestUserContent() || ""
|
|
38871
38872
|
}).then((r) => r.messagesV2), memory.getSystemMessage({
|
|
38872
38873
|
threadId,
|
|
38873
38874
|
memoryConfig
|
|
@@ -39216,7 +39217,7 @@ var require_chunk_OKHKG5C7 = __commonJS({
|
|
|
39216
39217
|
runId,
|
|
39217
39218
|
runtimeContext
|
|
39218
39219
|
});
|
|
39219
|
-
const messageList = new
|
|
39220
|
+
const messageList = new chunkXUODQRSL_cjs.MessageList({
|
|
39220
39221
|
threadId,
|
|
39221
39222
|
resourceId,
|
|
39222
39223
|
generateMessageId
|
|
@@ -39249,12 +39250,13 @@ var require_chunk_OKHKG5C7 = __commonJS({
|
|
|
39249
39250
|
resourceId,
|
|
39250
39251
|
memoryConfig
|
|
39251
39252
|
});
|
|
39252
|
-
|
|
39253
|
+
let [memoryMessages, memorySystemMessage] = threadId && memory ? await Promise.all([memory.rememberMessages({
|
|
39253
39254
|
threadId,
|
|
39254
39255
|
resourceId,
|
|
39255
39256
|
config: memoryConfig,
|
|
39256
|
-
|
|
39257
|
-
|
|
39257
|
+
// The new user messages aren't in the list yet cause we add memory messages first to try to make sure ordering is correct (memory comes before new user messages)
|
|
39258
|
+
vectorMessageSearch: new chunkXUODQRSL_cjs.MessageList().add(messages, `user`).getLatestUserContent() || ""
|
|
39259
|
+
}).then((r) => r.messagesV2), memory.getSystemMessage({
|
|
39258
39260
|
threadId,
|
|
39259
39261
|
memoryConfig
|
|
39260
39262
|
})]) : [[], null];
|
|
@@ -39263,10 +39265,28 @@ var require_chunk_OKHKG5C7 = __commonJS({
|
|
|
39263
39265
|
runId,
|
|
39264
39266
|
fetchedCount: memoryMessages.length
|
|
39265
39267
|
});
|
|
39268
|
+
const resultsFromOtherThreads = memoryMessages.filter((m) => m.threadId !== threadId);
|
|
39269
|
+
if (resultsFromOtherThreads.length && !memorySystemMessage) {
|
|
39270
|
+
memorySystemMessage = ``;
|
|
39271
|
+
}
|
|
39272
|
+
if (resultsFromOtherThreads.length) {
|
|
39273
|
+
memorySystemMessage += `
|
|
39274
|
+
The following messages were remembered from a different conversation:
|
|
39275
|
+
<remembered_from_other_conversation>
|
|
39276
|
+
${JSON.stringify(
|
|
39277
|
+
// get v1 since they're closer to CoreMessages (which get sent to the LLM) but also include timestamps
|
|
39278
|
+
new chunkXUODQRSL_cjs.MessageList().add(resultsFromOtherThreads, "memory").get.all.v1()
|
|
39279
|
+
)}
|
|
39280
|
+
<end_remembered_from_other_conversation>`;
|
|
39281
|
+
}
|
|
39266
39282
|
if (memorySystemMessage) {
|
|
39267
39283
|
messageList.addSystem(memorySystemMessage, "memory");
|
|
39268
39284
|
}
|
|
39269
|
-
messageList.add(
|
|
39285
|
+
messageList.add(
|
|
39286
|
+
memoryMessages.filter((m) => m.threadId === threadId),
|
|
39287
|
+
// filter out messages from other threads. those are added to system message above
|
|
39288
|
+
"memory"
|
|
39289
|
+
).add(messages, "user");
|
|
39270
39290
|
const systemMessage = messageList.getSystemMessages()?.map((m) => m.content)?.join(`
|
|
39271
39291
|
`) ?? void 0;
|
|
39272
39292
|
const processedMemoryMessages = memory.processMessages({
|
|
@@ -39278,7 +39298,7 @@ var require_chunk_OKHKG5C7 = __commonJS({
|
|
|
39278
39298
|
systemMessage,
|
|
39279
39299
|
memorySystemMessage: memorySystemMessage || void 0
|
|
39280
39300
|
});
|
|
39281
|
-
const processedList = new
|
|
39301
|
+
const processedList = new chunkXUODQRSL_cjs.MessageList({
|
|
39282
39302
|
threadId,
|
|
39283
39303
|
resourceId
|
|
39284
39304
|
}).addSystem(instructions || `${this.instructions}.`).addSystem(memorySystemMessage).add(context2 || [], "context").add(processedMemoryMessages, "memory").add(messageList.get.input.v2(), "user").get.all.prompt();
|
|
@@ -42753,18 +42773,18 @@ var require_chunk_OKHKG5C7 = __commonJS({
|
|
|
42753
42773
|
// ../../packages/core/dist/agent/index.cjs
|
|
42754
42774
|
var require_agent = __commonJS({
|
|
42755
42775
|
"../../packages/core/dist/agent/index.cjs"(exports2) {
|
|
42756
|
-
var
|
|
42757
|
-
var
|
|
42776
|
+
var chunkZWYZGIV3_cjs = require_chunk_ZWYZGIV3();
|
|
42777
|
+
var chunkXUODQRSL_cjs = require_chunk_XUODQRSL();
|
|
42758
42778
|
Object.defineProperty(exports2, "Agent", {
|
|
42759
42779
|
enumerable: true,
|
|
42760
42780
|
get: function() {
|
|
42761
|
-
return
|
|
42781
|
+
return chunkZWYZGIV3_cjs.Agent;
|
|
42762
42782
|
}
|
|
42763
42783
|
});
|
|
42764
42784
|
Object.defineProperty(exports2, "MessageList", {
|
|
42765
42785
|
enumerable: true,
|
|
42766
42786
|
get: function() {
|
|
42767
|
-
return
|
|
42787
|
+
return chunkXUODQRSL_cjs.MessageList;
|
|
42768
42788
|
}
|
|
42769
42789
|
});
|
|
42770
42790
|
}
|
|
@@ -42781,6 +42801,11 @@ var UpstashStore = class extends MastraStorage {
|
|
|
42781
42801
|
token: config.token
|
|
42782
42802
|
});
|
|
42783
42803
|
}
|
|
42804
|
+
get supports() {
|
|
42805
|
+
return {
|
|
42806
|
+
selectByIncludeResourceScope: true
|
|
42807
|
+
};
|
|
42808
|
+
}
|
|
42784
42809
|
transformEvalRecord(record) {
|
|
42785
42810
|
let result = record.result;
|
|
42786
42811
|
if (typeof result === "string") {
|
|
@@ -42865,7 +42890,8 @@ var UpstashStore = class extends MastraStorage {
|
|
|
42865
42890
|
return totalDeleted;
|
|
42866
42891
|
}
|
|
42867
42892
|
getMessageKey(threadId, messageId) {
|
|
42868
|
-
|
|
42893
|
+
const key = this.getKey(TABLE_MESSAGES, { threadId, id: messageId });
|
|
42894
|
+
return key;
|
|
42869
42895
|
}
|
|
42870
42896
|
getThreadMessagesKey(threadId) {
|
|
42871
42897
|
return `thread:${threadId}:messages`;
|
|
@@ -43238,6 +43264,14 @@ var UpstashStore = class extends MastraStorage {
|
|
|
43238
43264
|
async saveMessages(args) {
|
|
43239
43265
|
const { messages, format = "v1" } = args;
|
|
43240
43266
|
if (messages.length === 0) return [];
|
|
43267
|
+
const threadId = messages[0]?.threadId;
|
|
43268
|
+
if (!threadId) {
|
|
43269
|
+
throw new Error("Thread ID is required");
|
|
43270
|
+
}
|
|
43271
|
+
const thread = await this.getThreadById({ threadId });
|
|
43272
|
+
if (!thread) {
|
|
43273
|
+
throw new Error(`Thread ${threadId} not found`);
|
|
43274
|
+
}
|
|
43241
43275
|
const messagesWithIndex = messages.map((message, index) => ({
|
|
43242
43276
|
...message,
|
|
43243
43277
|
_index: index
|
|
@@ -43248,7 +43282,8 @@ var UpstashStore = class extends MastraStorage {
|
|
|
43248
43282
|
const pipeline = this.redis.pipeline();
|
|
43249
43283
|
for (const message of batch) {
|
|
43250
43284
|
const key = this.getMessageKey(message.threadId, message.id);
|
|
43251
|
-
const
|
|
43285
|
+
const createdAtScore = new Date(message.createdAt).getTime();
|
|
43286
|
+
const score = message._index !== void 0 ? message._index : createdAtScore;
|
|
43252
43287
|
pipeline.set(key, message);
|
|
43253
43288
|
pipeline.zadd(this.getThreadMessagesKey(message.threadId), {
|
|
43254
43289
|
score,
|
|
@@ -43343,38 +43378,55 @@ var UpstashStore = class extends MastraStorage {
|
|
|
43343
43378
|
limit = Number.MAX_SAFE_INTEGER;
|
|
43344
43379
|
}
|
|
43345
43380
|
const messageIds = /* @__PURE__ */ new Set();
|
|
43381
|
+
const messageIdToThreadIds = {};
|
|
43346
43382
|
if (limit === 0 && !selectBy?.include) {
|
|
43347
43383
|
return [];
|
|
43348
43384
|
}
|
|
43349
43385
|
if (selectBy?.include?.length) {
|
|
43350
43386
|
for (const item of selectBy.include) {
|
|
43351
43387
|
messageIds.add(item.id);
|
|
43352
|
-
|
|
43353
|
-
|
|
43354
|
-
|
|
43355
|
-
|
|
43356
|
-
|
|
43357
|
-
|
|
43358
|
-
|
|
43359
|
-
|
|
43360
|
-
|
|
43361
|
-
|
|
43362
|
-
|
|
43363
|
-
}
|
|
43388
|
+
const itemThreadId = item.threadId || threadId;
|
|
43389
|
+
messageIdToThreadIds[item.id] = itemThreadId;
|
|
43390
|
+
const itemThreadMessagesKey = this.getThreadMessagesKey(itemThreadId);
|
|
43391
|
+
const rank = await this.redis.zrank(itemThreadMessagesKey, item.id);
|
|
43392
|
+
if (rank === null) continue;
|
|
43393
|
+
if (item.withPreviousMessages) {
|
|
43394
|
+
const start = Math.max(0, rank - item.withPreviousMessages);
|
|
43395
|
+
const prevIds = rank === 0 ? [] : await this.redis.zrange(itemThreadMessagesKey, start, rank - 1);
|
|
43396
|
+
prevIds.forEach((id) => {
|
|
43397
|
+
messageIds.add(id);
|
|
43398
|
+
messageIdToThreadIds[id] = itemThreadId;
|
|
43399
|
+
});
|
|
43400
|
+
}
|
|
43401
|
+
if (item.withNextMessages) {
|
|
43402
|
+
const nextIds = await this.redis.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
|
|
43403
|
+
nextIds.forEach((id) => {
|
|
43404
|
+
messageIds.add(id);
|
|
43405
|
+
messageIdToThreadIds[id] = itemThreadId;
|
|
43406
|
+
});
|
|
43364
43407
|
}
|
|
43365
43408
|
}
|
|
43366
43409
|
}
|
|
43367
43410
|
if (limit === Number.MAX_SAFE_INTEGER) {
|
|
43368
43411
|
const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
43369
|
-
allIds.forEach((id) =>
|
|
43412
|
+
allIds.forEach((id) => {
|
|
43413
|
+
messageIds.add(id);
|
|
43414
|
+
messageIdToThreadIds[id] = threadId;
|
|
43415
|
+
});
|
|
43370
43416
|
} else if (limit > 0) {
|
|
43371
43417
|
const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
|
|
43372
|
-
latestIds.forEach((id) =>
|
|
43418
|
+
latestIds.forEach((id) => {
|
|
43419
|
+
messageIds.add(id);
|
|
43420
|
+
messageIdToThreadIds[id] = threadId;
|
|
43421
|
+
});
|
|
43373
43422
|
}
|
|
43374
43423
|
const messages = (await Promise.all(
|
|
43375
|
-
Array.from(messageIds).map(
|
|
43376
|
-
|
|
43377
|
-
|
|
43424
|
+
Array.from(messageIds).map(async (id) => {
|
|
43425
|
+
const tId = messageIdToThreadIds[id] || threadId;
|
|
43426
|
+
const byThreadId = await this.redis.get(this.getMessageKey(tId, id));
|
|
43427
|
+
if (byThreadId) return byThreadId;
|
|
43428
|
+
return null;
|
|
43429
|
+
})
|
|
43378
43430
|
)).filter((msg) => msg !== null);
|
|
43379
43431
|
messages.sort((a, b) => allMessageIds.indexOf(a.id) - allMessageIds.indexOf(b.id));
|
|
43380
43432
|
const prepared = messages.filter((message) => message !== null && message !== void 0).map((message) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/upstash",
|
|
3
|
-
"version": "0.10.3-alpha.
|
|
3
|
+
"version": "0.10.3-alpha.2",
|
|
4
4
|
"description": "Upstash provider for Mastra - includes both vector and db storage capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"tsup": "^8.5.0",
|
|
32
32
|
"typescript": "^5.8.2",
|
|
33
33
|
"vitest": "^3.2.2",
|
|
34
|
-
"@internal/storage-test-utils": "0.0.6",
|
|
35
34
|
"@internal/lint": "0.0.10",
|
|
36
|
-
"@
|
|
35
|
+
"@internal/storage-test-utils": "0.0.6",
|
|
36
|
+
"@mastra/core": "0.10.4-alpha.2"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@mastra/core": "^0.10.2-alpha.0"
|
package/src/storage/index.ts
CHANGED
|
@@ -36,6 +36,14 @@ export class UpstashStore extends MastraStorage {
|
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
public get supports(): {
|
|
40
|
+
selectByIncludeResourceScope: boolean;
|
|
41
|
+
} {
|
|
42
|
+
return {
|
|
43
|
+
selectByIncludeResourceScope: true,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
39
47
|
private transformEvalRecord(record: Record<string, any>): EvalRow {
|
|
40
48
|
// Parse JSON strings if needed
|
|
41
49
|
let result = record.result;
|
|
@@ -136,7 +144,8 @@ export class UpstashStore extends MastraStorage {
|
|
|
136
144
|
}
|
|
137
145
|
|
|
138
146
|
private getMessageKey(threadId: string, messageId: string): string {
|
|
139
|
-
|
|
147
|
+
const key = this.getKey(TABLE_MESSAGES, { threadId, id: messageId });
|
|
148
|
+
return key;
|
|
140
149
|
}
|
|
141
150
|
|
|
142
151
|
private getThreadMessagesKey(threadId: string): string {
|
|
@@ -664,6 +673,17 @@ export class UpstashStore extends MastraStorage {
|
|
|
664
673
|
const { messages, format = 'v1' } = args;
|
|
665
674
|
if (messages.length === 0) return [];
|
|
666
675
|
|
|
676
|
+
const threadId = messages[0]?.threadId;
|
|
677
|
+
if (!threadId) {
|
|
678
|
+
throw new Error('Thread ID is required');
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Check if thread exists
|
|
682
|
+
const thread = await this.getThreadById({ threadId });
|
|
683
|
+
if (!thread) {
|
|
684
|
+
throw new Error(`Thread ${threadId} not found`);
|
|
685
|
+
}
|
|
686
|
+
|
|
667
687
|
// Add an index to each message to maintain order
|
|
668
688
|
const messagesWithIndex = messages.map((message, index) => ({
|
|
669
689
|
...message,
|
|
@@ -676,7 +696,8 @@ export class UpstashStore extends MastraStorage {
|
|
|
676
696
|
const pipeline = this.redis.pipeline();
|
|
677
697
|
for (const message of batch) {
|
|
678
698
|
const key = this.getMessageKey(message.threadId!, message.id);
|
|
679
|
-
const
|
|
699
|
+
const createdAtScore = new Date(message.createdAt).getTime();
|
|
700
|
+
const score = message._index !== undefined ? message._index : createdAtScore;
|
|
680
701
|
|
|
681
702
|
// Store the message data
|
|
682
703
|
pipeline.set(key, message);
|
|
@@ -844,6 +865,7 @@ export class UpstashStore extends MastraStorage {
|
|
|
844
865
|
}
|
|
845
866
|
|
|
846
867
|
const messageIds = new Set<string>();
|
|
868
|
+
const messageIdToThreadIds: Record<string, string> = {};
|
|
847
869
|
|
|
848
870
|
if (limit === 0 && !selectBy?.include) {
|
|
849
871
|
return [];
|
|
@@ -854,23 +876,32 @@ export class UpstashStore extends MastraStorage {
|
|
|
854
876
|
for (const item of selectBy.include) {
|
|
855
877
|
messageIds.add(item.id);
|
|
856
878
|
|
|
857
|
-
if
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
879
|
+
// Use per-include threadId if present, else fallback to main threadId
|
|
880
|
+
const itemThreadId = item.threadId || threadId;
|
|
881
|
+
messageIdToThreadIds[item.id] = itemThreadId;
|
|
882
|
+
const itemThreadMessagesKey = this.getThreadMessagesKey(itemThreadId);
|
|
883
|
+
|
|
884
|
+
// Get the rank of this message in the sorted set
|
|
885
|
+
const rank = await this.redis.zrank(itemThreadMessagesKey, item.id);
|
|
886
|
+
if (rank === null) continue;
|
|
887
|
+
|
|
888
|
+
// Get previous messages if requested
|
|
889
|
+
if (item.withPreviousMessages) {
|
|
890
|
+
const start = Math.max(0, rank - item.withPreviousMessages);
|
|
891
|
+
const prevIds = rank === 0 ? [] : await this.redis.zrange(itemThreadMessagesKey, start, rank - 1);
|
|
892
|
+
prevIds.forEach(id => {
|
|
893
|
+
messageIds.add(id as string);
|
|
894
|
+
messageIdToThreadIds[id as string] = itemThreadId;
|
|
895
|
+
});
|
|
896
|
+
}
|
|
868
897
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
898
|
+
// Get next messages if requested
|
|
899
|
+
if (item.withNextMessages) {
|
|
900
|
+
const nextIds = await this.redis.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
|
|
901
|
+
nextIds.forEach(id => {
|
|
902
|
+
messageIds.add(id as string);
|
|
903
|
+
messageIdToThreadIds[id as string] = itemThreadId;
|
|
904
|
+
});
|
|
874
905
|
}
|
|
875
906
|
}
|
|
876
907
|
}
|
|
@@ -879,19 +910,29 @@ export class UpstashStore extends MastraStorage {
|
|
|
879
910
|
if (limit === Number.MAX_SAFE_INTEGER) {
|
|
880
911
|
// Get all messages
|
|
881
912
|
const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
882
|
-
allIds.forEach(id =>
|
|
913
|
+
allIds.forEach(id => {
|
|
914
|
+
messageIds.add(id as string);
|
|
915
|
+
messageIdToThreadIds[id as string] = threadId;
|
|
916
|
+
});
|
|
883
917
|
} else if (limit > 0) {
|
|
884
918
|
// Get limited number of recent messages
|
|
885
919
|
const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
|
|
886
|
-
latestIds.forEach(id =>
|
|
920
|
+
latestIds.forEach(id => {
|
|
921
|
+
messageIds.add(id as string);
|
|
922
|
+
messageIdToThreadIds[id as string] = threadId;
|
|
923
|
+
});
|
|
887
924
|
}
|
|
888
925
|
|
|
889
926
|
// Fetch all needed messages in parallel
|
|
890
927
|
const messages = (
|
|
891
928
|
await Promise.all(
|
|
892
|
-
Array.from(messageIds).map(async id =>
|
|
893
|
-
|
|
894
|
-
|
|
929
|
+
Array.from(messageIds).map(async id => {
|
|
930
|
+
const tId = messageIdToThreadIds[id] || threadId;
|
|
931
|
+
const byThreadId = await this.redis.get<MastraMessageV2 & { _index?: number }>(this.getMessageKey(tId, id));
|
|
932
|
+
if (byThreadId) return byThreadId;
|
|
933
|
+
|
|
934
|
+
return null;
|
|
935
|
+
}),
|
|
895
936
|
)
|
|
896
937
|
).filter(msg => msg !== null) as (MastraMessageV2 & { _index?: number })[];
|
|
897
938
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
checkWorkflowSnapshot,
|
|
4
|
+
createSampleMessageV2,
|
|
5
|
+
createSampleThread,
|
|
6
|
+
createSampleWorkflowSnapshot,
|
|
7
|
+
} from '@internal/storage-test-utils';
|
|
3
8
|
import type { MastraMessageV2 } from '@mastra/core';
|
|
4
9
|
import type { TABLE_NAMES } from '@mastra/core/storage';
|
|
5
10
|
import {
|
|
@@ -51,13 +56,6 @@ const createSampleEval = (agentName: string, isTest = false) => {
|
|
|
51
56
|
};
|
|
52
57
|
};
|
|
53
58
|
|
|
54
|
-
const checkWorkflowSnapshot = (snapshot: WorkflowRunState | string, stepId: string, status: string) => {
|
|
55
|
-
if (typeof snapshot === 'string') {
|
|
56
|
-
throw new Error('Expected WorkflowRunState, got string');
|
|
57
|
-
}
|
|
58
|
-
expect(snapshot.context?.[stepId]?.status).toBe(status);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
59
|
describe('UpstashStore', () => {
|
|
62
60
|
let store: UpstashStore;
|
|
63
61
|
const testTableName = 'test_table';
|
|
@@ -154,7 +152,7 @@ describe('UpstashStore', () => {
|
|
|
154
152
|
|
|
155
153
|
it('should get threads by resource ID', async () => {
|
|
156
154
|
const thread1 = createSampleThread();
|
|
157
|
-
const thread2 =
|
|
155
|
+
const thread2 = createSampleThread({ resourceId: thread1.resourceId });
|
|
158
156
|
const threads = [thread1, thread2];
|
|
159
157
|
|
|
160
158
|
const resourceId = threads[0].resourceId;
|
|
@@ -187,7 +185,7 @@ describe('UpstashStore', () => {
|
|
|
187
185
|
it('should fetch >100000 threads by resource ID', async () => {
|
|
188
186
|
const resourceId = `resource-${randomUUID()}`;
|
|
189
187
|
const total = 100_000;
|
|
190
|
-
const threads = Array.from({ length: total }, () => ({
|
|
188
|
+
const threads = Array.from({ length: total }, () => createSampleThread({ resourceId }));
|
|
191
189
|
|
|
192
190
|
await store.batchInsert({ tableName: TABLE_THREADS, records: threads });
|
|
193
191
|
|
|
@@ -300,13 +298,117 @@ describe('UpstashStore', () => {
|
|
|
300
298
|
createSampleMessageV2({ threadId, content: 'Third' }),
|
|
301
299
|
];
|
|
302
300
|
|
|
303
|
-
await store.saveMessages({ messages
|
|
301
|
+
await store.saveMessages({ messages, format: 'v2' });
|
|
304
302
|
|
|
305
303
|
const retrievedMessages = await store.getMessages({ threadId, format: 'v2' });
|
|
306
304
|
expect(retrievedMessages).toHaveLength(3);
|
|
307
305
|
expect(retrievedMessages.map((m: any) => m.content.parts[0].text)).toEqual(['First', 'Second', 'Third']);
|
|
308
306
|
});
|
|
309
307
|
|
|
308
|
+
it('should retrieve messages w/ next/prev messages by message id + resource id', async () => {
|
|
309
|
+
const thread = createSampleThread({ id: 'thread-one' });
|
|
310
|
+
await store.saveThread({ thread });
|
|
311
|
+
|
|
312
|
+
const thread2 = createSampleThread({ id: 'thread-two' });
|
|
313
|
+
await store.saveThread({ thread: thread2 });
|
|
314
|
+
|
|
315
|
+
const thread3 = createSampleThread({ id: 'thread-three' });
|
|
316
|
+
await store.saveThread({ thread: thread3 });
|
|
317
|
+
|
|
318
|
+
const messages: MastraMessageV2[] = [
|
|
319
|
+
createSampleMessageV2({ threadId: 'thread-one', content: 'First', resourceId: 'cross-thread-resource' }),
|
|
320
|
+
createSampleMessageV2({ threadId: 'thread-one', content: 'Second', resourceId: 'cross-thread-resource' }),
|
|
321
|
+
createSampleMessageV2({ threadId: 'thread-one', content: 'Third', resourceId: 'cross-thread-resource' }),
|
|
322
|
+
|
|
323
|
+
createSampleMessageV2({ threadId: 'thread-two', content: 'Fourth', resourceId: 'cross-thread-resource' }),
|
|
324
|
+
createSampleMessageV2({ threadId: 'thread-two', content: 'Fifth', resourceId: 'cross-thread-resource' }),
|
|
325
|
+
createSampleMessageV2({ threadId: 'thread-two', content: 'Sixth', resourceId: 'cross-thread-resource' }),
|
|
326
|
+
|
|
327
|
+
createSampleMessageV2({ threadId: 'thread-three', content: 'Seventh', resourceId: 'other-resource' }),
|
|
328
|
+
createSampleMessageV2({ threadId: 'thread-three', content: 'Eighth', resourceId: 'other-resource' }),
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
await store.saveMessages({ messages: messages, format: 'v2' });
|
|
332
|
+
|
|
333
|
+
const retrievedMessages = await store.getMessages({ threadId: 'thread-one', format: 'v2' });
|
|
334
|
+
expect(retrievedMessages).toHaveLength(3);
|
|
335
|
+
expect(retrievedMessages.map((m: any) => m.content.parts[0].text)).toEqual(['First', 'Second', 'Third']);
|
|
336
|
+
|
|
337
|
+
const retrievedMessages2 = await store.getMessages({ threadId: 'thread-two', format: 'v2' });
|
|
338
|
+
expect(retrievedMessages2).toHaveLength(3);
|
|
339
|
+
expect(retrievedMessages2.map((m: any) => m.content.parts[0].text)).toEqual(['Fourth', 'Fifth', 'Sixth']);
|
|
340
|
+
|
|
341
|
+
const retrievedMessages3 = await store.getMessages({ threadId: 'thread-three', format: 'v2' });
|
|
342
|
+
expect(retrievedMessages3).toHaveLength(2);
|
|
343
|
+
expect(retrievedMessages3.map((m: any) => m.content.parts[0].text)).toEqual(['Seventh', 'Eighth']);
|
|
344
|
+
|
|
345
|
+
const crossThreadMessages = await store.getMessages({
|
|
346
|
+
threadId: 'thread-doesnt-exist',
|
|
347
|
+
format: 'v2',
|
|
348
|
+
selectBy: {
|
|
349
|
+
last: 0,
|
|
350
|
+
include: [
|
|
351
|
+
{
|
|
352
|
+
id: messages[1].id,
|
|
353
|
+
threadId: 'thread-one',
|
|
354
|
+
withNextMessages: 2,
|
|
355
|
+
withPreviousMessages: 2,
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
id: messages[4].id,
|
|
359
|
+
threadId: 'thread-two',
|
|
360
|
+
withPreviousMessages: 2,
|
|
361
|
+
withNextMessages: 2,
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
expect(crossThreadMessages).toHaveLength(6);
|
|
368
|
+
expect(crossThreadMessages.filter(m => m.threadId === `thread-one`)).toHaveLength(3);
|
|
369
|
+
expect(crossThreadMessages.filter(m => m.threadId === `thread-two`)).toHaveLength(3);
|
|
370
|
+
|
|
371
|
+
const crossThreadMessages2 = await store.getMessages({
|
|
372
|
+
threadId: 'thread-one',
|
|
373
|
+
format: 'v2',
|
|
374
|
+
selectBy: {
|
|
375
|
+
last: 0,
|
|
376
|
+
include: [
|
|
377
|
+
{
|
|
378
|
+
id: messages[4].id,
|
|
379
|
+
threadId: 'thread-two',
|
|
380
|
+
withPreviousMessages: 1,
|
|
381
|
+
withNextMessages: 1,
|
|
382
|
+
},
|
|
383
|
+
],
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
expect(crossThreadMessages2).toHaveLength(3);
|
|
388
|
+
expect(crossThreadMessages2.filter(m => m.threadId === `thread-one`)).toHaveLength(0);
|
|
389
|
+
expect(crossThreadMessages2.filter(m => m.threadId === `thread-two`)).toHaveLength(3);
|
|
390
|
+
|
|
391
|
+
const crossThreadMessages3 = await store.getMessages({
|
|
392
|
+
threadId: 'thread-two',
|
|
393
|
+
format: 'v2',
|
|
394
|
+
selectBy: {
|
|
395
|
+
last: 0,
|
|
396
|
+
include: [
|
|
397
|
+
{
|
|
398
|
+
id: messages[1].id,
|
|
399
|
+
threadId: 'thread-one',
|
|
400
|
+
withNextMessages: 1,
|
|
401
|
+
withPreviousMessages: 1,
|
|
402
|
+
},
|
|
403
|
+
],
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
expect(crossThreadMessages3).toHaveLength(3);
|
|
408
|
+
expect(crossThreadMessages3.filter(m => m.threadId === `thread-one`)).toHaveLength(3);
|
|
409
|
+
expect(crossThreadMessages3.filter(m => m.threadId === `thread-two`)).toHaveLength(0);
|
|
410
|
+
});
|
|
411
|
+
|
|
310
412
|
it('should handle empty message array', async () => {
|
|
311
413
|
const result = await store.saveMessages({ messages: [] });
|
|
312
414
|
expect(result).toEqual([]);
|
|
@@ -1219,10 +1321,7 @@ describe('UpstashStore', () => {
|
|
|
1219
1321
|
describe('Enhanced existing methods with pagination', () => {
|
|
1220
1322
|
it('should support pagination in getThreadsByResourceId', async () => {
|
|
1221
1323
|
const resourceId = 'enhanced-resource';
|
|
1222
|
-
const threads = Array.from({ length: 17 }, () => ({
|
|
1223
|
-
...createSampleThread(),
|
|
1224
|
-
resourceId,
|
|
1225
|
-
}));
|
|
1324
|
+
const threads = Array.from({ length: 17 }, () => createSampleThread({ resourceId }));
|
|
1226
1325
|
|
|
1227
1326
|
for (const thread of threads) {
|
|
1228
1327
|
await store.saveThread({ thread });
|