@mastra/upstash 0.0.0-working-memory-per-user-20250620163010 → 0.0.0-zod-v4-compat-part-2-20250820135355
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +268 -6
- package/LICENSE.md +12 -4
- package/README.md +98 -0
- package/dist/index.cjs +1629 -626
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1645 -642
- package/dist/index.js.map +1 -0
- package/dist/storage/domains/legacy-evals/index.d.ts +28 -0
- package/dist/storage/domains/legacy-evals/index.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +86 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -0
- package/dist/storage/domains/operations/index.d.ts +40 -0
- package/dist/storage/domains/operations/index.d.ts.map +1 -0
- package/dist/storage/domains/scores/index.d.ts +65 -0
- package/dist/storage/domains/scores/index.d.ts.map +1 -0
- package/dist/storage/domains/traces/index.d.ts +28 -0
- package/dist/storage/domains/traces/index.d.ts.map +1 -0
- package/dist/storage/domains/utils.d.ts +12 -0
- package/dist/storage/domains/utils.d.ts.map +1 -0
- package/dist/storage/domains/workflows/index.d.ts +36 -0
- package/dist/storage/domains/workflows/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +208 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/vector/filter.d.ts +21 -0
- package/dist/vector/filter.d.ts.map +1 -0
- package/dist/vector/index.d.ts +79 -0
- package/dist/vector/index.d.ts.map +1 -0
- package/dist/vector/prompt.d.ts +6 -0
- package/dist/vector/prompt.d.ts.map +1 -0
- package/dist/vector/types.d.ts +23 -0
- package/dist/vector/types.d.ts.map +1 -0
- package/docker-compose.yaml +1 -1
- package/package.json +12 -12
- package/src/storage/domains/legacy-evals/index.ts +279 -0
- package/src/storage/domains/memory/index.ts +972 -0
- package/src/storage/domains/operations/index.ts +168 -0
- package/src/storage/domains/scores/index.ts +216 -0
- package/src/storage/domains/traces/index.ts +172 -0
- package/src/storage/domains/utils.ts +57 -0
- package/src/storage/domains/workflows/index.ts +243 -0
- package/src/storage/index.test.ts +13 -0
- package/src/storage/index.ts +149 -1078
- package/src/vector/filter.test.ts +7 -6
- package/src/vector/filter.ts +10 -4
- package/src/vector/hybrid.test.ts +1455 -0
- package/src/vector/index.test.ts +4 -4
- package/src/vector/index.ts +155 -69
- package/src/vector/types.ts +26 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +1 -1
- package/tsup.config.ts +22 -0
- package/dist/_tsup-dts-rollup.d.cts +0 -318
- package/dist/_tsup-dts-rollup.d.ts +0 -318
- package/dist/index.d.cts +0 -4
- package/src/storage/upstash.test.ts +0 -1386
package/dist/index.js
CHANGED
|
@@ -1,156 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { MastraStorage, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_EVALS, TABLE_TRACES, TABLE_THREADS, TABLE_RESOURCES } from '@mastra/core/storage';
|
|
1
|
+
import { MastraStorage, StoreOperations, TracesStorage, TABLE_TRACES, ScoresStorage, TABLE_SCORERS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, MemoryStorage, TABLE_THREADS, resolveMessageLimit, TABLE_RESOURCES, LegacyEvalsStorage, TABLE_EVALS, TABLE_MESSAGES, serializeDate } from '@mastra/core/storage';
|
|
3
2
|
import { Redis } from '@upstash/redis';
|
|
3
|
+
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
4
|
+
import { MessageList } from '@mastra/core/agent';
|
|
4
5
|
import { MastraVector } from '@mastra/core/vector';
|
|
5
6
|
import { Index } from '@upstash/vector';
|
|
6
7
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
7
8
|
|
|
8
9
|
// src/storage/index.ts
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
get supports() {
|
|
19
|
-
return {
|
|
20
|
-
selectByIncludeResourceScope: true,
|
|
21
|
-
resourceWorkingMemory: true
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
transformEvalRecord(record) {
|
|
25
|
-
let result = record.result;
|
|
26
|
-
if (typeof result === "string") {
|
|
27
|
-
try {
|
|
28
|
-
result = JSON.parse(result);
|
|
29
|
-
} catch {
|
|
30
|
-
console.warn("Failed to parse result JSON:");
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
let testInfo = record.test_info;
|
|
34
|
-
if (typeof testInfo === "string") {
|
|
35
|
-
try {
|
|
36
|
-
testInfo = JSON.parse(testInfo);
|
|
37
|
-
} catch {
|
|
38
|
-
console.warn("Failed to parse test_info JSON:");
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
agentName: record.agent_name,
|
|
43
|
-
input: record.input,
|
|
44
|
-
output: record.output,
|
|
45
|
-
result,
|
|
46
|
-
metricName: record.metric_name,
|
|
47
|
-
instructions: record.instructions,
|
|
48
|
-
testInfo,
|
|
49
|
-
globalRunId: record.global_run_id,
|
|
50
|
-
runId: record.run_id,
|
|
51
|
-
createdAt: typeof record.created_at === "string" ? record.created_at : record.created_at instanceof Date ? record.created_at.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
parseJSON(value) {
|
|
55
|
-
if (typeof value === "string") {
|
|
56
|
-
try {
|
|
57
|
-
return JSON.parse(value);
|
|
58
|
-
} catch {
|
|
59
|
-
return value;
|
|
60
|
-
}
|
|
10
|
+
function transformEvalRecord(record) {
|
|
11
|
+
let result = record.result;
|
|
12
|
+
if (typeof result === "string") {
|
|
13
|
+
try {
|
|
14
|
+
result = JSON.parse(result);
|
|
15
|
+
} catch {
|
|
16
|
+
console.warn("Failed to parse result JSON:");
|
|
61
17
|
}
|
|
62
|
-
return value;
|
|
63
|
-
}
|
|
64
|
-
getKey(tableName, keys) {
|
|
65
|
-
const keyParts = Object.entries(keys).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}:${value}`);
|
|
66
|
-
return `${tableName}:${keyParts.join(":")}`;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Scans for keys matching the given pattern using SCAN and returns them as an array.
|
|
70
|
-
* @param pattern Redis key pattern, e.g. "table:*"
|
|
71
|
-
* @param batchSize Number of keys to scan per batch (default: 1000)
|
|
72
|
-
*/
|
|
73
|
-
async scanKeys(pattern, batchSize = 1e4) {
|
|
74
|
-
let cursor = "0";
|
|
75
|
-
let keys = [];
|
|
76
|
-
do {
|
|
77
|
-
const [nextCursor, batch] = await this.redis.scan(cursor, {
|
|
78
|
-
match: pattern,
|
|
79
|
-
count: batchSize
|
|
80
|
-
});
|
|
81
|
-
keys.push(...batch);
|
|
82
|
-
cursor = nextCursor;
|
|
83
|
-
} while (cursor !== "0");
|
|
84
|
-
return keys;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Deletes all keys matching the given pattern using SCAN and DEL in batches.
|
|
88
|
-
* @param pattern Redis key pattern, e.g. "table:*"
|
|
89
|
-
* @param batchSize Number of keys to delete per batch (default: 1000)
|
|
90
|
-
*/
|
|
91
|
-
async scanAndDelete(pattern, batchSize = 1e4) {
|
|
92
|
-
let cursor = "0";
|
|
93
|
-
let totalDeleted = 0;
|
|
94
|
-
do {
|
|
95
|
-
const [nextCursor, keys] = await this.redis.scan(cursor, {
|
|
96
|
-
match: pattern,
|
|
97
|
-
count: batchSize
|
|
98
|
-
});
|
|
99
|
-
if (keys.length > 0) {
|
|
100
|
-
await this.redis.del(...keys);
|
|
101
|
-
totalDeleted += keys.length;
|
|
102
|
-
}
|
|
103
|
-
cursor = nextCursor;
|
|
104
|
-
} while (cursor !== "0");
|
|
105
|
-
return totalDeleted;
|
|
106
|
-
}
|
|
107
|
-
getMessageKey(threadId, messageId) {
|
|
108
|
-
const key = this.getKey(TABLE_MESSAGES, { threadId, id: messageId });
|
|
109
|
-
return key;
|
|
110
18
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
parsedSnapshot = JSON.parse(row.snapshot);
|
|
119
|
-
} catch (e) {
|
|
120
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
121
|
-
}
|
|
19
|
+
let testInfo = record.test_info;
|
|
20
|
+
if (typeof testInfo === "string") {
|
|
21
|
+
try {
|
|
22
|
+
testInfo = JSON.parse(testInfo);
|
|
23
|
+
} catch {
|
|
24
|
+
console.warn("Failed to parse test_info JSON:");
|
|
122
25
|
}
|
|
123
|
-
return {
|
|
124
|
-
workflowName: row.workflow_name,
|
|
125
|
-
runId: row.run_id,
|
|
126
|
-
snapshot: parsedSnapshot,
|
|
127
|
-
createdAt: this.ensureDate(row.createdAt),
|
|
128
|
-
updatedAt: this.ensureDate(row.updatedAt),
|
|
129
|
-
resourceId: row.resourceId
|
|
130
|
-
};
|
|
131
26
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
};
|
|
153
|
-
return { key, processedRecord };
|
|
27
|
+
return {
|
|
28
|
+
agentName: record.agent_name,
|
|
29
|
+
input: record.input,
|
|
30
|
+
output: record.output,
|
|
31
|
+
result,
|
|
32
|
+
metricName: record.metric_name,
|
|
33
|
+
instructions: record.instructions,
|
|
34
|
+
testInfo,
|
|
35
|
+
globalRunId: record.global_run_id,
|
|
36
|
+
runId: record.run_id,
|
|
37
|
+
createdAt: typeof record.created_at === "string" ? record.created_at : record.created_at instanceof Date ? record.created_at.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
var StoreLegacyEvalsUpstash = class extends LegacyEvalsStorage {
|
|
41
|
+
client;
|
|
42
|
+
operations;
|
|
43
|
+
constructor({ client, operations }) {
|
|
44
|
+
super();
|
|
45
|
+
this.client = client;
|
|
46
|
+
this.operations = operations;
|
|
154
47
|
}
|
|
155
48
|
/**
|
|
156
49
|
* @deprecated Use getEvals instead
|
|
@@ -158,11 +51,11 @@ var UpstashStore = class extends MastraStorage {
|
|
|
158
51
|
async getEvalsByAgentName(agentName, type) {
|
|
159
52
|
try {
|
|
160
53
|
const pattern = `${TABLE_EVALS}:*`;
|
|
161
|
-
const keys = await this.scanKeys(pattern);
|
|
54
|
+
const keys = await this.operations.scanKeys(pattern);
|
|
162
55
|
if (keys.length === 0) {
|
|
163
56
|
return [];
|
|
164
57
|
}
|
|
165
|
-
const pipeline = this.
|
|
58
|
+
const pipeline = this.client.pipeline();
|
|
166
59
|
keys.forEach((key) => pipeline.get(key));
|
|
167
60
|
const results = await pipeline.exec();
|
|
168
61
|
const nonNullRecords = results.filter(
|
|
@@ -196,170 +89,220 @@ var UpstashStore = class extends MastraStorage {
|
|
|
196
89
|
}
|
|
197
90
|
});
|
|
198
91
|
}
|
|
199
|
-
return filteredEvals.map((record) =>
|
|
92
|
+
return filteredEvals.map((record) => transformEvalRecord(record));
|
|
200
93
|
} catch (error) {
|
|
201
|
-
|
|
94
|
+
const mastraError = new MastraError(
|
|
95
|
+
{
|
|
96
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_EVALS_BY_AGENT_NAME_FAILED",
|
|
97
|
+
domain: ErrorDomain.STORAGE,
|
|
98
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
99
|
+
details: { agentName }
|
|
100
|
+
},
|
|
101
|
+
error
|
|
102
|
+
);
|
|
103
|
+
this.logger?.trackException(mastraError);
|
|
104
|
+
this.logger.error(mastraError.toString());
|
|
202
105
|
return [];
|
|
203
106
|
}
|
|
204
107
|
}
|
|
205
108
|
/**
|
|
206
|
-
*
|
|
109
|
+
* Get all evaluations with pagination and total count
|
|
110
|
+
* @param options Pagination and filtering options
|
|
111
|
+
* @returns Object with evals array and total count
|
|
207
112
|
*/
|
|
208
|
-
async
|
|
209
|
-
if (args.fromDate || args.toDate) {
|
|
210
|
-
args.dateRange = {
|
|
211
|
-
start: args.fromDate,
|
|
212
|
-
end: args.toDate
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
const { traces } = await this.getTracesPaginated(args);
|
|
216
|
-
return traces;
|
|
217
|
-
}
|
|
218
|
-
async getTracesPaginated(args) {
|
|
219
|
-
const { name, scope, page = 0, perPage = 100, attributes, filters, dateRange } = args;
|
|
220
|
-
const fromDate = dateRange?.start;
|
|
221
|
-
const toDate = dateRange?.end;
|
|
113
|
+
async getEvals(options) {
|
|
222
114
|
try {
|
|
223
|
-
const
|
|
224
|
-
const
|
|
115
|
+
const { agentName, type, page = 0, perPage = 100, dateRange } = options || {};
|
|
116
|
+
const fromDate = dateRange?.start;
|
|
117
|
+
const toDate = dateRange?.end;
|
|
118
|
+
const pattern = `${TABLE_EVALS}:*`;
|
|
119
|
+
const keys = await this.operations.scanKeys(pattern);
|
|
225
120
|
if (keys.length === 0) {
|
|
226
121
|
return {
|
|
227
|
-
|
|
122
|
+
evals: [],
|
|
228
123
|
total: 0,
|
|
229
124
|
page,
|
|
230
|
-
perPage
|
|
125
|
+
perPage,
|
|
231
126
|
hasMore: false
|
|
232
127
|
};
|
|
233
128
|
}
|
|
234
|
-
const pipeline = this.
|
|
129
|
+
const pipeline = this.client.pipeline();
|
|
235
130
|
keys.forEach((key) => pipeline.get(key));
|
|
236
131
|
const results = await pipeline.exec();
|
|
237
|
-
let
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (name) {
|
|
241
|
-
filteredTraces = filteredTraces.filter((record) => record.name?.toLowerCase().startsWith(name.toLowerCase()));
|
|
242
|
-
}
|
|
243
|
-
if (scope) {
|
|
244
|
-
filteredTraces = filteredTraces.filter((record) => record.scope === scope);
|
|
132
|
+
let filteredEvals = results.map((result) => result).filter((record) => record !== null && typeof record === "object");
|
|
133
|
+
if (agentName) {
|
|
134
|
+
filteredEvals = filteredEvals.filter((record) => record.agent_name === agentName);
|
|
245
135
|
}
|
|
246
|
-
if (
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
136
|
+
if (type === "test") {
|
|
137
|
+
filteredEvals = filteredEvals.filter((record) => {
|
|
138
|
+
if (!record.test_info) return false;
|
|
139
|
+
try {
|
|
140
|
+
if (typeof record.test_info === "string") {
|
|
141
|
+
const parsedTestInfo = JSON.parse(record.test_info);
|
|
142
|
+
return parsedTestInfo && typeof parsedTestInfo === "object" && "testPath" in parsedTestInfo;
|
|
143
|
+
}
|
|
144
|
+
return typeof record.test_info === "object" && "testPath" in record.test_info;
|
|
145
|
+
} catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
} else if (type === "live") {
|
|
150
|
+
filteredEvals = filteredEvals.filter((record) => {
|
|
151
|
+
if (!record.test_info) return true;
|
|
152
|
+
try {
|
|
153
|
+
if (typeof record.test_info === "string") {
|
|
154
|
+
const parsedTestInfo = JSON.parse(record.test_info);
|
|
155
|
+
return !(parsedTestInfo && typeof parsedTestInfo === "object" && "testPath" in parsedTestInfo);
|
|
156
|
+
}
|
|
157
|
+
return !(typeof record.test_info === "object" && "testPath" in record.test_info);
|
|
158
|
+
} catch {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
252
161
|
});
|
|
253
|
-
}
|
|
254
|
-
if (filters) {
|
|
255
|
-
filteredTraces = filteredTraces.filter(
|
|
256
|
-
(record) => Object.entries(filters).every(([key, value]) => record[key] === value)
|
|
257
|
-
);
|
|
258
162
|
}
|
|
259
163
|
if (fromDate) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
164
|
+
filteredEvals = filteredEvals.filter((record) => {
|
|
165
|
+
const createdAt = new Date(record.created_at || record.createdAt || 0);
|
|
166
|
+
return createdAt.getTime() >= fromDate.getTime();
|
|
167
|
+
});
|
|
263
168
|
}
|
|
264
169
|
if (toDate) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
170
|
+
filteredEvals = filteredEvals.filter((record) => {
|
|
171
|
+
const createdAt = new Date(record.created_at || record.createdAt || 0);
|
|
172
|
+
return createdAt.getTime() <= toDate.getTime();
|
|
173
|
+
});
|
|
268
174
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
events: this.parseJSON(record.events),
|
|
279
|
-
links: this.parseJSON(record.links),
|
|
280
|
-
attributes: this.parseJSON(record.attributes),
|
|
281
|
-
startTime: record.startTime,
|
|
282
|
-
endTime: record.endTime,
|
|
283
|
-
other: this.parseJSON(record.other),
|
|
284
|
-
createdAt: this.ensureDate(record.createdAt)
|
|
285
|
-
}));
|
|
286
|
-
const total = transformedTraces.length;
|
|
287
|
-
const resolvedPerPage = perPage || 100;
|
|
288
|
-
const start = page * resolvedPerPage;
|
|
289
|
-
const end = start + resolvedPerPage;
|
|
290
|
-
const paginatedTraces = transformedTraces.slice(start, end);
|
|
175
|
+
filteredEvals.sort((a, b) => {
|
|
176
|
+
const dateA = new Date(a.created_at || a.createdAt || 0).getTime();
|
|
177
|
+
const dateB = new Date(b.created_at || b.createdAt || 0).getTime();
|
|
178
|
+
return dateB - dateA;
|
|
179
|
+
});
|
|
180
|
+
const total = filteredEvals.length;
|
|
181
|
+
const start = page * perPage;
|
|
182
|
+
const end = start + perPage;
|
|
183
|
+
const paginatedEvals = filteredEvals.slice(start, end);
|
|
291
184
|
const hasMore = end < total;
|
|
185
|
+
const evals = paginatedEvals.map((record) => transformEvalRecord(record));
|
|
292
186
|
return {
|
|
293
|
-
|
|
187
|
+
evals,
|
|
294
188
|
total,
|
|
295
189
|
page,
|
|
296
|
-
perPage
|
|
190
|
+
perPage,
|
|
297
191
|
hasMore
|
|
298
192
|
};
|
|
299
193
|
} catch (error) {
|
|
300
|
-
|
|
194
|
+
const { page = 0, perPage = 100 } = options || {};
|
|
195
|
+
const mastraError = new MastraError(
|
|
196
|
+
{
|
|
197
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_EVALS_FAILED",
|
|
198
|
+
domain: ErrorDomain.STORAGE,
|
|
199
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
200
|
+
details: {
|
|
201
|
+
page,
|
|
202
|
+
perPage
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
error
|
|
206
|
+
);
|
|
207
|
+
this.logger.error(mastraError.toString());
|
|
208
|
+
this.logger?.trackException(mastraError);
|
|
301
209
|
return {
|
|
302
|
-
|
|
210
|
+
evals: [],
|
|
303
211
|
total: 0,
|
|
304
212
|
page,
|
|
305
|
-
perPage
|
|
213
|
+
perPage,
|
|
306
214
|
hasMore: false
|
|
307
215
|
};
|
|
308
216
|
}
|
|
309
217
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
async clearTable({ tableName }) {
|
|
325
|
-
const pattern = `${tableName}:*`;
|
|
326
|
-
await this.scanAndDelete(pattern);
|
|
327
|
-
}
|
|
328
|
-
async insert({ tableName, record }) {
|
|
329
|
-
const { key, processedRecord } = this.processRecord(tableName, record);
|
|
330
|
-
await this.redis.set(key, processedRecord);
|
|
331
|
-
}
|
|
332
|
-
async batchInsert(input) {
|
|
333
|
-
const { tableName, records } = input;
|
|
334
|
-
if (!records.length) return;
|
|
335
|
-
const batchSize = 1e3;
|
|
336
|
-
for (let i = 0; i < records.length; i += batchSize) {
|
|
337
|
-
const batch = records.slice(i, i + batchSize);
|
|
338
|
-
const pipeline = this.redis.pipeline();
|
|
339
|
-
for (const record of batch) {
|
|
340
|
-
const { key, processedRecord } = this.processRecord(tableName, record);
|
|
341
|
-
pipeline.set(key, processedRecord);
|
|
342
|
-
}
|
|
343
|
-
await pipeline.exec();
|
|
218
|
+
};
|
|
219
|
+
function ensureDate(value) {
|
|
220
|
+
if (!value) return null;
|
|
221
|
+
if (value instanceof Date) return value;
|
|
222
|
+
if (typeof value === "string") return new Date(value);
|
|
223
|
+
if (typeof value === "number") return new Date(value);
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
function parseJSON(value) {
|
|
227
|
+
if (typeof value === "string") {
|
|
228
|
+
try {
|
|
229
|
+
return JSON.parse(value);
|
|
230
|
+
} catch {
|
|
231
|
+
return value;
|
|
344
232
|
}
|
|
345
233
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
234
|
+
return value;
|
|
235
|
+
}
|
|
236
|
+
function getKey(tableName, keys) {
|
|
237
|
+
const keyParts = Object.entries(keys).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}:${value}`);
|
|
238
|
+
return `${tableName}:${keyParts.join(":")}`;
|
|
239
|
+
}
|
|
240
|
+
function processRecord(tableName, record) {
|
|
241
|
+
let key;
|
|
242
|
+
if (tableName === TABLE_MESSAGES) {
|
|
243
|
+
key = getKey(tableName, { threadId: record.threadId, id: record.id });
|
|
244
|
+
} else if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
245
|
+
key = getKey(tableName, {
|
|
246
|
+
namespace: record.namespace || "workflows",
|
|
247
|
+
workflow_name: record.workflow_name,
|
|
248
|
+
run_id: record.run_id,
|
|
249
|
+
...record.resourceId ? { resourceId: record.resourceId } : {}
|
|
250
|
+
});
|
|
251
|
+
} else if (tableName === TABLE_EVALS) {
|
|
252
|
+
key = getKey(tableName, { id: record.run_id });
|
|
253
|
+
} else {
|
|
254
|
+
key = getKey(tableName, { id: record.id });
|
|
255
|
+
}
|
|
256
|
+
const processedRecord = {
|
|
257
|
+
...record,
|
|
258
|
+
createdAt: serializeDate(record.createdAt),
|
|
259
|
+
updatedAt: serializeDate(record.updatedAt)
|
|
260
|
+
};
|
|
261
|
+
return { key, processedRecord };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/storage/domains/memory/index.ts
|
|
265
|
+
function getThreadMessagesKey(threadId) {
|
|
266
|
+
return `thread:${threadId}:messages`;
|
|
267
|
+
}
|
|
268
|
+
function getMessageKey(threadId, messageId) {
|
|
269
|
+
const key = getKey(TABLE_MESSAGES, { threadId, id: messageId });
|
|
270
|
+
return key;
|
|
271
|
+
}
|
|
272
|
+
var StoreMemoryUpstash = class extends MemoryStorage {
|
|
273
|
+
client;
|
|
274
|
+
operations;
|
|
275
|
+
constructor({ client, operations }) {
|
|
276
|
+
super();
|
|
277
|
+
this.client = client;
|
|
278
|
+
this.operations = operations;
|
|
350
279
|
}
|
|
351
280
|
async getThreadById({ threadId }) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
281
|
+
try {
|
|
282
|
+
const thread = await this.operations.load({
|
|
283
|
+
tableName: TABLE_THREADS,
|
|
284
|
+
keys: { id: threadId }
|
|
285
|
+
});
|
|
286
|
+
if (!thread) return null;
|
|
287
|
+
return {
|
|
288
|
+
...thread,
|
|
289
|
+
createdAt: ensureDate(thread.createdAt),
|
|
290
|
+
updatedAt: ensureDate(thread.updatedAt),
|
|
291
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
|
|
292
|
+
};
|
|
293
|
+
} catch (error) {
|
|
294
|
+
throw new MastraError(
|
|
295
|
+
{
|
|
296
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_THREAD_BY_ID_FAILED",
|
|
297
|
+
domain: ErrorDomain.STORAGE,
|
|
298
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
299
|
+
details: {
|
|
300
|
+
threadId
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
error
|
|
304
|
+
);
|
|
305
|
+
}
|
|
363
306
|
}
|
|
364
307
|
/**
|
|
365
308
|
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
@@ -367,12 +310,12 @@ var UpstashStore = class extends MastraStorage {
|
|
|
367
310
|
async getThreadsByResourceId({ resourceId }) {
|
|
368
311
|
try {
|
|
369
312
|
const pattern = `${TABLE_THREADS}:*`;
|
|
370
|
-
const keys = await this.scanKeys(pattern);
|
|
313
|
+
const keys = await this.operations.scanKeys(pattern);
|
|
371
314
|
if (keys.length === 0) {
|
|
372
315
|
return [];
|
|
373
316
|
}
|
|
374
317
|
const allThreads = [];
|
|
375
|
-
const pipeline = this.
|
|
318
|
+
const pipeline = this.client.pipeline();
|
|
376
319
|
keys.forEach((key) => pipeline.get(key));
|
|
377
320
|
const results = await pipeline.exec();
|
|
378
321
|
for (let i = 0; i < results.length; i++) {
|
|
@@ -380,8 +323,8 @@ var UpstashStore = class extends MastraStorage {
|
|
|
380
323
|
if (thread && thread.resourceId === resourceId) {
|
|
381
324
|
allThreads.push({
|
|
382
325
|
...thread,
|
|
383
|
-
createdAt:
|
|
384
|
-
updatedAt:
|
|
326
|
+
createdAt: ensureDate(thread.createdAt),
|
|
327
|
+
updatedAt: ensureDate(thread.updatedAt),
|
|
385
328
|
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
|
|
386
329
|
});
|
|
387
330
|
}
|
|
@@ -389,7 +332,19 @@ var UpstashStore = class extends MastraStorage {
|
|
|
389
332
|
allThreads.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
390
333
|
return allThreads;
|
|
391
334
|
} catch (error) {
|
|
392
|
-
|
|
335
|
+
const mastraError = new MastraError(
|
|
336
|
+
{
|
|
337
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED",
|
|
338
|
+
domain: ErrorDomain.STORAGE,
|
|
339
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
340
|
+
details: {
|
|
341
|
+
resourceId
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
error
|
|
345
|
+
);
|
|
346
|
+
this.logger?.trackException(mastraError);
|
|
347
|
+
this.logger.error(mastraError.toString());
|
|
393
348
|
return [];
|
|
394
349
|
}
|
|
395
350
|
}
|
|
@@ -410,7 +365,21 @@ var UpstashStore = class extends MastraStorage {
|
|
|
410
365
|
hasMore
|
|
411
366
|
};
|
|
412
367
|
} catch (error) {
|
|
413
|
-
|
|
368
|
+
const mastraError = new MastraError(
|
|
369
|
+
{
|
|
370
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
|
|
371
|
+
domain: ErrorDomain.STORAGE,
|
|
372
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
373
|
+
details: {
|
|
374
|
+
resourceId,
|
|
375
|
+
page,
|
|
376
|
+
perPage
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
error
|
|
380
|
+
);
|
|
381
|
+
this.logger?.trackException(mastraError);
|
|
382
|
+
this.logger.error(mastraError.toString());
|
|
414
383
|
return {
|
|
415
384
|
threads: [],
|
|
416
385
|
total: 0,
|
|
@@ -421,20 +390,45 @@ var UpstashStore = class extends MastraStorage {
|
|
|
421
390
|
}
|
|
422
391
|
}
|
|
423
392
|
async saveThread({ thread }) {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
393
|
+
try {
|
|
394
|
+
await this.operations.insert({
|
|
395
|
+
tableName: TABLE_THREADS,
|
|
396
|
+
record: thread
|
|
397
|
+
});
|
|
398
|
+
return thread;
|
|
399
|
+
} catch (error) {
|
|
400
|
+
const mastraError = new MastraError(
|
|
401
|
+
{
|
|
402
|
+
id: "STORAGE_UPSTASH_STORAGE_SAVE_THREAD_FAILED",
|
|
403
|
+
domain: ErrorDomain.STORAGE,
|
|
404
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
405
|
+
details: {
|
|
406
|
+
threadId: thread.id
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
error
|
|
410
|
+
);
|
|
411
|
+
this.logger?.trackException(mastraError);
|
|
412
|
+
this.logger.error(mastraError.toString());
|
|
413
|
+
throw mastraError;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async updateThread({
|
|
417
|
+
id,
|
|
432
418
|
title,
|
|
433
419
|
metadata
|
|
434
420
|
}) {
|
|
435
421
|
const thread = await this.getThreadById({ threadId: id });
|
|
436
422
|
if (!thread) {
|
|
437
|
-
throw new
|
|
423
|
+
throw new MastraError({
|
|
424
|
+
id: "STORAGE_UPSTASH_STORAGE_UPDATE_THREAD_FAILED",
|
|
425
|
+
domain: ErrorDomain.STORAGE,
|
|
426
|
+
category: ErrorCategory.USER,
|
|
427
|
+
text: `Thread ${id} not found`,
|
|
428
|
+
details: {
|
|
429
|
+
threadId: id
|
|
430
|
+
}
|
|
431
|
+
});
|
|
438
432
|
}
|
|
439
433
|
const updatedThread = {
|
|
440
434
|
...thread,
|
|
@@ -444,67 +438,149 @@ var UpstashStore = class extends MastraStorage {
|
|
|
444
438
|
...metadata
|
|
445
439
|
}
|
|
446
440
|
};
|
|
447
|
-
|
|
448
|
-
|
|
441
|
+
try {
|
|
442
|
+
await this.saveThread({ thread: updatedThread });
|
|
443
|
+
return updatedThread;
|
|
444
|
+
} catch (error) {
|
|
445
|
+
throw new MastraError(
|
|
446
|
+
{
|
|
447
|
+
id: "STORAGE_UPSTASH_STORAGE_UPDATE_THREAD_FAILED",
|
|
448
|
+
domain: ErrorDomain.STORAGE,
|
|
449
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
450
|
+
details: {
|
|
451
|
+
threadId: id
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
error
|
|
455
|
+
);
|
|
456
|
+
}
|
|
449
457
|
}
|
|
450
458
|
async deleteThread({ threadId }) {
|
|
451
|
-
const threadKey =
|
|
452
|
-
const threadMessagesKey =
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
459
|
+
const threadKey = getKey(TABLE_THREADS, { id: threadId });
|
|
460
|
+
const threadMessagesKey = getThreadMessagesKey(threadId);
|
|
461
|
+
try {
|
|
462
|
+
const messageIds = await this.client.zrange(threadMessagesKey, 0, -1);
|
|
463
|
+
const pipeline = this.client.pipeline();
|
|
464
|
+
pipeline.del(threadKey);
|
|
465
|
+
pipeline.del(threadMessagesKey);
|
|
466
|
+
for (let i = 0; i < messageIds.length; i++) {
|
|
467
|
+
const messageId = messageIds[i];
|
|
468
|
+
const messageKey = getMessageKey(threadId, messageId);
|
|
469
|
+
pipeline.del(messageKey);
|
|
470
|
+
}
|
|
471
|
+
await pipeline.exec();
|
|
472
|
+
await this.operations.scanAndDelete(getMessageKey(threadId, "*"));
|
|
473
|
+
} catch (error) {
|
|
474
|
+
throw new MastraError(
|
|
475
|
+
{
|
|
476
|
+
id: "STORAGE_UPSTASH_STORAGE_DELETE_THREAD_FAILED",
|
|
477
|
+
domain: ErrorDomain.STORAGE,
|
|
478
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
479
|
+
details: {
|
|
480
|
+
threadId
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
error
|
|
484
|
+
);
|
|
485
|
+
}
|
|
464
486
|
}
|
|
465
487
|
async saveMessages(args) {
|
|
466
488
|
const { messages, format = "v1" } = args;
|
|
467
489
|
if (messages.length === 0) return [];
|
|
468
490
|
const threadId = messages[0]?.threadId;
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
491
|
+
try {
|
|
492
|
+
if (!threadId) {
|
|
493
|
+
throw new Error("Thread ID is required");
|
|
494
|
+
}
|
|
495
|
+
const thread = await this.getThreadById({ threadId });
|
|
496
|
+
if (!thread) {
|
|
497
|
+
throw new Error(`Thread ${threadId} not found`);
|
|
498
|
+
}
|
|
499
|
+
} catch (error) {
|
|
500
|
+
throw new MastraError(
|
|
501
|
+
{
|
|
502
|
+
id: "STORAGE_UPSTASH_STORAGE_SAVE_MESSAGES_INVALID_ARGS",
|
|
503
|
+
domain: ErrorDomain.STORAGE,
|
|
504
|
+
category: ErrorCategory.USER
|
|
505
|
+
},
|
|
506
|
+
error
|
|
507
|
+
);
|
|
475
508
|
}
|
|
476
|
-
const messagesWithIndex = messages.map((message, index) =>
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const existingThread = await this.redis.get(threadKey);
|
|
482
|
-
const batchSize = 1e3;
|
|
483
|
-
for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
|
|
484
|
-
const batch = messagesWithIndex.slice(i, i + batchSize);
|
|
485
|
-
const pipeline = this.redis.pipeline();
|
|
486
|
-
for (const message of batch) {
|
|
487
|
-
const key = this.getMessageKey(message.threadId, message.id);
|
|
488
|
-
const createdAtScore = new Date(message.createdAt).getTime();
|
|
489
|
-
const score = message._index !== void 0 ? message._index : createdAtScore;
|
|
490
|
-
pipeline.set(key, message);
|
|
491
|
-
pipeline.zadd(this.getThreadMessagesKey(message.threadId), {
|
|
492
|
-
score,
|
|
493
|
-
member: message.id
|
|
494
|
-
});
|
|
509
|
+
const messagesWithIndex = messages.map((message, index) => {
|
|
510
|
+
if (!message.threadId) {
|
|
511
|
+
throw new Error(
|
|
512
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
513
|
+
);
|
|
495
514
|
}
|
|
496
|
-
if (
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
};
|
|
501
|
-
pipeline.set(threadKey, this.processRecord(TABLE_THREADS, updatedThread).processedRecord);
|
|
515
|
+
if (!message.resourceId) {
|
|
516
|
+
throw new Error(
|
|
517
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
518
|
+
);
|
|
502
519
|
}
|
|
503
|
-
|
|
520
|
+
return {
|
|
521
|
+
...message,
|
|
522
|
+
_index: index
|
|
523
|
+
};
|
|
524
|
+
});
|
|
525
|
+
const threadKey = getKey(TABLE_THREADS, { id: threadId });
|
|
526
|
+
const existingThread = await this.client.get(threadKey);
|
|
527
|
+
try {
|
|
528
|
+
const batchSize = 1e3;
|
|
529
|
+
for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
|
|
530
|
+
const batch = messagesWithIndex.slice(i, i + batchSize);
|
|
531
|
+
const pipeline = this.client.pipeline();
|
|
532
|
+
for (const message of batch) {
|
|
533
|
+
const key = getMessageKey(message.threadId, message.id);
|
|
534
|
+
const createdAtScore = new Date(message.createdAt).getTime();
|
|
535
|
+
const score = message._index !== void 0 ? message._index : createdAtScore;
|
|
536
|
+
const existingKeyPattern = getMessageKey("*", message.id);
|
|
537
|
+
const keys = await this.operations.scanKeys(existingKeyPattern);
|
|
538
|
+
if (keys.length > 0) {
|
|
539
|
+
const pipeline2 = this.client.pipeline();
|
|
540
|
+
keys.forEach((key2) => pipeline2.get(key2));
|
|
541
|
+
const results = await pipeline2.exec();
|
|
542
|
+
const existingMessages = results.filter(
|
|
543
|
+
(msg) => msg !== null
|
|
544
|
+
);
|
|
545
|
+
for (const existingMessage of existingMessages) {
|
|
546
|
+
const existingMessageKey = getMessageKey(existingMessage.threadId, existingMessage.id);
|
|
547
|
+
if (existingMessage && existingMessage.threadId !== message.threadId) {
|
|
548
|
+
pipeline.del(existingMessageKey);
|
|
549
|
+
pipeline.zrem(getThreadMessagesKey(existingMessage.threadId), existingMessage.id);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
pipeline.set(key, message);
|
|
554
|
+
pipeline.zadd(getThreadMessagesKey(message.threadId), {
|
|
555
|
+
score,
|
|
556
|
+
member: message.id
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
if (i === 0 && existingThread) {
|
|
560
|
+
const updatedThread = {
|
|
561
|
+
...existingThread,
|
|
562
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
563
|
+
};
|
|
564
|
+
pipeline.set(threadKey, processRecord(TABLE_THREADS, updatedThread).processedRecord);
|
|
565
|
+
}
|
|
566
|
+
await pipeline.exec();
|
|
567
|
+
}
|
|
568
|
+
const list = new MessageList().add(messages, "memory");
|
|
569
|
+
if (format === `v2`) return list.get.all.v2();
|
|
570
|
+
return list.get.all.v1();
|
|
571
|
+
} catch (error) {
|
|
572
|
+
throw new MastraError(
|
|
573
|
+
{
|
|
574
|
+
id: "STORAGE_UPSTASH_STORAGE_SAVE_MESSAGES_FAILED",
|
|
575
|
+
domain: ErrorDomain.STORAGE,
|
|
576
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
577
|
+
details: {
|
|
578
|
+
threadId
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
error
|
|
582
|
+
);
|
|
504
583
|
}
|
|
505
|
-
const list = new MessageList().add(messages, "memory");
|
|
506
|
-
if (format === `v2`) return list.get.all.v2();
|
|
507
|
-
return list.get.all.v1();
|
|
508
584
|
}
|
|
509
585
|
async _getIncludedMessages(threadId, selectBy) {
|
|
510
586
|
const messageIds = /* @__PURE__ */ new Set();
|
|
@@ -514,29 +590,29 @@ var UpstashStore = class extends MastraStorage {
|
|
|
514
590
|
messageIds.add(item.id);
|
|
515
591
|
const itemThreadId = item.threadId || threadId;
|
|
516
592
|
messageIdToThreadIds[item.id] = itemThreadId;
|
|
517
|
-
const itemThreadMessagesKey =
|
|
518
|
-
const rank = await this.
|
|
593
|
+
const itemThreadMessagesKey = getThreadMessagesKey(itemThreadId);
|
|
594
|
+
const rank = await this.client.zrank(itemThreadMessagesKey, item.id);
|
|
519
595
|
if (rank === null) continue;
|
|
520
596
|
if (item.withPreviousMessages) {
|
|
521
597
|
const start = Math.max(0, rank - item.withPreviousMessages);
|
|
522
|
-
const prevIds = rank === 0 ? [] : await this.
|
|
598
|
+
const prevIds = rank === 0 ? [] : await this.client.zrange(itemThreadMessagesKey, start, rank - 1);
|
|
523
599
|
prevIds.forEach((id) => {
|
|
524
600
|
messageIds.add(id);
|
|
525
601
|
messageIdToThreadIds[id] = itemThreadId;
|
|
526
602
|
});
|
|
527
603
|
}
|
|
528
604
|
if (item.withNextMessages) {
|
|
529
|
-
const nextIds = await this.
|
|
605
|
+
const nextIds = await this.client.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
|
|
530
606
|
nextIds.forEach((id) => {
|
|
531
607
|
messageIds.add(id);
|
|
532
608
|
messageIdToThreadIds[id] = itemThreadId;
|
|
533
609
|
});
|
|
534
610
|
}
|
|
535
611
|
}
|
|
536
|
-
const pipeline = this.
|
|
612
|
+
const pipeline = this.client.pipeline();
|
|
537
613
|
Array.from(messageIds).forEach((id) => {
|
|
538
614
|
const tId = messageIdToThreadIds[id] || threadId;
|
|
539
|
-
pipeline.get(
|
|
615
|
+
pipeline.get(getMessageKey(tId, id));
|
|
540
616
|
});
|
|
541
617
|
const results = await pipeline.exec();
|
|
542
618
|
return results.filter((result) => result !== null);
|
|
@@ -548,69 +624,91 @@ var UpstashStore = class extends MastraStorage {
|
|
|
548
624
|
selectBy,
|
|
549
625
|
format
|
|
550
626
|
}) {
|
|
551
|
-
const threadMessagesKey =
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
627
|
+
const threadMessagesKey = getThreadMessagesKey(threadId);
|
|
628
|
+
try {
|
|
629
|
+
const allMessageIds = await this.client.zrange(threadMessagesKey, 0, -1);
|
|
630
|
+
const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
631
|
+
const messageIds = /* @__PURE__ */ new Set();
|
|
632
|
+
const messageIdToThreadIds = {};
|
|
633
|
+
if (limit === 0 && !selectBy?.include) {
|
|
634
|
+
return [];
|
|
635
|
+
}
|
|
636
|
+
if (limit === Number.MAX_SAFE_INTEGER) {
|
|
637
|
+
const allIds = await this.client.zrange(threadMessagesKey, 0, -1);
|
|
638
|
+
allIds.forEach((id) => {
|
|
639
|
+
messageIds.add(id);
|
|
640
|
+
messageIdToThreadIds[id] = threadId;
|
|
641
|
+
});
|
|
642
|
+
} else if (limit > 0) {
|
|
643
|
+
const latestIds = await this.client.zrange(threadMessagesKey, -limit, -1);
|
|
644
|
+
latestIds.forEach((id) => {
|
|
645
|
+
messageIds.add(id);
|
|
646
|
+
messageIdToThreadIds[id] = threadId;
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
650
|
+
const messages = [
|
|
651
|
+
...includedMessages,
|
|
652
|
+
...(await Promise.all(
|
|
653
|
+
Array.from(messageIds).map(async (id) => {
|
|
654
|
+
const tId = messageIdToThreadIds[id] || threadId;
|
|
655
|
+
const byThreadId = await this.client.get(getMessageKey(tId, id));
|
|
656
|
+
if (byThreadId) return byThreadId;
|
|
657
|
+
return null;
|
|
658
|
+
})
|
|
659
|
+
)).filter((msg) => msg !== null)
|
|
660
|
+
];
|
|
661
|
+
messages.sort((a, b) => allMessageIds.indexOf(a.id) - allMessageIds.indexOf(b.id));
|
|
662
|
+
const seen = /* @__PURE__ */ new Set();
|
|
663
|
+
const dedupedMessages = messages.filter((row) => {
|
|
664
|
+
if (seen.has(row.id)) return false;
|
|
665
|
+
seen.add(row.id);
|
|
666
|
+
return true;
|
|
564
667
|
});
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
messageIds.add(id);
|
|
569
|
-
messageIdToThreadIds[id] = threadId;
|
|
668
|
+
const prepared = dedupedMessages.filter((message) => message !== null && message !== void 0).map((message) => {
|
|
669
|
+
const { _index, ...messageWithoutIndex } = message;
|
|
670
|
+
return messageWithoutIndex;
|
|
570
671
|
});
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const byThreadId = await this.redis.get(this.getMessageKey(tId, id));
|
|
579
|
-
if (byThreadId) return byThreadId;
|
|
580
|
-
return null;
|
|
581
|
-
})
|
|
582
|
-
)).filter((msg) => msg !== null)
|
|
583
|
-
];
|
|
584
|
-
messages.sort((a, b) => allMessageIds.indexOf(a.id) - allMessageIds.indexOf(b.id));
|
|
585
|
-
const seen = /* @__PURE__ */ new Set();
|
|
586
|
-
const dedupedMessages = messages.filter((row) => {
|
|
587
|
-
if (seen.has(row.id)) return false;
|
|
588
|
-
seen.add(row.id);
|
|
589
|
-
return true;
|
|
590
|
-
});
|
|
591
|
-
const prepared = dedupedMessages.filter((message) => message !== null && message !== void 0).map((message) => {
|
|
592
|
-
const { _index, ...messageWithoutIndex } = message;
|
|
593
|
-
return messageWithoutIndex;
|
|
594
|
-
});
|
|
595
|
-
if (format === "v2") {
|
|
672
|
+
if (format === "v2") {
|
|
673
|
+
return prepared.map((msg) => ({
|
|
674
|
+
...msg,
|
|
675
|
+
createdAt: new Date(msg.createdAt),
|
|
676
|
+
content: msg.content || { format: 2, parts: [{ type: "text", text: "" }] }
|
|
677
|
+
}));
|
|
678
|
+
}
|
|
596
679
|
return prepared.map((msg) => ({
|
|
597
680
|
...msg,
|
|
598
|
-
|
|
681
|
+
createdAt: new Date(msg.createdAt)
|
|
599
682
|
}));
|
|
683
|
+
} catch (error) {
|
|
684
|
+
throw new MastraError(
|
|
685
|
+
{
|
|
686
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_MESSAGES_FAILED",
|
|
687
|
+
domain: ErrorDomain.STORAGE,
|
|
688
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
689
|
+
details: {
|
|
690
|
+
threadId
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
error
|
|
694
|
+
);
|
|
600
695
|
}
|
|
601
|
-
return prepared;
|
|
602
696
|
}
|
|
603
697
|
async getMessagesPaginated(args) {
|
|
604
698
|
const { threadId, selectBy, format } = args;
|
|
605
699
|
const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
|
|
606
700
|
const fromDate = dateRange?.start;
|
|
607
701
|
const toDate = dateRange?.end;
|
|
608
|
-
const threadMessagesKey =
|
|
702
|
+
const threadMessagesKey = getThreadMessagesKey(threadId);
|
|
609
703
|
const messages = [];
|
|
610
|
-
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
611
|
-
messages.push(...includedMessages);
|
|
612
704
|
try {
|
|
613
|
-
const
|
|
705
|
+
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
706
|
+
messages.push(...includedMessages);
|
|
707
|
+
const allMessageIds = await this.client.zrange(
|
|
708
|
+
threadMessagesKey,
|
|
709
|
+
args?.selectBy?.last ? -args.selectBy.last : 0,
|
|
710
|
+
-1
|
|
711
|
+
);
|
|
614
712
|
if (allMessageIds.length === 0) {
|
|
615
713
|
return {
|
|
616
714
|
messages: [],
|
|
@@ -620,8 +718,8 @@ var UpstashStore = class extends MastraStorage {
|
|
|
620
718
|
hasMore: false
|
|
621
719
|
};
|
|
622
720
|
}
|
|
623
|
-
const pipeline = this.
|
|
624
|
-
allMessageIds.forEach((id) => pipeline.get(
|
|
721
|
+
const pipeline = this.client.pipeline();
|
|
722
|
+
allMessageIds.forEach((id) => pipeline.get(getMessageKey(threadId, id)));
|
|
625
723
|
const results = await pipeline.exec();
|
|
626
724
|
let messagesData = results.filter((msg) => msg !== null);
|
|
627
725
|
if (fromDate) {
|
|
@@ -647,7 +745,19 @@ var UpstashStore = class extends MastraStorage {
|
|
|
647
745
|
hasMore
|
|
648
746
|
};
|
|
649
747
|
} catch (error) {
|
|
650
|
-
|
|
748
|
+
const mastraError = new MastraError(
|
|
749
|
+
{
|
|
750
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_MESSAGES_PAGINATED_FAILED",
|
|
751
|
+
domain: ErrorDomain.STORAGE,
|
|
752
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
753
|
+
details: {
|
|
754
|
+
threadId
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
error
|
|
758
|
+
);
|
|
759
|
+
this.logger.error(mastraError.toString());
|
|
760
|
+
this.logger?.trackException(mastraError);
|
|
651
761
|
return {
|
|
652
762
|
messages: [],
|
|
653
763
|
total: 0,
|
|
@@ -657,161 +767,849 @@ var UpstashStore = class extends MastraStorage {
|
|
|
657
767
|
};
|
|
658
768
|
}
|
|
659
769
|
}
|
|
660
|
-
async
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
workflow_name: workflowName,
|
|
667
|
-
run_id: runId,
|
|
668
|
-
snapshot,
|
|
669
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
670
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
770
|
+
async getResourceById({ resourceId }) {
|
|
771
|
+
try {
|
|
772
|
+
const key = `${TABLE_RESOURCES}:${resourceId}`;
|
|
773
|
+
const data = await this.client.get(key);
|
|
774
|
+
if (!data) {
|
|
775
|
+
return null;
|
|
671
776
|
}
|
|
672
|
-
|
|
777
|
+
return {
|
|
778
|
+
...data,
|
|
779
|
+
createdAt: new Date(data.createdAt),
|
|
780
|
+
updatedAt: new Date(data.updatedAt),
|
|
781
|
+
// Ensure workingMemory is always returned as a string, regardless of automatic parsing
|
|
782
|
+
workingMemory: typeof data.workingMemory === "object" ? JSON.stringify(data.workingMemory) : data.workingMemory,
|
|
783
|
+
metadata: typeof data.metadata === "string" ? JSON.parse(data.metadata) : data.metadata
|
|
784
|
+
};
|
|
785
|
+
} catch (error) {
|
|
786
|
+
this.logger.error("Error getting resource by ID:", error);
|
|
787
|
+
throw error;
|
|
788
|
+
}
|
|
673
789
|
}
|
|
674
|
-
async
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
790
|
+
async saveResource({ resource }) {
|
|
791
|
+
try {
|
|
792
|
+
const key = `${TABLE_RESOURCES}:${resource.id}`;
|
|
793
|
+
const serializedResource = {
|
|
794
|
+
...resource,
|
|
795
|
+
metadata: JSON.stringify(resource.metadata),
|
|
796
|
+
createdAt: resource.createdAt.toISOString(),
|
|
797
|
+
updatedAt: resource.updatedAt.toISOString()
|
|
798
|
+
};
|
|
799
|
+
await this.client.set(key, serializedResource);
|
|
800
|
+
return resource;
|
|
801
|
+
} catch (error) {
|
|
802
|
+
this.logger.error("Error saving resource:", error);
|
|
803
|
+
throw error;
|
|
804
|
+
}
|
|
684
805
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
async getEvals(options) {
|
|
806
|
+
async updateResource({
|
|
807
|
+
resourceId,
|
|
808
|
+
workingMemory,
|
|
809
|
+
metadata
|
|
810
|
+
}) {
|
|
691
811
|
try {
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
total: 0,
|
|
701
|
-
page,
|
|
702
|
-
perPage,
|
|
703
|
-
hasMore: false
|
|
812
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
813
|
+
if (!existingResource) {
|
|
814
|
+
const newResource = {
|
|
815
|
+
id: resourceId,
|
|
816
|
+
workingMemory,
|
|
817
|
+
metadata: metadata || {},
|
|
818
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
819
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
704
820
|
};
|
|
821
|
+
return this.saveResource({ resource: newResource });
|
|
705
822
|
}
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
823
|
+
const updatedResource = {
|
|
824
|
+
...existingResource,
|
|
825
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
826
|
+
metadata: {
|
|
827
|
+
...existingResource.metadata,
|
|
828
|
+
...metadata
|
|
829
|
+
},
|
|
830
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
831
|
+
};
|
|
832
|
+
await this.saveResource({ resource: updatedResource });
|
|
833
|
+
return updatedResource;
|
|
834
|
+
} catch (error) {
|
|
835
|
+
this.logger.error("Error updating resource:", error);
|
|
836
|
+
throw error;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
async updateMessages(args) {
|
|
840
|
+
const { messages } = args;
|
|
841
|
+
if (messages.length === 0) {
|
|
842
|
+
return [];
|
|
843
|
+
}
|
|
844
|
+
try {
|
|
845
|
+
const messageIds = messages.map((m) => m.id);
|
|
846
|
+
const existingMessages = [];
|
|
847
|
+
const messageIdToKey = {};
|
|
848
|
+
for (const messageId of messageIds) {
|
|
849
|
+
const pattern = getMessageKey("*", messageId);
|
|
850
|
+
const keys = await this.operations.scanKeys(pattern);
|
|
851
|
+
for (const key of keys) {
|
|
852
|
+
const message = await this.client.get(key);
|
|
853
|
+
if (message && message.id === messageId) {
|
|
854
|
+
existingMessages.push(message);
|
|
855
|
+
messageIdToKey[messageId] = key;
|
|
856
|
+
break;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
712
859
|
}
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
860
|
+
if (existingMessages.length === 0) {
|
|
861
|
+
return [];
|
|
862
|
+
}
|
|
863
|
+
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
864
|
+
const pipeline = this.client.pipeline();
|
|
865
|
+
for (const existingMessage of existingMessages) {
|
|
866
|
+
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
867
|
+
if (!updatePayload) continue;
|
|
868
|
+
const { id, ...fieldsToUpdate } = updatePayload;
|
|
869
|
+
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
870
|
+
threadIdsToUpdate.add(existingMessage.threadId);
|
|
871
|
+
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
872
|
+
threadIdsToUpdate.add(updatePayload.threadId);
|
|
873
|
+
}
|
|
874
|
+
const updatedMessage = { ...existingMessage };
|
|
875
|
+
if (fieldsToUpdate.content) {
|
|
876
|
+
const existingContent = existingMessage.content;
|
|
877
|
+
const newContent = {
|
|
878
|
+
...existingContent,
|
|
879
|
+
...fieldsToUpdate.content,
|
|
880
|
+
// Deep merge metadata if it exists on both
|
|
881
|
+
...existingContent?.metadata && fieldsToUpdate.content.metadata ? {
|
|
882
|
+
metadata: {
|
|
883
|
+
...existingContent.metadata,
|
|
884
|
+
...fieldsToUpdate.content.metadata
|
|
885
|
+
}
|
|
886
|
+
} : {}
|
|
887
|
+
};
|
|
888
|
+
updatedMessage.content = newContent;
|
|
889
|
+
}
|
|
890
|
+
for (const key2 in fieldsToUpdate) {
|
|
891
|
+
if (Object.prototype.hasOwnProperty.call(fieldsToUpdate, key2) && key2 !== "content") {
|
|
892
|
+
updatedMessage[key2] = fieldsToUpdate[key2];
|
|
724
893
|
}
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if (
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
894
|
+
}
|
|
895
|
+
const key = messageIdToKey[id];
|
|
896
|
+
if (key) {
|
|
897
|
+
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
898
|
+
const oldThreadMessagesKey = getThreadMessagesKey(existingMessage.threadId);
|
|
899
|
+
pipeline.zrem(oldThreadMessagesKey, id);
|
|
900
|
+
pipeline.del(key);
|
|
901
|
+
const newKey = getMessageKey(updatePayload.threadId, id);
|
|
902
|
+
pipeline.set(newKey, updatedMessage);
|
|
903
|
+
const newThreadMessagesKey = getThreadMessagesKey(updatePayload.threadId);
|
|
904
|
+
const score = updatedMessage._index !== void 0 ? updatedMessage._index : new Date(updatedMessage.createdAt).getTime();
|
|
905
|
+
pipeline.zadd(newThreadMessagesKey, { score, member: id });
|
|
906
|
+
} else {
|
|
907
|
+
pipeline.set(key, updatedMessage);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
const now = /* @__PURE__ */ new Date();
|
|
912
|
+
for (const threadId of threadIdsToUpdate) {
|
|
913
|
+
if (threadId) {
|
|
914
|
+
const threadKey = getKey(TABLE_THREADS, { id: threadId });
|
|
915
|
+
const existingThread = await this.client.get(threadKey);
|
|
916
|
+
if (existingThread) {
|
|
917
|
+
const updatedThread = {
|
|
918
|
+
...existingThread,
|
|
919
|
+
updatedAt: now
|
|
920
|
+
};
|
|
921
|
+
pipeline.set(threadKey, processRecord(TABLE_THREADS, updatedThread).processedRecord);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
await pipeline.exec();
|
|
926
|
+
const updatedMessages = [];
|
|
927
|
+
for (const messageId of messageIds) {
|
|
928
|
+
const key = messageIdToKey[messageId];
|
|
929
|
+
if (key) {
|
|
930
|
+
const updatedMessage = await this.client.get(key);
|
|
931
|
+
if (updatedMessage) {
|
|
932
|
+
const v2e = updatedMessage;
|
|
933
|
+
updatedMessages.push(v2e);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
return updatedMessages;
|
|
938
|
+
} catch (error) {
|
|
939
|
+
throw new MastraError(
|
|
940
|
+
{
|
|
941
|
+
id: "STORAGE_UPSTASH_STORAGE_UPDATE_MESSAGES_FAILED",
|
|
942
|
+
domain: ErrorDomain.STORAGE,
|
|
943
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
944
|
+
details: {
|
|
945
|
+
messageIds: messages.map((m) => m.id).join(",")
|
|
946
|
+
}
|
|
947
|
+
},
|
|
948
|
+
error
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
async deleteMessages(messageIds) {
|
|
953
|
+
if (!messageIds || messageIds.length === 0) {
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
try {
|
|
957
|
+
const threadIds = /* @__PURE__ */ new Set();
|
|
958
|
+
const messageKeys = [];
|
|
959
|
+
for (const messageId of messageIds) {
|
|
960
|
+
const pattern = getMessageKey("*", messageId);
|
|
961
|
+
const keys = await this.operations.scanKeys(pattern);
|
|
962
|
+
for (const key of keys) {
|
|
963
|
+
const message = await this.client.get(key);
|
|
964
|
+
if (message && message.id === messageId) {
|
|
965
|
+
messageKeys.push(key);
|
|
966
|
+
if (message.threadId) {
|
|
967
|
+
threadIds.add(message.threadId);
|
|
733
968
|
}
|
|
734
|
-
|
|
735
|
-
} catch {
|
|
736
|
-
return true;
|
|
969
|
+
break;
|
|
737
970
|
}
|
|
738
|
-
}
|
|
971
|
+
}
|
|
739
972
|
}
|
|
740
|
-
if (
|
|
741
|
-
|
|
742
|
-
const createdAt = new Date(record.created_at || record.createdAt || 0);
|
|
743
|
-
return createdAt.getTime() >= fromDate.getTime();
|
|
744
|
-
});
|
|
973
|
+
if (messageKeys.length === 0) {
|
|
974
|
+
return;
|
|
745
975
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
return createdAt.getTime() <= toDate.getTime();
|
|
750
|
-
});
|
|
976
|
+
const pipeline = this.client.pipeline();
|
|
977
|
+
for (const key of messageKeys) {
|
|
978
|
+
pipeline.del(key);
|
|
751
979
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
980
|
+
if (threadIds.size > 0) {
|
|
981
|
+
for (const threadId of threadIds) {
|
|
982
|
+
const threadKey = getKey(TABLE_THREADS, { id: threadId });
|
|
983
|
+
const thread = await this.client.get(threadKey);
|
|
984
|
+
if (thread) {
|
|
985
|
+
const updatedThread = {
|
|
986
|
+
...thread,
|
|
987
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
988
|
+
};
|
|
989
|
+
pipeline.set(threadKey, processRecord(TABLE_THREADS, updatedThread).processedRecord);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
await pipeline.exec();
|
|
994
|
+
} catch (error) {
|
|
995
|
+
throw new MastraError(
|
|
996
|
+
{
|
|
997
|
+
id: "STORAGE_UPSTASH_DELETE_MESSAGES_FAILED",
|
|
998
|
+
domain: ErrorDomain.STORAGE,
|
|
999
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1000
|
+
details: { messageIds: messageIds.join(", ") }
|
|
1001
|
+
},
|
|
1002
|
+
error
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
var StoreOperationsUpstash = class extends StoreOperations {
|
|
1008
|
+
client;
|
|
1009
|
+
constructor({ client }) {
|
|
1010
|
+
super();
|
|
1011
|
+
this.client = client;
|
|
1012
|
+
}
|
|
1013
|
+
async createTable({
|
|
1014
|
+
tableName: _tableName,
|
|
1015
|
+
schema: _schema
|
|
1016
|
+
}) {
|
|
1017
|
+
}
|
|
1018
|
+
async alterTable({
|
|
1019
|
+
tableName: _tableName,
|
|
1020
|
+
schema: _schema,
|
|
1021
|
+
ifNotExists: _ifNotExists
|
|
1022
|
+
}) {
|
|
1023
|
+
}
|
|
1024
|
+
async clearTable({ tableName }) {
|
|
1025
|
+
const pattern = `${tableName}:*`;
|
|
1026
|
+
try {
|
|
1027
|
+
await this.scanAndDelete(pattern);
|
|
1028
|
+
} catch (error) {
|
|
1029
|
+
throw new MastraError(
|
|
1030
|
+
{
|
|
1031
|
+
id: "STORAGE_UPSTASH_STORAGE_CLEAR_TABLE_FAILED",
|
|
1032
|
+
domain: ErrorDomain.STORAGE,
|
|
1033
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1034
|
+
details: {
|
|
1035
|
+
tableName
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
error
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
async dropTable({ tableName }) {
|
|
1043
|
+
return this.clearTable({ tableName });
|
|
1044
|
+
}
|
|
1045
|
+
async insert({ tableName, record }) {
|
|
1046
|
+
const { key, processedRecord } = processRecord(tableName, record);
|
|
1047
|
+
try {
|
|
1048
|
+
await this.client.set(key, processedRecord);
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
throw new MastraError(
|
|
1051
|
+
{
|
|
1052
|
+
id: "STORAGE_UPSTASH_STORAGE_INSERT_FAILED",
|
|
1053
|
+
domain: ErrorDomain.STORAGE,
|
|
1054
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1055
|
+
details: {
|
|
1056
|
+
tableName
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
error
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
async batchInsert(input) {
|
|
1064
|
+
const { tableName, records } = input;
|
|
1065
|
+
if (!records.length) return;
|
|
1066
|
+
const batchSize = 1e3;
|
|
1067
|
+
try {
|
|
1068
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
1069
|
+
const batch = records.slice(i, i + batchSize);
|
|
1070
|
+
const pipeline = this.client.pipeline();
|
|
1071
|
+
for (const record of batch) {
|
|
1072
|
+
const { key, processedRecord } = processRecord(tableName, record);
|
|
1073
|
+
pipeline.set(key, processedRecord);
|
|
1074
|
+
}
|
|
1075
|
+
await pipeline.exec();
|
|
1076
|
+
}
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
throw new MastraError(
|
|
1079
|
+
{
|
|
1080
|
+
id: "STORAGE_UPSTASH_STORAGE_BATCH_INSERT_FAILED",
|
|
1081
|
+
domain: ErrorDomain.STORAGE,
|
|
1082
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1083
|
+
details: {
|
|
1084
|
+
tableName
|
|
1085
|
+
}
|
|
1086
|
+
},
|
|
1087
|
+
error
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
async load({ tableName, keys }) {
|
|
1092
|
+
const key = getKey(tableName, keys);
|
|
1093
|
+
try {
|
|
1094
|
+
const data = await this.client.get(key);
|
|
1095
|
+
return data || null;
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
throw new MastraError(
|
|
1098
|
+
{
|
|
1099
|
+
id: "STORAGE_UPSTASH_STORAGE_LOAD_FAILED",
|
|
1100
|
+
domain: ErrorDomain.STORAGE,
|
|
1101
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1102
|
+
details: {
|
|
1103
|
+
tableName
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
error
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
async hasColumn(_tableName, _column) {
|
|
1111
|
+
return true;
|
|
1112
|
+
}
|
|
1113
|
+
async scanKeys(pattern, batchSize = 1e4) {
|
|
1114
|
+
let cursor = "0";
|
|
1115
|
+
let keys = [];
|
|
1116
|
+
do {
|
|
1117
|
+
const [nextCursor, batch] = await this.client.scan(cursor, {
|
|
1118
|
+
match: pattern,
|
|
1119
|
+
count: batchSize
|
|
1120
|
+
});
|
|
1121
|
+
keys.push(...batch);
|
|
1122
|
+
cursor = nextCursor;
|
|
1123
|
+
} while (cursor !== "0");
|
|
1124
|
+
return keys;
|
|
1125
|
+
}
|
|
1126
|
+
async scanAndDelete(pattern, batchSize = 1e4) {
|
|
1127
|
+
let cursor = "0";
|
|
1128
|
+
let totalDeleted = 0;
|
|
1129
|
+
do {
|
|
1130
|
+
const [nextCursor, keys] = await this.client.scan(cursor, {
|
|
1131
|
+
match: pattern,
|
|
1132
|
+
count: batchSize
|
|
1133
|
+
});
|
|
1134
|
+
if (keys.length > 0) {
|
|
1135
|
+
await this.client.del(...keys);
|
|
1136
|
+
totalDeleted += keys.length;
|
|
1137
|
+
}
|
|
1138
|
+
cursor = nextCursor;
|
|
1139
|
+
} while (cursor !== "0");
|
|
1140
|
+
return totalDeleted;
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
function transformScoreRow(row) {
|
|
1144
|
+
const parseField = (v) => {
|
|
1145
|
+
if (typeof v === "string") {
|
|
1146
|
+
try {
|
|
1147
|
+
return JSON.parse(v);
|
|
1148
|
+
} catch {
|
|
1149
|
+
return v;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return v;
|
|
1153
|
+
};
|
|
1154
|
+
return {
|
|
1155
|
+
...row,
|
|
1156
|
+
scorer: parseField(row.scorer),
|
|
1157
|
+
extractStepResult: parseField(row.extractStepResult),
|
|
1158
|
+
analyzeStepResult: parseField(row.analyzeStepResult),
|
|
1159
|
+
metadata: parseField(row.metadata),
|
|
1160
|
+
input: parseField(row.input),
|
|
1161
|
+
output: parseField(row.output),
|
|
1162
|
+
additionalContext: parseField(row.additionalContext),
|
|
1163
|
+
runtimeContext: parseField(row.runtimeContext),
|
|
1164
|
+
entity: parseField(row.entity),
|
|
1165
|
+
createdAt: row.createdAt,
|
|
1166
|
+
updatedAt: row.updatedAt
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
var ScoresUpstash = class extends ScoresStorage {
|
|
1170
|
+
client;
|
|
1171
|
+
operations;
|
|
1172
|
+
constructor({ client, operations }) {
|
|
1173
|
+
super();
|
|
1174
|
+
this.client = client;
|
|
1175
|
+
this.operations = operations;
|
|
1176
|
+
}
|
|
1177
|
+
async getScoreById({ id }) {
|
|
1178
|
+
try {
|
|
1179
|
+
const data = await this.operations.load({
|
|
1180
|
+
tableName: TABLE_SCORERS,
|
|
1181
|
+
keys: { id }
|
|
1182
|
+
});
|
|
1183
|
+
if (!data) return null;
|
|
1184
|
+
return transformScoreRow(data);
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
throw new MastraError(
|
|
1187
|
+
{
|
|
1188
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_SCORE_BY_ID_FAILED",
|
|
1189
|
+
domain: ErrorDomain.STORAGE,
|
|
1190
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1191
|
+
details: { id }
|
|
1192
|
+
},
|
|
1193
|
+
error
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
async getScoresByScorerId({
|
|
1198
|
+
scorerId,
|
|
1199
|
+
pagination = { page: 0, perPage: 20 }
|
|
1200
|
+
}) {
|
|
1201
|
+
const pattern = `${TABLE_SCORERS}:*`;
|
|
1202
|
+
const keys = await this.operations.scanKeys(pattern);
|
|
1203
|
+
if (keys.length === 0) {
|
|
1204
|
+
return {
|
|
1205
|
+
scores: [],
|
|
1206
|
+
pagination: { total: 0, page: pagination.page, perPage: pagination.perPage, hasMore: false }
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
const pipeline = this.client.pipeline();
|
|
1210
|
+
keys.forEach((key) => pipeline.get(key));
|
|
1211
|
+
const results = await pipeline.exec();
|
|
1212
|
+
const filtered = results.map((row) => row).filter((row) => !!row && typeof row === "object" && row.scorerId === scorerId);
|
|
1213
|
+
const total = filtered.length;
|
|
1214
|
+
const { page, perPage } = pagination;
|
|
1215
|
+
const start = page * perPage;
|
|
1216
|
+
const end = start + perPage;
|
|
1217
|
+
const paged = filtered.slice(start, end);
|
|
1218
|
+
const scores = paged.map((row) => transformScoreRow(row));
|
|
1219
|
+
return {
|
|
1220
|
+
scores,
|
|
1221
|
+
pagination: {
|
|
1222
|
+
total,
|
|
1223
|
+
page,
|
|
1224
|
+
perPage,
|
|
1225
|
+
hasMore: end < total
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
async saveScore(score) {
|
|
1230
|
+
const { key, processedRecord } = processRecord(TABLE_SCORERS, score);
|
|
1231
|
+
try {
|
|
1232
|
+
await this.client.set(key, processedRecord);
|
|
1233
|
+
return { score };
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
throw new MastraError(
|
|
1236
|
+
{
|
|
1237
|
+
id: "STORAGE_UPSTASH_STORAGE_SAVE_SCORE_FAILED",
|
|
1238
|
+
domain: ErrorDomain.STORAGE,
|
|
1239
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1240
|
+
details: { id: score.id }
|
|
1241
|
+
},
|
|
1242
|
+
error
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
async getScoresByRunId({
|
|
1247
|
+
runId,
|
|
1248
|
+
pagination = { page: 0, perPage: 20 }
|
|
1249
|
+
}) {
|
|
1250
|
+
const pattern = `${TABLE_SCORERS}:*`;
|
|
1251
|
+
const keys = await this.operations.scanKeys(pattern);
|
|
1252
|
+
if (keys.length === 0) {
|
|
1253
|
+
return {
|
|
1254
|
+
scores: [],
|
|
1255
|
+
pagination: { total: 0, page: pagination.page, perPage: pagination.perPage, hasMore: false }
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
const pipeline = this.client.pipeline();
|
|
1259
|
+
keys.forEach((key) => pipeline.get(key));
|
|
1260
|
+
const results = await pipeline.exec();
|
|
1261
|
+
const filtered = results.map((row) => row).filter((row) => !!row && typeof row === "object" && row.runId === runId);
|
|
1262
|
+
const total = filtered.length;
|
|
1263
|
+
const { page, perPage } = pagination;
|
|
1264
|
+
const start = page * perPage;
|
|
1265
|
+
const end = start + perPage;
|
|
1266
|
+
const paged = filtered.slice(start, end);
|
|
1267
|
+
const scores = paged.map((row) => transformScoreRow(row));
|
|
1268
|
+
return {
|
|
1269
|
+
scores,
|
|
1270
|
+
pagination: {
|
|
1271
|
+
total,
|
|
1272
|
+
page,
|
|
1273
|
+
perPage,
|
|
1274
|
+
hasMore: end < total
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
async getScoresByEntityId({
|
|
1279
|
+
entityId,
|
|
1280
|
+
entityType,
|
|
1281
|
+
pagination = { page: 0, perPage: 20 }
|
|
1282
|
+
}) {
|
|
1283
|
+
const pattern = `${TABLE_SCORERS}:*`;
|
|
1284
|
+
const keys = await this.operations.scanKeys(pattern);
|
|
1285
|
+
if (keys.length === 0) {
|
|
1286
|
+
return {
|
|
1287
|
+
scores: [],
|
|
1288
|
+
pagination: { total: 0, page: pagination.page, perPage: pagination.perPage, hasMore: false }
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
const pipeline = this.client.pipeline();
|
|
1292
|
+
keys.forEach((key) => pipeline.get(key));
|
|
1293
|
+
const results = await pipeline.exec();
|
|
1294
|
+
const filtered = results.map((row) => row).filter((row) => {
|
|
1295
|
+
if (!row || typeof row !== "object") return false;
|
|
1296
|
+
if (row.entityId !== entityId) return false;
|
|
1297
|
+
if (entityType && row.entityType !== entityType) return false;
|
|
1298
|
+
return true;
|
|
1299
|
+
});
|
|
1300
|
+
const total = filtered.length;
|
|
1301
|
+
const { page, perPage } = pagination;
|
|
1302
|
+
const start = page * perPage;
|
|
1303
|
+
const end = start + perPage;
|
|
1304
|
+
const paged = filtered.slice(start, end);
|
|
1305
|
+
const scores = paged.map((row) => transformScoreRow(row));
|
|
1306
|
+
return {
|
|
1307
|
+
scores,
|
|
1308
|
+
pagination: {
|
|
1309
|
+
total,
|
|
1310
|
+
page,
|
|
1311
|
+
perPage,
|
|
1312
|
+
hasMore: end < total
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
var TracesUpstash = class extends TracesStorage {
|
|
1318
|
+
client;
|
|
1319
|
+
operations;
|
|
1320
|
+
constructor({ client, operations }) {
|
|
1321
|
+
super();
|
|
1322
|
+
this.client = client;
|
|
1323
|
+
this.operations = operations;
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* @deprecated use getTracesPaginated instead
|
|
1327
|
+
*/
|
|
1328
|
+
async getTraces(args) {
|
|
1329
|
+
if (args.fromDate || args.toDate) {
|
|
1330
|
+
args.dateRange = {
|
|
1331
|
+
start: args.fromDate,
|
|
1332
|
+
end: args.toDate
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
try {
|
|
1336
|
+
const { traces } = await this.getTracesPaginated(args);
|
|
1337
|
+
return traces;
|
|
1338
|
+
} catch (error) {
|
|
1339
|
+
throw new MastraError(
|
|
1340
|
+
{
|
|
1341
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_TRACES_FAILED",
|
|
1342
|
+
domain: ErrorDomain.STORAGE,
|
|
1343
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1344
|
+
},
|
|
1345
|
+
error
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
async getTracesPaginated(args) {
|
|
1350
|
+
const { name, scope, page = 0, perPage = 100, attributes, filters, dateRange } = args;
|
|
1351
|
+
const fromDate = dateRange?.start;
|
|
1352
|
+
const toDate = dateRange?.end;
|
|
1353
|
+
try {
|
|
1354
|
+
const pattern = `${TABLE_TRACES}:*`;
|
|
1355
|
+
const keys = await this.operations.scanKeys(pattern);
|
|
1356
|
+
if (keys.length === 0) {
|
|
1357
|
+
return {
|
|
1358
|
+
traces: [],
|
|
1359
|
+
total: 0,
|
|
1360
|
+
page,
|
|
1361
|
+
perPage: perPage || 100,
|
|
1362
|
+
hasMore: false
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
const pipeline = this.client.pipeline();
|
|
1366
|
+
keys.forEach((key) => pipeline.get(key));
|
|
1367
|
+
const results = await pipeline.exec();
|
|
1368
|
+
let filteredTraces = results.filter(
|
|
1369
|
+
(record) => record !== null && typeof record === "object"
|
|
1370
|
+
);
|
|
1371
|
+
if (name) {
|
|
1372
|
+
filteredTraces = filteredTraces.filter((record) => record.name?.toLowerCase().startsWith(name.toLowerCase()));
|
|
1373
|
+
}
|
|
1374
|
+
if (scope) {
|
|
1375
|
+
filteredTraces = filteredTraces.filter((record) => record.scope === scope);
|
|
1376
|
+
}
|
|
1377
|
+
if (attributes) {
|
|
1378
|
+
filteredTraces = filteredTraces.filter((record) => {
|
|
1379
|
+
const recordAttributes = record.attributes;
|
|
1380
|
+
if (!recordAttributes) return false;
|
|
1381
|
+
const parsedAttributes = typeof recordAttributes === "string" ? JSON.parse(recordAttributes) : recordAttributes;
|
|
1382
|
+
return Object.entries(attributes).every(([key, value]) => parsedAttributes[key] === value);
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
if (filters) {
|
|
1386
|
+
filteredTraces = filteredTraces.filter(
|
|
1387
|
+
(record) => Object.entries(filters).every(([key, value]) => record[key] === value)
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
if (fromDate) {
|
|
1391
|
+
filteredTraces = filteredTraces.filter(
|
|
1392
|
+
(record) => new Date(record.createdAt).getTime() >= new Date(fromDate).getTime()
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1395
|
+
if (toDate) {
|
|
1396
|
+
filteredTraces = filteredTraces.filter(
|
|
1397
|
+
(record) => new Date(record.createdAt).getTime() <= new Date(toDate).getTime()
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
filteredTraces.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
1401
|
+
const transformedTraces = filteredTraces.map((record) => ({
|
|
1402
|
+
id: record.id,
|
|
1403
|
+
parentSpanId: record.parentSpanId,
|
|
1404
|
+
traceId: record.traceId,
|
|
1405
|
+
name: record.name,
|
|
1406
|
+
scope: record.scope,
|
|
1407
|
+
kind: record.kind,
|
|
1408
|
+
status: parseJSON(record.status),
|
|
1409
|
+
events: parseJSON(record.events),
|
|
1410
|
+
links: parseJSON(record.links),
|
|
1411
|
+
attributes: parseJSON(record.attributes),
|
|
1412
|
+
startTime: record.startTime,
|
|
1413
|
+
endTime: record.endTime,
|
|
1414
|
+
other: parseJSON(record.other),
|
|
1415
|
+
createdAt: ensureDate(record.createdAt)
|
|
1416
|
+
}));
|
|
1417
|
+
const total = transformedTraces.length;
|
|
1418
|
+
const resolvedPerPage = perPage || 100;
|
|
1419
|
+
const start = page * resolvedPerPage;
|
|
1420
|
+
const end = start + resolvedPerPage;
|
|
1421
|
+
const paginatedTraces = transformedTraces.slice(start, end);
|
|
1422
|
+
const hasMore = end < total;
|
|
1423
|
+
return {
|
|
1424
|
+
traces: paginatedTraces,
|
|
1425
|
+
total,
|
|
1426
|
+
page,
|
|
1427
|
+
perPage: resolvedPerPage,
|
|
1428
|
+
hasMore
|
|
1429
|
+
};
|
|
1430
|
+
} catch (error) {
|
|
1431
|
+
const mastraError = new MastraError(
|
|
1432
|
+
{
|
|
1433
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_TRACES_PAGINATED_FAILED",
|
|
1434
|
+
domain: ErrorDomain.STORAGE,
|
|
1435
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1436
|
+
details: {
|
|
1437
|
+
name: args.name || "",
|
|
1438
|
+
scope: args.scope || ""
|
|
1439
|
+
}
|
|
1440
|
+
},
|
|
1441
|
+
error
|
|
1442
|
+
);
|
|
1443
|
+
this.logger?.trackException(mastraError);
|
|
1444
|
+
this.logger.error(mastraError.toString());
|
|
1445
|
+
return {
|
|
1446
|
+
traces: [],
|
|
1447
|
+
total: 0,
|
|
1448
|
+
page,
|
|
1449
|
+
perPage: perPage || 100,
|
|
1450
|
+
hasMore: false
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
async batchTraceInsert(args) {
|
|
1455
|
+
return this.operations.batchInsert({
|
|
1456
|
+
tableName: TABLE_TRACES,
|
|
1457
|
+
records: args.records
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
function parseWorkflowRun(row) {
|
|
1462
|
+
let parsedSnapshot = row.snapshot;
|
|
1463
|
+
if (typeof parsedSnapshot === "string") {
|
|
1464
|
+
try {
|
|
1465
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
1466
|
+
} catch (e) {
|
|
1467
|
+
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return {
|
|
1471
|
+
workflowName: row.workflow_name,
|
|
1472
|
+
runId: row.run_id,
|
|
1473
|
+
snapshot: parsedSnapshot,
|
|
1474
|
+
createdAt: ensureDate(row.createdAt),
|
|
1475
|
+
updatedAt: ensureDate(row.updatedAt),
|
|
1476
|
+
resourceId: row.resourceId
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
var WorkflowsUpstash = class extends WorkflowsStorage {
|
|
1480
|
+
client;
|
|
1481
|
+
operations;
|
|
1482
|
+
constructor({ client, operations }) {
|
|
1483
|
+
super();
|
|
1484
|
+
this.client = client;
|
|
1485
|
+
this.operations = operations;
|
|
1486
|
+
}
|
|
1487
|
+
async persistWorkflowSnapshot(params) {
|
|
1488
|
+
const { namespace = "workflows", workflowName, runId, snapshot } = params;
|
|
1489
|
+
try {
|
|
1490
|
+
await this.operations.insert({
|
|
1491
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
1492
|
+
record: {
|
|
1493
|
+
namespace,
|
|
1494
|
+
workflow_name: workflowName,
|
|
1495
|
+
run_id: runId,
|
|
1496
|
+
snapshot,
|
|
1497
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1498
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
} catch (error) {
|
|
1502
|
+
throw new MastraError(
|
|
1503
|
+
{
|
|
1504
|
+
id: "STORAGE_UPSTASH_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
|
|
1505
|
+
domain: ErrorDomain.STORAGE,
|
|
1506
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1507
|
+
details: {
|
|
1508
|
+
namespace,
|
|
1509
|
+
workflowName,
|
|
1510
|
+
runId
|
|
1511
|
+
}
|
|
1512
|
+
},
|
|
1513
|
+
error
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
async loadWorkflowSnapshot(params) {
|
|
1518
|
+
const { namespace = "workflows", workflowName, runId } = params;
|
|
1519
|
+
const key = getKey(TABLE_WORKFLOW_SNAPSHOT, {
|
|
1520
|
+
namespace,
|
|
1521
|
+
workflow_name: workflowName,
|
|
1522
|
+
run_id: runId
|
|
1523
|
+
});
|
|
1524
|
+
try {
|
|
1525
|
+
const data = await this.client.get(key);
|
|
1526
|
+
if (!data) return null;
|
|
1527
|
+
return data.snapshot;
|
|
1528
|
+
} catch (error) {
|
|
1529
|
+
throw new MastraError(
|
|
1530
|
+
{
|
|
1531
|
+
id: "STORAGE_UPSTASH_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
|
|
1532
|
+
domain: ErrorDomain.STORAGE,
|
|
1533
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1534
|
+
details: {
|
|
1535
|
+
namespace,
|
|
1536
|
+
workflowName,
|
|
1537
|
+
runId
|
|
1538
|
+
}
|
|
1539
|
+
},
|
|
1540
|
+
error
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
async getWorkflowRunById({
|
|
1545
|
+
runId,
|
|
1546
|
+
workflowName
|
|
1547
|
+
}) {
|
|
1548
|
+
try {
|
|
1549
|
+
const key = getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows", workflow_name: workflowName, run_id: runId }) + "*";
|
|
1550
|
+
const keys = await this.operations.scanKeys(key);
|
|
1551
|
+
const workflows = await Promise.all(
|
|
1552
|
+
keys.map(async (key2) => {
|
|
1553
|
+
const data2 = await this.client.get(key2);
|
|
1554
|
+
return data2;
|
|
1555
|
+
})
|
|
1556
|
+
);
|
|
1557
|
+
const data = workflows.find((w) => w?.run_id === runId && w?.workflow_name === workflowName);
|
|
1558
|
+
if (!data) return null;
|
|
1559
|
+
return parseWorkflowRun(data);
|
|
770
1560
|
} catch (error) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
1561
|
+
throw new MastraError(
|
|
1562
|
+
{
|
|
1563
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUN_BY_ID_FAILED",
|
|
1564
|
+
domain: ErrorDomain.STORAGE,
|
|
1565
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1566
|
+
details: {
|
|
1567
|
+
namespace: "workflows",
|
|
1568
|
+
runId,
|
|
1569
|
+
workflowName: workflowName || ""
|
|
1570
|
+
}
|
|
1571
|
+
},
|
|
1572
|
+
error
|
|
1573
|
+
);
|
|
780
1574
|
}
|
|
781
1575
|
}
|
|
782
1576
|
async getWorkflowRuns({
|
|
783
|
-
namespace,
|
|
784
1577
|
workflowName,
|
|
785
1578
|
fromDate,
|
|
786
1579
|
toDate,
|
|
787
1580
|
limit,
|
|
788
1581
|
offset,
|
|
789
1582
|
resourceId
|
|
790
|
-
}
|
|
1583
|
+
}) {
|
|
791
1584
|
try {
|
|
792
|
-
let pattern =
|
|
1585
|
+
let pattern = getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows" }) + ":*";
|
|
793
1586
|
if (workflowName && resourceId) {
|
|
794
|
-
pattern =
|
|
795
|
-
namespace,
|
|
1587
|
+
pattern = getKey(TABLE_WORKFLOW_SNAPSHOT, {
|
|
1588
|
+
namespace: "workflows",
|
|
796
1589
|
workflow_name: workflowName,
|
|
797
1590
|
run_id: "*",
|
|
798
1591
|
resourceId
|
|
799
1592
|
});
|
|
800
1593
|
} else if (workflowName) {
|
|
801
|
-
pattern =
|
|
1594
|
+
pattern = getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace: "workflows", workflow_name: workflowName }) + ":*";
|
|
802
1595
|
} else if (resourceId) {
|
|
803
|
-
pattern =
|
|
1596
|
+
pattern = getKey(TABLE_WORKFLOW_SNAPSHOT, {
|
|
1597
|
+
namespace: "workflows",
|
|
1598
|
+
workflow_name: "*",
|
|
1599
|
+
run_id: "*",
|
|
1600
|
+
resourceId
|
|
1601
|
+
});
|
|
804
1602
|
}
|
|
805
|
-
const keys = await this.scanKeys(pattern);
|
|
1603
|
+
const keys = await this.operations.scanKeys(pattern);
|
|
806
1604
|
if (keys.length === 0) {
|
|
807
1605
|
return { runs: [], total: 0 };
|
|
808
1606
|
}
|
|
809
|
-
const pipeline = this.
|
|
1607
|
+
const pipeline = this.client.pipeline();
|
|
810
1608
|
keys.forEach((key) => pipeline.get(key));
|
|
811
1609
|
const results = await pipeline.exec();
|
|
812
1610
|
let runs = results.map((result) => result).filter(
|
|
813
1611
|
(record) => record !== null && record !== void 0 && typeof record === "object" && "workflow_name" in record
|
|
814
|
-
).filter((record) => !workflowName || record.workflow_name === workflowName).map((w) =>
|
|
1612
|
+
).filter((record) => !workflowName || record.workflow_name === workflowName).map((w) => parseWorkflowRun(w)).filter((w) => {
|
|
815
1613
|
if (fromDate && w.createdAt < fromDate) return false;
|
|
816
1614
|
if (toDate && w.createdAt > toDate) return false;
|
|
817
1615
|
return true;
|
|
@@ -822,106 +1620,222 @@ var UpstashStore = class extends MastraStorage {
|
|
|
822
1620
|
}
|
|
823
1621
|
return { runs, total };
|
|
824
1622
|
} catch (error) {
|
|
825
|
-
|
|
826
|
-
|
|
1623
|
+
throw new MastraError(
|
|
1624
|
+
{
|
|
1625
|
+
id: "STORAGE_UPSTASH_STORAGE_GET_WORKFLOW_RUNS_FAILED",
|
|
1626
|
+
domain: ErrorDomain.STORAGE,
|
|
1627
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1628
|
+
details: {
|
|
1629
|
+
namespace: "workflows",
|
|
1630
|
+
workflowName: workflowName || "",
|
|
1631
|
+
resourceId: resourceId || ""
|
|
1632
|
+
}
|
|
1633
|
+
},
|
|
1634
|
+
error
|
|
1635
|
+
);
|
|
827
1636
|
}
|
|
828
1637
|
}
|
|
1638
|
+
};
|
|
1639
|
+
|
|
1640
|
+
// src/storage/index.ts
|
|
1641
|
+
var UpstashStore = class extends MastraStorage {
|
|
1642
|
+
redis;
|
|
1643
|
+
stores;
|
|
1644
|
+
constructor(config) {
|
|
1645
|
+
super({ name: "Upstash" });
|
|
1646
|
+
this.redis = new Redis({
|
|
1647
|
+
url: config.url,
|
|
1648
|
+
token: config.token
|
|
1649
|
+
});
|
|
1650
|
+
const operations = new StoreOperationsUpstash({ client: this.redis });
|
|
1651
|
+
const traces = new TracesUpstash({ client: this.redis, operations });
|
|
1652
|
+
const scores = new ScoresUpstash({ client: this.redis, operations });
|
|
1653
|
+
const workflows = new WorkflowsUpstash({ client: this.redis, operations });
|
|
1654
|
+
const memory = new StoreMemoryUpstash({ client: this.redis, operations });
|
|
1655
|
+
const legacyEvals = new StoreLegacyEvalsUpstash({ client: this.redis, operations });
|
|
1656
|
+
this.stores = {
|
|
1657
|
+
operations,
|
|
1658
|
+
traces,
|
|
1659
|
+
scores,
|
|
1660
|
+
workflows,
|
|
1661
|
+
memory,
|
|
1662
|
+
legacyEvals
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
get supports() {
|
|
1666
|
+
return {
|
|
1667
|
+
selectByIncludeResourceScope: true,
|
|
1668
|
+
resourceWorkingMemory: true,
|
|
1669
|
+
hasColumn: false,
|
|
1670
|
+
createTable: false,
|
|
1671
|
+
deleteMessages: true
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* @deprecated Use getEvals instead
|
|
1676
|
+
*/
|
|
1677
|
+
async getEvalsByAgentName(agentName, type) {
|
|
1678
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
1679
|
+
}
|
|
1680
|
+
/**
|
|
1681
|
+
* Get all evaluations with pagination and total count
|
|
1682
|
+
* @param options Pagination and filtering options
|
|
1683
|
+
* @returns Object with evals array and total count
|
|
1684
|
+
*/
|
|
1685
|
+
async getEvals(options) {
|
|
1686
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* @deprecated use getTracesPaginated instead
|
|
1690
|
+
*/
|
|
1691
|
+
async getTraces(args) {
|
|
1692
|
+
return this.stores.traces.getTraces(args);
|
|
1693
|
+
}
|
|
1694
|
+
async getTracesPaginated(args) {
|
|
1695
|
+
return this.stores.traces.getTracesPaginated(args);
|
|
1696
|
+
}
|
|
1697
|
+
async batchTraceInsert(args) {
|
|
1698
|
+
return this.stores.traces.batchTraceInsert(args);
|
|
1699
|
+
}
|
|
1700
|
+
async createTable({
|
|
1701
|
+
tableName,
|
|
1702
|
+
schema
|
|
1703
|
+
}) {
|
|
1704
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
1705
|
+
}
|
|
1706
|
+
/**
|
|
1707
|
+
* No-op: This backend is schemaless and does not require schema changes.
|
|
1708
|
+
* @param tableName Name of the table
|
|
1709
|
+
* @param schema Schema of the table
|
|
1710
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
1711
|
+
*/
|
|
1712
|
+
async alterTable(args) {
|
|
1713
|
+
return this.stores.operations.alterTable(args);
|
|
1714
|
+
}
|
|
1715
|
+
async clearTable({ tableName }) {
|
|
1716
|
+
return this.stores.operations.clearTable({ tableName });
|
|
1717
|
+
}
|
|
1718
|
+
async dropTable({ tableName }) {
|
|
1719
|
+
return this.stores.operations.dropTable({ tableName });
|
|
1720
|
+
}
|
|
1721
|
+
async insert({ tableName, record }) {
|
|
1722
|
+
return this.stores.operations.insert({ tableName, record });
|
|
1723
|
+
}
|
|
1724
|
+
async batchInsert(input) {
|
|
1725
|
+
return this.stores.operations.batchInsert(input);
|
|
1726
|
+
}
|
|
1727
|
+
async load({ tableName, keys }) {
|
|
1728
|
+
return this.stores.operations.load({ tableName, keys });
|
|
1729
|
+
}
|
|
1730
|
+
async getThreadById({ threadId }) {
|
|
1731
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
1735
|
+
*/
|
|
1736
|
+
async getThreadsByResourceId({ resourceId }) {
|
|
1737
|
+
return this.stores.memory.getThreadsByResourceId({ resourceId });
|
|
1738
|
+
}
|
|
1739
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
1740
|
+
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
1741
|
+
}
|
|
1742
|
+
async saveThread({ thread }) {
|
|
1743
|
+
return this.stores.memory.saveThread({ thread });
|
|
1744
|
+
}
|
|
1745
|
+
async updateThread({
|
|
1746
|
+
id,
|
|
1747
|
+
title,
|
|
1748
|
+
metadata
|
|
1749
|
+
}) {
|
|
1750
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
1751
|
+
}
|
|
1752
|
+
async deleteThread({ threadId }) {
|
|
1753
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
1754
|
+
}
|
|
1755
|
+
async saveMessages(args) {
|
|
1756
|
+
return this.stores.memory.saveMessages(args);
|
|
1757
|
+
}
|
|
1758
|
+
async getMessages({
|
|
1759
|
+
threadId,
|
|
1760
|
+
selectBy,
|
|
1761
|
+
format
|
|
1762
|
+
}) {
|
|
1763
|
+
return this.stores.memory.getMessages({ threadId, selectBy, format });
|
|
1764
|
+
}
|
|
1765
|
+
async getMessagesPaginated(args) {
|
|
1766
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
1767
|
+
}
|
|
1768
|
+
async persistWorkflowSnapshot(params) {
|
|
1769
|
+
return this.stores.workflows.persistWorkflowSnapshot(params);
|
|
1770
|
+
}
|
|
1771
|
+
async loadWorkflowSnapshot(params) {
|
|
1772
|
+
return this.stores.workflows.loadWorkflowSnapshot(params);
|
|
1773
|
+
}
|
|
1774
|
+
async getWorkflowRuns({
|
|
1775
|
+
workflowName,
|
|
1776
|
+
fromDate,
|
|
1777
|
+
toDate,
|
|
1778
|
+
limit,
|
|
1779
|
+
offset,
|
|
1780
|
+
resourceId
|
|
1781
|
+
} = {}) {
|
|
1782
|
+
return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
|
|
1783
|
+
}
|
|
829
1784
|
async getWorkflowRunById({
|
|
830
|
-
namespace = "workflows",
|
|
831
1785
|
runId,
|
|
832
1786
|
workflowName
|
|
833
1787
|
}) {
|
|
834
|
-
|
|
835
|
-
const key = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace, workflow_name: workflowName, run_id: runId }) + "*";
|
|
836
|
-
const keys = await this.scanKeys(key);
|
|
837
|
-
const workflows = await Promise.all(
|
|
838
|
-
keys.map(async (key2) => {
|
|
839
|
-
const data2 = await this.redis.get(key2);
|
|
840
|
-
return data2;
|
|
841
|
-
})
|
|
842
|
-
);
|
|
843
|
-
const data = workflows.find((w) => w?.run_id === runId && w?.workflow_name === workflowName);
|
|
844
|
-
if (!data) return null;
|
|
845
|
-
return this.parseWorkflowRun(data);
|
|
846
|
-
} catch (error) {
|
|
847
|
-
console.error("Error getting workflow run by ID:", error);
|
|
848
|
-
throw error;
|
|
849
|
-
}
|
|
1788
|
+
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
850
1789
|
}
|
|
851
1790
|
async close() {
|
|
852
1791
|
}
|
|
853
|
-
async updateMessages(
|
|
854
|
-
this.
|
|
855
|
-
|
|
1792
|
+
async updateMessages(args) {
|
|
1793
|
+
return this.stores.memory.updateMessages(args);
|
|
1794
|
+
}
|
|
1795
|
+
async deleteMessages(messageIds) {
|
|
1796
|
+
return this.stores.memory.deleteMessages(messageIds);
|
|
856
1797
|
}
|
|
857
1798
|
async getResourceById({ resourceId }) {
|
|
858
|
-
|
|
859
|
-
const key = `${TABLE_RESOURCES}:${resourceId}`;
|
|
860
|
-
const data = await this.redis.get(key);
|
|
861
|
-
if (!data) {
|
|
862
|
-
return null;
|
|
863
|
-
}
|
|
864
|
-
return {
|
|
865
|
-
...data,
|
|
866
|
-
createdAt: new Date(data.createdAt),
|
|
867
|
-
updatedAt: new Date(data.updatedAt),
|
|
868
|
-
// Ensure workingMemory is always returned as a string, regardless of automatic parsing
|
|
869
|
-
workingMemory: typeof data.workingMemory === "object" ? JSON.stringify(data.workingMemory) : data.workingMemory,
|
|
870
|
-
metadata: typeof data.metadata === "string" ? JSON.parse(data.metadata) : data.metadata
|
|
871
|
-
};
|
|
872
|
-
} catch (error) {
|
|
873
|
-
this.logger.error("Error getting resource by ID:", error);
|
|
874
|
-
throw error;
|
|
875
|
-
}
|
|
1799
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
876
1800
|
}
|
|
877
1801
|
async saveResource({ resource }) {
|
|
878
|
-
|
|
879
|
-
const key = `${TABLE_RESOURCES}:${resource.id}`;
|
|
880
|
-
const serializedResource = {
|
|
881
|
-
...resource,
|
|
882
|
-
metadata: JSON.stringify(resource.metadata),
|
|
883
|
-
createdAt: resource.createdAt.toISOString(),
|
|
884
|
-
updatedAt: resource.updatedAt.toISOString()
|
|
885
|
-
};
|
|
886
|
-
await this.redis.set(key, serializedResource);
|
|
887
|
-
return resource;
|
|
888
|
-
} catch (error) {
|
|
889
|
-
this.logger.error("Error saving resource:", error);
|
|
890
|
-
throw error;
|
|
891
|
-
}
|
|
1802
|
+
return this.stores.memory.saveResource({ resource });
|
|
892
1803
|
}
|
|
893
1804
|
async updateResource({
|
|
894
1805
|
resourceId,
|
|
895
1806
|
workingMemory,
|
|
896
1807
|
metadata
|
|
897
1808
|
}) {
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1809
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
1810
|
+
}
|
|
1811
|
+
async getScoreById({ id: _id }) {
|
|
1812
|
+
return this.stores.scores.getScoreById({ id: _id });
|
|
1813
|
+
}
|
|
1814
|
+
async saveScore(score) {
|
|
1815
|
+
return this.stores.scores.saveScore(score);
|
|
1816
|
+
}
|
|
1817
|
+
async getScoresByRunId({
|
|
1818
|
+
runId,
|
|
1819
|
+
pagination
|
|
1820
|
+
}) {
|
|
1821
|
+
return this.stores.scores.getScoresByRunId({ runId, pagination });
|
|
1822
|
+
}
|
|
1823
|
+
async getScoresByEntityId({
|
|
1824
|
+
entityId,
|
|
1825
|
+
entityType,
|
|
1826
|
+
pagination
|
|
1827
|
+
}) {
|
|
1828
|
+
return this.stores.scores.getScoresByEntityId({
|
|
1829
|
+
entityId,
|
|
1830
|
+
entityType,
|
|
1831
|
+
pagination
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
async getScoresByScorerId({
|
|
1835
|
+
scorerId,
|
|
1836
|
+
pagination
|
|
1837
|
+
}) {
|
|
1838
|
+
return this.stores.scores.getScoresByScorerId({ scorerId, pagination });
|
|
925
1839
|
}
|
|
926
1840
|
};
|
|
927
1841
|
var UpstashFilterTranslator = class extends BaseFilterTranslator {
|
|
@@ -1137,21 +2051,40 @@ var UpstashVector = class extends MastraVector {
|
|
|
1137
2051
|
* @param {UpsertVectorParams} params - The parameters for the upsert operation.
|
|
1138
2052
|
* @returns {Promise<string[]>} A promise that resolves to the IDs of the upserted vectors.
|
|
1139
2053
|
*/
|
|
1140
|
-
async upsert({
|
|
2054
|
+
async upsert({
|
|
2055
|
+
indexName: namespace,
|
|
2056
|
+
vectors,
|
|
2057
|
+
metadata,
|
|
2058
|
+
ids,
|
|
2059
|
+
sparseVectors
|
|
2060
|
+
}) {
|
|
1141
2061
|
const generatedIds = ids || vectors.map(() => crypto.randomUUID());
|
|
1142
2062
|
const points = vectors.map((vector, index) => ({
|
|
1143
2063
|
id: generatedIds[index],
|
|
1144
2064
|
vector,
|
|
2065
|
+
...sparseVectors?.[index] && { sparseVector: sparseVectors[index] },
|
|
1145
2066
|
metadata: metadata?.[index]
|
|
1146
2067
|
}));
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
2068
|
+
try {
|
|
2069
|
+
await this.client.upsert(points, {
|
|
2070
|
+
namespace
|
|
2071
|
+
});
|
|
2072
|
+
return generatedIds;
|
|
2073
|
+
} catch (error) {
|
|
2074
|
+
throw new MastraError(
|
|
2075
|
+
{
|
|
2076
|
+
id: "STORAGE_UPSTASH_VECTOR_UPSERT_FAILED",
|
|
2077
|
+
domain: ErrorDomain.STORAGE,
|
|
2078
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2079
|
+
details: { namespace, vectorCount: vectors.length }
|
|
2080
|
+
},
|
|
2081
|
+
error
|
|
2082
|
+
);
|
|
2083
|
+
}
|
|
1151
2084
|
}
|
|
1152
2085
|
/**
|
|
1153
2086
|
* Transforms a Mastra vector filter into an Upstash-compatible filter string.
|
|
1154
|
-
* @param {
|
|
2087
|
+
* @param {UpstashVectorFilter} [filter] - The filter to transform.
|
|
1155
2088
|
* @returns {string | undefined} The transformed filter string, or undefined if no filter is provided.
|
|
1156
2089
|
*/
|
|
1157
2090
|
transformFilter(filter) {
|
|
@@ -1176,31 +2109,60 @@ var UpstashVector = class extends MastraVector {
|
|
|
1176
2109
|
queryVector,
|
|
1177
2110
|
topK = 10,
|
|
1178
2111
|
filter,
|
|
1179
|
-
includeVector = false
|
|
2112
|
+
includeVector = false,
|
|
2113
|
+
sparseVector,
|
|
2114
|
+
fusionAlgorithm,
|
|
2115
|
+
queryMode
|
|
1180
2116
|
}) {
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
2117
|
+
try {
|
|
2118
|
+
const ns = this.client.namespace(namespace);
|
|
2119
|
+
const filterString = this.transformFilter(filter);
|
|
2120
|
+
const results = await ns.query({
|
|
2121
|
+
topK,
|
|
2122
|
+
vector: queryVector,
|
|
2123
|
+
...sparseVector && { sparseVector },
|
|
2124
|
+
includeVectors: includeVector,
|
|
2125
|
+
includeMetadata: true,
|
|
2126
|
+
...filterString ? { filter: filterString } : {},
|
|
2127
|
+
...fusionAlgorithm && { fusionAlgorithm },
|
|
2128
|
+
...queryMode && { queryMode }
|
|
2129
|
+
});
|
|
2130
|
+
return (results || []).map((result) => ({
|
|
2131
|
+
id: `${result.id}`,
|
|
2132
|
+
score: result.score,
|
|
2133
|
+
metadata: result.metadata,
|
|
2134
|
+
...includeVector && { vector: result.vector || [] }
|
|
2135
|
+
}));
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
throw new MastraError(
|
|
2138
|
+
{
|
|
2139
|
+
id: "STORAGE_UPSTASH_VECTOR_QUERY_FAILED",
|
|
2140
|
+
domain: ErrorDomain.STORAGE,
|
|
2141
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2142
|
+
details: { namespace, topK }
|
|
2143
|
+
},
|
|
2144
|
+
error
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
1196
2147
|
}
|
|
1197
2148
|
/**
|
|
1198
2149
|
* Lists all namespaces in the Upstash vector index, which correspond to indexes.
|
|
1199
2150
|
* @returns {Promise<string[]>} A promise that resolves to a list of index names.
|
|
1200
2151
|
*/
|
|
1201
2152
|
async listIndexes() {
|
|
1202
|
-
|
|
1203
|
-
|
|
2153
|
+
try {
|
|
2154
|
+
const indexes = await this.client.listNamespaces();
|
|
2155
|
+
return indexes.filter(Boolean);
|
|
2156
|
+
} catch (error) {
|
|
2157
|
+
throw new MastraError(
|
|
2158
|
+
{
|
|
2159
|
+
id: "STORAGE_UPSTASH_VECTOR_LIST_INDEXES_FAILED",
|
|
2160
|
+
domain: ErrorDomain.STORAGE,
|
|
2161
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2162
|
+
},
|
|
2163
|
+
error
|
|
2164
|
+
);
|
|
2165
|
+
}
|
|
1204
2166
|
}
|
|
1205
2167
|
/**
|
|
1206
2168
|
* Retrieves statistics about a vector index.
|
|
@@ -1209,12 +2171,24 @@ var UpstashVector = class extends MastraVector {
|
|
|
1209
2171
|
* @returns A promise that resolves to the index statistics including dimension, count and metric
|
|
1210
2172
|
*/
|
|
1211
2173
|
async describeIndex({ indexName: namespace }) {
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
2174
|
+
try {
|
|
2175
|
+
const info = await this.client.info();
|
|
2176
|
+
return {
|
|
2177
|
+
dimension: info.dimension,
|
|
2178
|
+
count: info.namespaces?.[namespace]?.vectorCount || 0,
|
|
2179
|
+
metric: info?.similarityFunction?.toLowerCase()
|
|
2180
|
+
};
|
|
2181
|
+
} catch (error) {
|
|
2182
|
+
throw new MastraError(
|
|
2183
|
+
{
|
|
2184
|
+
id: "STORAGE_UPSTASH_VECTOR_DESCRIBE_INDEX_FAILED",
|
|
2185
|
+
domain: ErrorDomain.STORAGE,
|
|
2186
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2187
|
+
details: { namespace }
|
|
2188
|
+
},
|
|
2189
|
+
error
|
|
2190
|
+
);
|
|
2191
|
+
}
|
|
1218
2192
|
}
|
|
1219
2193
|
/**
|
|
1220
2194
|
* Deletes an index (namespace).
|
|
@@ -1225,7 +2199,15 @@ var UpstashVector = class extends MastraVector {
|
|
|
1225
2199
|
try {
|
|
1226
2200
|
await this.client.deleteNamespace(namespace);
|
|
1227
2201
|
} catch (error) {
|
|
1228
|
-
|
|
2202
|
+
throw new MastraError(
|
|
2203
|
+
{
|
|
2204
|
+
id: "STORAGE_UPSTASH_VECTOR_DELETE_INDEX_FAILED",
|
|
2205
|
+
domain: ErrorDomain.STORAGE,
|
|
2206
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2207
|
+
details: { namespace }
|
|
2208
|
+
},
|
|
2209
|
+
error
|
|
2210
|
+
);
|
|
1229
2211
|
}
|
|
1230
2212
|
}
|
|
1231
2213
|
/**
|
|
@@ -1239,30 +2221,40 @@ var UpstashVector = class extends MastraVector {
|
|
|
1239
2221
|
* @throws Will throw an error if no updates are provided or if the update operation fails.
|
|
1240
2222
|
*/
|
|
1241
2223
|
async updateVector({ indexName: namespace, id, update }) {
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
const updatePayload = { id };
|
|
1250
|
-
if (update.vector) {
|
|
1251
|
-
updatePayload.vector = update.vector;
|
|
1252
|
-
}
|
|
1253
|
-
if (update.metadata) {
|
|
1254
|
-
updatePayload.metadata = update.metadata;
|
|
1255
|
-
}
|
|
1256
|
-
const points = {
|
|
1257
|
-
id: updatePayload.id,
|
|
1258
|
-
vector: updatePayload.vector,
|
|
1259
|
-
metadata: updatePayload.metadata
|
|
1260
|
-
};
|
|
1261
|
-
await this.client.upsert(points, {
|
|
1262
|
-
namespace
|
|
2224
|
+
if (!update.vector && !update.metadata && !update.sparseVector) {
|
|
2225
|
+
throw new MastraError({
|
|
2226
|
+
id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
|
|
2227
|
+
domain: ErrorDomain.STORAGE,
|
|
2228
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2229
|
+
details: { namespace, id },
|
|
2230
|
+
text: "No update data provided"
|
|
1263
2231
|
});
|
|
2232
|
+
}
|
|
2233
|
+
if (!update.vector && !update.sparseVector && update.metadata) {
|
|
2234
|
+
throw new MastraError({
|
|
2235
|
+
id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
|
|
2236
|
+
domain: ErrorDomain.STORAGE,
|
|
2237
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2238
|
+
details: { namespace, id },
|
|
2239
|
+
text: "Both vector and metadata must be provided for an update"
|
|
2240
|
+
});
|
|
2241
|
+
}
|
|
2242
|
+
try {
|
|
2243
|
+
const points = { id };
|
|
2244
|
+
if (update.vector) points.vector = update.vector;
|
|
2245
|
+
if (update.metadata) points.metadata = update.metadata;
|
|
2246
|
+
if (update.sparseVector) points.sparseVector = update.sparseVector;
|
|
2247
|
+
await this.client.upsert(points, { namespace });
|
|
1264
2248
|
} catch (error) {
|
|
1265
|
-
throw new
|
|
2249
|
+
throw new MastraError(
|
|
2250
|
+
{
|
|
2251
|
+
id: "STORAGE_UPSTASH_VECTOR_UPDATE_VECTOR_FAILED",
|
|
2252
|
+
domain: ErrorDomain.STORAGE,
|
|
2253
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2254
|
+
details: { namespace, id }
|
|
2255
|
+
},
|
|
2256
|
+
error
|
|
2257
|
+
);
|
|
1266
2258
|
}
|
|
1267
2259
|
}
|
|
1268
2260
|
/**
|
|
@@ -1278,7 +2270,16 @@ var UpstashVector = class extends MastraVector {
|
|
|
1278
2270
|
namespace
|
|
1279
2271
|
});
|
|
1280
2272
|
} catch (error) {
|
|
1281
|
-
|
|
2273
|
+
const mastraError = new MastraError(
|
|
2274
|
+
{
|
|
2275
|
+
id: "STORAGE_UPSTASH_VECTOR_DELETE_VECTOR_FAILED",
|
|
2276
|
+
domain: ErrorDomain.STORAGE,
|
|
2277
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2278
|
+
details: { namespace, id }
|
|
2279
|
+
},
|
|
2280
|
+
error
|
|
2281
|
+
);
|
|
2282
|
+
this.logger?.error(mastraError.toString());
|
|
1282
2283
|
}
|
|
1283
2284
|
}
|
|
1284
2285
|
};
|
|
@@ -1359,3 +2360,5 @@ Example Complex Query:
|
|
|
1359
2360
|
}`;
|
|
1360
2361
|
|
|
1361
2362
|
export { UPSTASH_PROMPT, UpstashStore, UpstashVector };
|
|
2363
|
+
//# sourceMappingURL=index.js.map
|
|
2364
|
+
//# sourceMappingURL=index.js.map
|