@mastra/cloudflare 0.11.1 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_tsup-dts-rollup.d.cts +353 -93
- package/dist/_tsup-dts-rollup.d.ts +353 -93
- package/dist/index.cjs +1851 -982
- package/dist/index.js +1850 -981
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,194 +1,993 @@
|
|
|
1
|
-
import { MessageList } from '@mastra/core/agent';
|
|
2
1
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
3
|
-
import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_EVALS, TABLE_TRACES } from '@mastra/core/storage';
|
|
2
|
+
import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_EVALS, TABLE_SCORERS, TABLE_TRACES, StoreOperations, serializeDate, ensureDate, LegacyEvalsStorage, WorkflowsStorage, TracesStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, ScoresStorage } from '@mastra/core/storage';
|
|
4
3
|
import Cloudflare from 'cloudflare';
|
|
4
|
+
import { MessageList } from '@mastra/core/agent';
|
|
5
5
|
|
|
6
6
|
// src/storage/index.ts
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
7
|
+
var LegacyEvalsStorageCloudflare = class extends LegacyEvalsStorage {
|
|
8
|
+
operations;
|
|
9
|
+
constructor({ operations }) {
|
|
10
|
+
super();
|
|
11
|
+
this.operations = operations;
|
|
12
|
+
}
|
|
13
|
+
async getEvalsByAgentName(agentName, type) {
|
|
14
|
+
try {
|
|
15
|
+
const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
|
|
16
|
+
const keyObjs = await this.operations.listKV(TABLE_EVALS, { prefix: `${prefix}${TABLE_EVALS}` });
|
|
17
|
+
const evals = [];
|
|
18
|
+
for (const { name: key } of keyObjs) {
|
|
19
|
+
const data = await this.operations.getKV(TABLE_EVALS, key);
|
|
20
|
+
if (!data) continue;
|
|
21
|
+
if (data.agent_name !== agentName) continue;
|
|
22
|
+
if (type) {
|
|
23
|
+
const isTest = data.test_info !== null && data.test_info !== void 0;
|
|
24
|
+
const evalType = isTest ? "test" : "live";
|
|
25
|
+
if (evalType !== type) continue;
|
|
26
|
+
}
|
|
27
|
+
const mappedData = {
|
|
28
|
+
...data,
|
|
29
|
+
runId: data.run_id,
|
|
30
|
+
testInfo: data.test_info
|
|
31
|
+
};
|
|
32
|
+
evals.push(mappedData);
|
|
33
|
+
}
|
|
34
|
+
evals.sort((a, b) => {
|
|
35
|
+
const aTime = new Date(a.createdAt || 0).getTime();
|
|
36
|
+
const bTime = new Date(b.createdAt || 0).getTime();
|
|
37
|
+
return bTime - aTime;
|
|
38
|
+
});
|
|
39
|
+
return evals;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
throw new MastraError(
|
|
42
|
+
{
|
|
43
|
+
id: "CLOUDFLARE_STORAGE_GET_EVALS_BY_AGENT_NAME_FAILED",
|
|
44
|
+
domain: ErrorDomain.STORAGE,
|
|
45
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
46
|
+
text: "Failed to get evals by agent name"
|
|
47
|
+
},
|
|
48
|
+
error
|
|
49
|
+
);
|
|
22
50
|
}
|
|
23
|
-
|
|
24
|
-
|
|
51
|
+
}
|
|
52
|
+
async getEvals(options) {
|
|
53
|
+
try {
|
|
54
|
+
const { agentName, type, page = 0, perPage = 100, dateRange } = options;
|
|
55
|
+
const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
|
|
56
|
+
const keyObjs = await this.operations.listKV(TABLE_EVALS, { prefix: `${prefix}${TABLE_EVALS}` });
|
|
57
|
+
const evals = [];
|
|
58
|
+
for (const { name: key } of keyObjs) {
|
|
59
|
+
const data = await this.operations.getKV(TABLE_EVALS, key);
|
|
60
|
+
if (!data) continue;
|
|
61
|
+
if (agentName && data.agent_name !== agentName) continue;
|
|
62
|
+
if (type) {
|
|
63
|
+
const isTest = data.test_info !== null && data.test_info !== void 0;
|
|
64
|
+
const evalType = isTest ? "test" : "live";
|
|
65
|
+
if (evalType !== type) continue;
|
|
66
|
+
}
|
|
67
|
+
if (dateRange?.start || dateRange?.end) {
|
|
68
|
+
const evalDate = new Date(data.createdAt || data.created_at || 0);
|
|
69
|
+
if (dateRange.start && evalDate < dateRange.start) continue;
|
|
70
|
+
if (dateRange.end && evalDate > dateRange.end) continue;
|
|
71
|
+
}
|
|
72
|
+
const mappedData = {
|
|
73
|
+
...data,
|
|
74
|
+
runId: data.run_id,
|
|
75
|
+
testInfo: data.test_info
|
|
76
|
+
};
|
|
77
|
+
evals.push(mappedData);
|
|
78
|
+
}
|
|
79
|
+
evals.sort((a, b) => {
|
|
80
|
+
const aTime = new Date(a.createdAt || 0).getTime();
|
|
81
|
+
const bTime = new Date(b.createdAt || 0).getTime();
|
|
82
|
+
return bTime - aTime;
|
|
83
|
+
});
|
|
84
|
+
const start = page * perPage;
|
|
85
|
+
const end = start + perPage;
|
|
86
|
+
const paginatedEvals = evals.slice(start, end);
|
|
87
|
+
return {
|
|
88
|
+
page,
|
|
89
|
+
perPage,
|
|
90
|
+
total: evals.length,
|
|
91
|
+
hasMore: start + perPage < evals.length,
|
|
92
|
+
evals: paginatedEvals
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
throw new MastraError(
|
|
96
|
+
{
|
|
97
|
+
id: "CLOUDFLARE_STORAGE_GET_EVALS_FAILED",
|
|
98
|
+
domain: ErrorDomain.STORAGE,
|
|
99
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
100
|
+
text: "Failed to get evals"
|
|
101
|
+
},
|
|
102
|
+
error
|
|
103
|
+
);
|
|
25
104
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var MemoryStorageCloudflare = class extends MemoryStorage {
|
|
108
|
+
operations;
|
|
109
|
+
constructor({ operations }) {
|
|
110
|
+
super();
|
|
111
|
+
this.operations = operations;
|
|
112
|
+
}
|
|
113
|
+
ensureMetadata(metadata) {
|
|
114
|
+
if (!metadata) return void 0;
|
|
115
|
+
return typeof metadata === "string" ? JSON.parse(metadata) : metadata;
|
|
116
|
+
}
|
|
117
|
+
async getThreadById({ threadId }) {
|
|
118
|
+
const thread = await this.operations.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
|
|
119
|
+
if (!thread) return null;
|
|
120
|
+
try {
|
|
121
|
+
return {
|
|
122
|
+
...thread,
|
|
123
|
+
createdAt: ensureDate(thread.createdAt),
|
|
124
|
+
updatedAt: ensureDate(thread.updatedAt),
|
|
125
|
+
metadata: this.ensureMetadata(thread.metadata)
|
|
126
|
+
};
|
|
127
|
+
} catch (error) {
|
|
128
|
+
const mastraError = new MastraError(
|
|
129
|
+
{
|
|
130
|
+
id: "CLOUDFLARE_STORAGE_GET_THREAD_BY_ID_FAILED",
|
|
131
|
+
domain: ErrorDomain.STORAGE,
|
|
132
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
133
|
+
details: {
|
|
134
|
+
threadId
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
error
|
|
138
|
+
);
|
|
139
|
+
this.logger?.trackException(mastraError);
|
|
140
|
+
this.logger?.error(mastraError.toString());
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async getThreadsByResourceId({ resourceId }) {
|
|
145
|
+
try {
|
|
146
|
+
const keyList = await this.operations.listKV(TABLE_THREADS);
|
|
147
|
+
const threads = await Promise.all(
|
|
148
|
+
keyList.map(async (keyObj) => {
|
|
149
|
+
try {
|
|
150
|
+
const data = await this.operations.getKV(TABLE_THREADS, keyObj.name);
|
|
151
|
+
if (!data) return null;
|
|
152
|
+
const thread = typeof data === "string" ? JSON.parse(data) : data;
|
|
153
|
+
if (!thread || !thread.resourceId || thread.resourceId !== resourceId) return null;
|
|
154
|
+
return {
|
|
155
|
+
...thread,
|
|
156
|
+
createdAt: ensureDate(thread.createdAt),
|
|
157
|
+
updatedAt: ensureDate(thread.updatedAt),
|
|
158
|
+
metadata: this.ensureMetadata(thread.metadata)
|
|
159
|
+
};
|
|
160
|
+
} catch (error) {
|
|
161
|
+
const mastraError = new MastraError(
|
|
162
|
+
{
|
|
163
|
+
id: "CLOUDFLARE_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED",
|
|
164
|
+
domain: ErrorDomain.STORAGE,
|
|
165
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
166
|
+
details: {
|
|
167
|
+
resourceId
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
error
|
|
171
|
+
);
|
|
172
|
+
this.logger?.trackException(mastraError);
|
|
173
|
+
this.logger?.error(mastraError.toString());
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
);
|
|
178
|
+
return threads.filter((thread) => thread !== null);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
const mastraError = new MastraError(
|
|
181
|
+
{
|
|
182
|
+
id: "CLOUDFLARE_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED",
|
|
183
|
+
domain: ErrorDomain.STORAGE,
|
|
184
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
185
|
+
details: {
|
|
186
|
+
resourceId
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
error
|
|
190
|
+
);
|
|
191
|
+
this.logger?.trackException(mastraError);
|
|
192
|
+
this.logger?.error(mastraError.toString());
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
197
|
+
try {
|
|
198
|
+
const { resourceId, page = 0, perPage = 100 } = args;
|
|
199
|
+
const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
|
|
200
|
+
const keyObjs = await this.operations.listKV(TABLE_THREADS, { prefix: `${prefix}${TABLE_THREADS}` });
|
|
201
|
+
const threads = [];
|
|
202
|
+
for (const { name: key } of keyObjs) {
|
|
203
|
+
const data = await this.operations.getKV(TABLE_THREADS, key);
|
|
204
|
+
if (!data) continue;
|
|
205
|
+
if (data.resourceId !== resourceId) continue;
|
|
206
|
+
threads.push(data);
|
|
30
207
|
}
|
|
208
|
+
threads.sort((a, b) => {
|
|
209
|
+
const aTime = new Date(a.createdAt || 0).getTime();
|
|
210
|
+
const bTime = new Date(b.createdAt || 0).getTime();
|
|
211
|
+
return bTime - aTime;
|
|
212
|
+
});
|
|
213
|
+
const start = page * perPage;
|
|
214
|
+
const end = start + perPage;
|
|
215
|
+
const paginatedThreads = threads.slice(start, end);
|
|
216
|
+
return {
|
|
217
|
+
page,
|
|
218
|
+
perPage,
|
|
219
|
+
total: threads.length,
|
|
220
|
+
hasMore: start + perPage < threads.length,
|
|
221
|
+
threads: paginatedThreads
|
|
222
|
+
};
|
|
223
|
+
} catch (error) {
|
|
224
|
+
throw new MastraError(
|
|
225
|
+
{
|
|
226
|
+
id: "CLOUDFLARE_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
|
|
227
|
+
domain: ErrorDomain.STORAGE,
|
|
228
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
229
|
+
text: "Failed to get threads by resource ID with pagination"
|
|
230
|
+
},
|
|
231
|
+
error
|
|
232
|
+
);
|
|
31
233
|
}
|
|
32
234
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
235
|
+
async saveThread({ thread }) {
|
|
236
|
+
try {
|
|
237
|
+
await this.operations.insert({ tableName: TABLE_THREADS, record: thread });
|
|
238
|
+
return thread;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
throw new MastraError(
|
|
241
|
+
{
|
|
242
|
+
id: "CLOUDFLARE_STORAGE_SAVE_THREAD_FAILED",
|
|
243
|
+
domain: ErrorDomain.STORAGE,
|
|
244
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
245
|
+
details: {
|
|
246
|
+
threadId: thread.id
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
error
|
|
250
|
+
);
|
|
36
251
|
}
|
|
37
|
-
|
|
38
|
-
|
|
252
|
+
}
|
|
253
|
+
async updateThread({
|
|
254
|
+
id,
|
|
255
|
+
title,
|
|
256
|
+
metadata
|
|
257
|
+
}) {
|
|
258
|
+
try {
|
|
259
|
+
const thread = await this.getThreadById({ threadId: id });
|
|
260
|
+
if (!thread) {
|
|
261
|
+
throw new Error(`Thread ${id} not found`);
|
|
262
|
+
}
|
|
263
|
+
const updatedThread = {
|
|
264
|
+
...thread,
|
|
265
|
+
title,
|
|
266
|
+
metadata: this.ensureMetadata({
|
|
267
|
+
...thread.metadata ?? {},
|
|
268
|
+
...metadata
|
|
269
|
+
}),
|
|
270
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
271
|
+
};
|
|
272
|
+
await this.operations.insert({ tableName: TABLE_THREADS, record: updatedThread });
|
|
273
|
+
return updatedThread;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
throw new MastraError(
|
|
276
|
+
{
|
|
277
|
+
id: "CLOUDFLARE_STORAGE_UPDATE_THREAD_FAILED",
|
|
278
|
+
domain: ErrorDomain.STORAGE,
|
|
279
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
280
|
+
details: {
|
|
281
|
+
threadId: id,
|
|
282
|
+
title
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
error
|
|
286
|
+
);
|
|
39
287
|
}
|
|
40
|
-
|
|
41
|
-
|
|
288
|
+
}
|
|
289
|
+
getMessageKey(threadId, messageId) {
|
|
290
|
+
try {
|
|
291
|
+
return this.operations.getKey(TABLE_MESSAGES, { threadId, id: messageId });
|
|
292
|
+
} catch (error) {
|
|
293
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
294
|
+
this.logger.error(`Error getting message key for thread ${threadId} and message ${messageId}:`, { message });
|
|
295
|
+
throw error;
|
|
42
296
|
}
|
|
43
297
|
}
|
|
44
|
-
|
|
45
|
-
super({ name: "Cloudflare" });
|
|
298
|
+
getThreadMessagesKey(threadId) {
|
|
46
299
|
try {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
});
|
|
59
|
-
|
|
300
|
+
return this.operations.getKey(TABLE_MESSAGES, { threadId, id: "messages" });
|
|
301
|
+
} catch (error) {
|
|
302
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
303
|
+
this.logger.error(`Error getting thread messages key for thread ${threadId}:`, { message });
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async deleteThread({ threadId }) {
|
|
308
|
+
try {
|
|
309
|
+
const thread = await this.getThreadById({ threadId });
|
|
310
|
+
if (!thread) {
|
|
311
|
+
throw new Error(`Thread ${threadId} not found`);
|
|
312
|
+
}
|
|
313
|
+
const messageKeys = await this.operations.listKV(TABLE_MESSAGES);
|
|
314
|
+
const threadMessageKeys = messageKeys.filter((key) => key.name.includes(`${TABLE_MESSAGES}:${threadId}:`));
|
|
315
|
+
await Promise.all([
|
|
316
|
+
// Delete message order
|
|
317
|
+
this.operations.deleteKV(TABLE_MESSAGES, this.getThreadMessagesKey(threadId)),
|
|
318
|
+
// Delete all messages
|
|
319
|
+
...threadMessageKeys.map((key) => this.operations.deleteKV(TABLE_MESSAGES, key.name)),
|
|
320
|
+
// Delete thread
|
|
321
|
+
this.operations.deleteKV(TABLE_THREADS, this.operations.getKey(TABLE_THREADS, { id: threadId }))
|
|
322
|
+
]);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
throw new MastraError(
|
|
325
|
+
{
|
|
326
|
+
id: "CLOUDFLARE_STORAGE_DELETE_THREAD_FAILED",
|
|
327
|
+
domain: ErrorDomain.STORAGE,
|
|
328
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
329
|
+
details: {
|
|
330
|
+
threadId
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
error
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async findMessageInAnyThread(messageId) {
|
|
338
|
+
try {
|
|
339
|
+
const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
|
|
340
|
+
const threadKeys = await this.operations.listKV(TABLE_THREADS, { prefix: `${prefix}${TABLE_THREADS}` });
|
|
341
|
+
for (const { name: threadKey } of threadKeys) {
|
|
342
|
+
const threadId = threadKey.split(":").pop();
|
|
343
|
+
if (!threadId || threadId === "messages") continue;
|
|
344
|
+
const messageKey = this.getMessageKey(threadId, messageId);
|
|
345
|
+
const message = await this.operations.getKV(TABLE_MESSAGES, messageKey);
|
|
346
|
+
if (message) {
|
|
347
|
+
return { ...message, threadId };
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return null;
|
|
351
|
+
} catch (error) {
|
|
352
|
+
this.logger?.error(`Error finding message ${messageId} in any thread:`, error);
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Queue for serializing sorted order updates.
|
|
358
|
+
* Updates the sorted order for a given key. This operation is eventually consistent.
|
|
359
|
+
*/
|
|
360
|
+
updateQueue = /* @__PURE__ */ new Map();
|
|
361
|
+
async updateSorting(threadMessages) {
|
|
362
|
+
return threadMessages.map((msg) => ({
|
|
363
|
+
message: msg,
|
|
364
|
+
// Use _index if available, otherwise timestamp, matching Upstash
|
|
365
|
+
score: msg._index !== void 0 ? msg._index : msg.createdAt.getTime()
|
|
366
|
+
})).sort((a, b) => a.score - b.score).map((item) => ({
|
|
367
|
+
id: item.message.id,
|
|
368
|
+
score: item.score
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Updates the sorted order for a given key. This operation is eventually consistent.
|
|
373
|
+
* Note: Operations on the same orderKey are serialized using a queue to prevent
|
|
374
|
+
* concurrent updates from conflicting with each other.
|
|
375
|
+
*/
|
|
376
|
+
async updateSortedMessages(orderKey, newEntries) {
|
|
377
|
+
const currentPromise = this.updateQueue.get(orderKey) || Promise.resolve();
|
|
378
|
+
const nextPromise = currentPromise.then(async () => {
|
|
379
|
+
try {
|
|
380
|
+
const currentOrder = await this.getSortedMessages(orderKey);
|
|
381
|
+
const orderMap = new Map(currentOrder.map((entry) => [entry.id, entry]));
|
|
382
|
+
for (const entry of newEntries) {
|
|
383
|
+
orderMap.set(entry.id, entry);
|
|
384
|
+
}
|
|
385
|
+
const updatedOrder = Array.from(orderMap.values()).sort((a, b) => a.score - b.score);
|
|
386
|
+
await this.operations.putKV({
|
|
387
|
+
tableName: TABLE_MESSAGES,
|
|
388
|
+
key: orderKey,
|
|
389
|
+
value: JSON.stringify(updatedOrder)
|
|
390
|
+
});
|
|
391
|
+
} catch (error) {
|
|
392
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
393
|
+
this.logger.error(`Error updating sorted order for key ${orderKey}:`, { message });
|
|
394
|
+
throw error;
|
|
395
|
+
} finally {
|
|
396
|
+
if (this.updateQueue.get(orderKey) === nextPromise) {
|
|
397
|
+
this.updateQueue.delete(orderKey);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
this.updateQueue.set(orderKey, nextPromise);
|
|
402
|
+
return nextPromise;
|
|
403
|
+
}
|
|
404
|
+
async getSortedMessages(orderKey) {
|
|
405
|
+
const raw = await this.operations.getKV(TABLE_MESSAGES, orderKey);
|
|
406
|
+
if (!raw) return [];
|
|
407
|
+
try {
|
|
408
|
+
const arr = JSON.parse(typeof raw === "string" ? raw : JSON.stringify(raw));
|
|
409
|
+
return Array.isArray(arr) ? arr : [];
|
|
410
|
+
} catch (e) {
|
|
411
|
+
this.logger.error(`Error parsing order data for key ${orderKey}:`, { e });
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
async migrateMessage(messageId, fromThreadId, toThreadId) {
|
|
416
|
+
try {
|
|
417
|
+
const oldMessageKey = this.getMessageKey(fromThreadId, messageId);
|
|
418
|
+
const message = await this.operations.getKV(TABLE_MESSAGES, oldMessageKey);
|
|
419
|
+
if (!message) return;
|
|
420
|
+
const updatedMessage = {
|
|
421
|
+
...message,
|
|
422
|
+
threadId: toThreadId
|
|
423
|
+
};
|
|
424
|
+
const newMessageKey = this.getMessageKey(toThreadId, messageId);
|
|
425
|
+
await this.operations.putKV({ tableName: TABLE_MESSAGES, key: newMessageKey, value: updatedMessage });
|
|
426
|
+
const oldOrderKey = this.getThreadMessagesKey(fromThreadId);
|
|
427
|
+
const oldEntries = await this.getSortedMessages(oldOrderKey);
|
|
428
|
+
const filteredEntries = oldEntries.filter((entry) => entry.id !== messageId);
|
|
429
|
+
await this.updateSortedMessages(oldOrderKey, filteredEntries);
|
|
430
|
+
const newOrderKey = this.getThreadMessagesKey(toThreadId);
|
|
431
|
+
const newEntries = await this.getSortedMessages(newOrderKey);
|
|
432
|
+
const newEntry = { id: messageId, score: Date.now() };
|
|
433
|
+
newEntries.push(newEntry);
|
|
434
|
+
await this.updateSortedMessages(newOrderKey, newEntries);
|
|
435
|
+
await this.operations.deleteKV(TABLE_MESSAGES, oldMessageKey);
|
|
436
|
+
} catch (error) {
|
|
437
|
+
this.logger?.error(`Error migrating message ${messageId} from ${fromThreadId} to ${toThreadId}:`, error);
|
|
438
|
+
throw error;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
async saveMessages(args) {
|
|
442
|
+
const { messages, format = "v1" } = args;
|
|
443
|
+
if (!Array.isArray(messages) || messages.length === 0) return [];
|
|
444
|
+
try {
|
|
445
|
+
const validatedMessages = messages.map((message, index) => {
|
|
446
|
+
const errors = [];
|
|
447
|
+
if (!message.id) errors.push("id is required");
|
|
448
|
+
if (!message.threadId) errors.push("threadId is required");
|
|
449
|
+
if (!message.content) errors.push("content is required");
|
|
450
|
+
if (!message.role) errors.push("role is required");
|
|
451
|
+
if (!message.createdAt) errors.push("createdAt is required");
|
|
452
|
+
if (message.resourceId === null || message.resourceId === void 0) errors.push("resourceId is required");
|
|
453
|
+
if (errors.length > 0) {
|
|
454
|
+
throw new Error(`Invalid message at index ${index}: ${errors.join(", ")}`);
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
...message,
|
|
458
|
+
createdAt: ensureDate(message.createdAt),
|
|
459
|
+
type: message.type || "v2",
|
|
460
|
+
_index: index
|
|
461
|
+
};
|
|
462
|
+
}).filter((m) => !!m);
|
|
463
|
+
const messageMigrationTasks = [];
|
|
464
|
+
for (const message of validatedMessages) {
|
|
465
|
+
const existingMessage = await this.findMessageInAnyThread(message.id);
|
|
466
|
+
console.log(`Checking message ${message.id}: existing=${existingMessage?.threadId}, new=${message.threadId}`);
|
|
467
|
+
if (existingMessage && existingMessage.threadId && existingMessage.threadId !== message.threadId) {
|
|
468
|
+
console.log(`Migrating message ${message.id} from ${existingMessage.threadId} to ${message.threadId}`);
|
|
469
|
+
messageMigrationTasks.push(this.migrateMessage(message.id, existingMessage.threadId, message.threadId));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
await Promise.all(messageMigrationTasks);
|
|
473
|
+
const messagesByThread = validatedMessages.reduce((acc, message) => {
|
|
474
|
+
if (message.threadId && !acc.has(message.threadId)) {
|
|
475
|
+
acc.set(message.threadId, []);
|
|
476
|
+
}
|
|
477
|
+
if (message.threadId) {
|
|
478
|
+
acc.get(message.threadId).push(message);
|
|
479
|
+
}
|
|
480
|
+
return acc;
|
|
481
|
+
}, /* @__PURE__ */ new Map());
|
|
482
|
+
await Promise.all(
|
|
483
|
+
Array.from(messagesByThread.entries()).map(async ([threadId, threadMessages]) => {
|
|
484
|
+
try {
|
|
485
|
+
const thread = await this.getThreadById({ threadId });
|
|
486
|
+
if (!thread) {
|
|
487
|
+
throw new Error(`Thread ${threadId} not found`);
|
|
488
|
+
}
|
|
489
|
+
await Promise.all(
|
|
490
|
+
threadMessages.map(async (message) => {
|
|
491
|
+
const key = this.getMessageKey(threadId, message.id);
|
|
492
|
+
const { _index, ...cleanMessage } = message;
|
|
493
|
+
const serializedMessage = {
|
|
494
|
+
...cleanMessage,
|
|
495
|
+
createdAt: serializeDate(cleanMessage.createdAt)
|
|
496
|
+
};
|
|
497
|
+
console.log(`Saving message ${message.id} with content:`, {
|
|
498
|
+
content: serializedMessage.content,
|
|
499
|
+
contentType: typeof serializedMessage.content,
|
|
500
|
+
isArray: Array.isArray(serializedMessage.content)
|
|
501
|
+
});
|
|
502
|
+
await this.operations.putKV({ tableName: TABLE_MESSAGES, key, value: serializedMessage });
|
|
503
|
+
})
|
|
504
|
+
);
|
|
505
|
+
const orderKey = this.getThreadMessagesKey(threadId);
|
|
506
|
+
const entries = await this.updateSorting(threadMessages);
|
|
507
|
+
await this.updateSortedMessages(orderKey, entries);
|
|
508
|
+
const updatedThread = {
|
|
509
|
+
...thread,
|
|
510
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
511
|
+
};
|
|
512
|
+
await this.operations.putKV({
|
|
513
|
+
tableName: TABLE_THREADS,
|
|
514
|
+
key: this.operations.getKey(TABLE_THREADS, { id: threadId }),
|
|
515
|
+
value: updatedThread
|
|
516
|
+
});
|
|
517
|
+
} catch (error) {
|
|
518
|
+
throw new MastraError(
|
|
519
|
+
{
|
|
520
|
+
id: "CLOUDFLARE_STORAGE_SAVE_MESSAGES_FAILED",
|
|
521
|
+
domain: ErrorDomain.STORAGE,
|
|
522
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
523
|
+
details: {
|
|
524
|
+
threadId
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
error
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
})
|
|
531
|
+
);
|
|
532
|
+
const prepared = validatedMessages.map(
|
|
533
|
+
({ _index, ...message }) => ({ ...message, type: message.type !== "v2" ? message.type : void 0 })
|
|
534
|
+
);
|
|
535
|
+
const list = new MessageList().add(prepared, "memory");
|
|
536
|
+
if (format === `v2`) return list.get.all.v2();
|
|
537
|
+
return list.get.all.v1();
|
|
538
|
+
} catch (error) {
|
|
539
|
+
throw new MastraError(
|
|
540
|
+
{
|
|
541
|
+
id: "CLOUDFLARE_STORAGE_SAVE_MESSAGES_FAILED",
|
|
542
|
+
domain: ErrorDomain.STORAGE,
|
|
543
|
+
category: ErrorCategory.THIRD_PARTY
|
|
544
|
+
},
|
|
545
|
+
error
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
async getRank(orderKey, id) {
|
|
550
|
+
const order = await this.getSortedMessages(orderKey);
|
|
551
|
+
const index = order.findIndex((item) => item.id === id);
|
|
552
|
+
return index >= 0 ? index : null;
|
|
553
|
+
}
|
|
554
|
+
async getRange(orderKey, start, end) {
|
|
555
|
+
const order = await this.getSortedMessages(orderKey);
|
|
556
|
+
const actualStart = start < 0 ? Math.max(0, order.length + start) : start;
|
|
557
|
+
const actualEnd = end < 0 ? order.length + end : Math.min(end, order.length - 1);
|
|
558
|
+
const sliced = order.slice(actualStart, actualEnd + 1);
|
|
559
|
+
return sliced.map((item) => item.id);
|
|
560
|
+
}
|
|
561
|
+
async getLastN(orderKey, n) {
|
|
562
|
+
return this.getRange(orderKey, -n, -1);
|
|
563
|
+
}
|
|
564
|
+
async getFullOrder(orderKey) {
|
|
565
|
+
return this.getRange(orderKey, 0, -1);
|
|
566
|
+
}
|
|
567
|
+
async getIncludedMessagesWithContext(threadId, include, messageIds) {
|
|
568
|
+
await Promise.all(
|
|
569
|
+
include.map(async (item) => {
|
|
570
|
+
const targetThreadId = item.threadId || threadId;
|
|
571
|
+
if (!targetThreadId) return;
|
|
572
|
+
const threadMessagesKey = this.getThreadMessagesKey(targetThreadId);
|
|
573
|
+
messageIds.add(item.id);
|
|
574
|
+
if (!item.withPreviousMessages && !item.withNextMessages) return;
|
|
575
|
+
const rank = await this.getRank(threadMessagesKey, item.id);
|
|
576
|
+
if (rank === null) return;
|
|
577
|
+
if (item.withPreviousMessages) {
|
|
578
|
+
const prevIds = await this.getRange(
|
|
579
|
+
threadMessagesKey,
|
|
580
|
+
Math.max(0, rank - item.withPreviousMessages),
|
|
581
|
+
rank - 1
|
|
582
|
+
);
|
|
583
|
+
prevIds.forEach((id) => messageIds.add(id));
|
|
584
|
+
}
|
|
585
|
+
if (item.withNextMessages) {
|
|
586
|
+
const nextIds = await this.getRange(threadMessagesKey, rank + 1, rank + item.withNextMessages);
|
|
587
|
+
nextIds.forEach((id) => messageIds.add(id));
|
|
588
|
+
}
|
|
589
|
+
})
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
async getRecentMessages(threadId, limit, messageIds) {
|
|
593
|
+
if (limit <= 0) return;
|
|
594
|
+
try {
|
|
595
|
+
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
596
|
+
const latestIds = await this.getLastN(threadMessagesKey, limit);
|
|
597
|
+
latestIds.forEach((id) => messageIds.add(id));
|
|
598
|
+
} catch {
|
|
599
|
+
console.log(`No message order found for thread ${threadId}, skipping latest messages`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
async fetchAndParseMessagesFromMultipleThreads(messageIds, include, targetThreadId) {
|
|
603
|
+
const messageIdToThreadId = /* @__PURE__ */ new Map();
|
|
604
|
+
if (include) {
|
|
605
|
+
for (const item of include) {
|
|
606
|
+
if (item.threadId) {
|
|
607
|
+
messageIdToThreadId.set(item.id, item.threadId);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
const messages = await Promise.all(
|
|
612
|
+
messageIds.map(async (id) => {
|
|
613
|
+
try {
|
|
614
|
+
let threadId = messageIdToThreadId.get(id);
|
|
615
|
+
if (!threadId) {
|
|
616
|
+
if (targetThreadId) {
|
|
617
|
+
threadId = targetThreadId;
|
|
618
|
+
} else {
|
|
619
|
+
const foundMessage = await this.findMessageInAnyThread(id);
|
|
620
|
+
if (foundMessage) {
|
|
621
|
+
threadId = foundMessage.threadId;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (!threadId) return null;
|
|
626
|
+
const key = this.getMessageKey(threadId, id);
|
|
627
|
+
const data = await this.operations.getKV(TABLE_MESSAGES, key);
|
|
628
|
+
if (!data) return null;
|
|
629
|
+
const parsed = typeof data === "string" ? JSON.parse(data) : data;
|
|
630
|
+
console.log(`Retrieved message ${id} from thread ${threadId} with content:`, {
|
|
631
|
+
content: parsed.content,
|
|
632
|
+
contentType: typeof parsed.content,
|
|
633
|
+
isArray: Array.isArray(parsed.content)
|
|
634
|
+
});
|
|
635
|
+
return parsed;
|
|
636
|
+
} catch (error) {
|
|
637
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
638
|
+
this.logger.error(`Error retrieving message ${id}:`, { message });
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
641
|
+
})
|
|
642
|
+
);
|
|
643
|
+
return messages.filter((msg) => msg !== null);
|
|
644
|
+
}
|
|
645
|
+
async getMessages({
|
|
646
|
+
threadId,
|
|
647
|
+
resourceId,
|
|
648
|
+
selectBy,
|
|
649
|
+
format
|
|
650
|
+
}) {
|
|
651
|
+
console.log(`getMessages called with format: ${format}, threadId: ${threadId}`);
|
|
652
|
+
if (!threadId) throw new Error("threadId is required");
|
|
653
|
+
const actualFormat = format || "v1";
|
|
654
|
+
console.log(`Using format: ${actualFormat}`);
|
|
655
|
+
if (!threadId) throw new Error("threadId is required");
|
|
656
|
+
const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
657
|
+
const messageIds = /* @__PURE__ */ new Set();
|
|
658
|
+
if (limit === 0 && !selectBy?.include?.length) return [];
|
|
659
|
+
try {
|
|
660
|
+
await Promise.all([
|
|
661
|
+
selectBy?.include?.length ? this.getIncludedMessagesWithContext(threadId, selectBy.include, messageIds) : Promise.resolve(),
|
|
662
|
+
limit > 0 ? this.getRecentMessages(threadId, limit, messageIds) : Promise.resolve()
|
|
663
|
+
]);
|
|
664
|
+
const targetThreadId = selectBy?.include?.length ? void 0 : threadId;
|
|
665
|
+
const messages = await this.fetchAndParseMessagesFromMultipleThreads(
|
|
666
|
+
Array.from(messageIds),
|
|
667
|
+
selectBy?.include,
|
|
668
|
+
targetThreadId
|
|
669
|
+
);
|
|
670
|
+
if (!messages.length) return [];
|
|
671
|
+
try {
|
|
672
|
+
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
673
|
+
const messageOrder = await this.getFullOrder(threadMessagesKey);
|
|
674
|
+
const orderMap = new Map(messageOrder.map((id, index) => [id, index]));
|
|
675
|
+
messages.sort((a, b) => {
|
|
676
|
+
const indexA = orderMap.get(a.id);
|
|
677
|
+
const indexB = orderMap.get(b.id);
|
|
678
|
+
if (indexA !== void 0 && indexB !== void 0) return orderMap.get(a.id) - orderMap.get(b.id);
|
|
679
|
+
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
|
680
|
+
});
|
|
681
|
+
} catch (error) {
|
|
682
|
+
const mastraError = new MastraError(
|
|
683
|
+
{
|
|
684
|
+
id: "CLOUDFLARE_STORAGE_SORT_MESSAGES_FAILED",
|
|
685
|
+
domain: ErrorDomain.STORAGE,
|
|
686
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
687
|
+
text: `Error sorting messages for thread ${threadId} falling back to creation time`,
|
|
688
|
+
details: {
|
|
689
|
+
threadId
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
error
|
|
693
|
+
);
|
|
694
|
+
this.logger?.trackException(mastraError);
|
|
695
|
+
this.logger?.error(mastraError.toString());
|
|
696
|
+
messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
697
|
+
}
|
|
698
|
+
const prepared = messages.map(({ _index, ...message }) => ({
|
|
699
|
+
...message,
|
|
700
|
+
type: message.type === `v2` ? void 0 : message.type,
|
|
701
|
+
createdAt: ensureDate(message.createdAt)
|
|
702
|
+
}));
|
|
703
|
+
if (actualFormat === `v1`) {
|
|
704
|
+
console.log(`Processing ${prepared.length} messages for v1 format - returning directly without MessageList`);
|
|
705
|
+
return prepared.map((msg) => ({
|
|
706
|
+
...msg,
|
|
707
|
+
createdAt: new Date(msg.createdAt)
|
|
708
|
+
}));
|
|
709
|
+
}
|
|
710
|
+
const list = new MessageList({ threadId, resourceId }).add(prepared, "memory");
|
|
711
|
+
return list.get.all.v2();
|
|
712
|
+
} catch (error) {
|
|
713
|
+
const mastraError = new MastraError(
|
|
714
|
+
{
|
|
715
|
+
id: "CLOUDFLARE_STORAGE_GET_MESSAGES_FAILED",
|
|
716
|
+
domain: ErrorDomain.STORAGE,
|
|
717
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
718
|
+
text: `Error retrieving messages for thread ${threadId}`,
|
|
719
|
+
details: {
|
|
720
|
+
threadId
|
|
721
|
+
}
|
|
722
|
+
},
|
|
723
|
+
error
|
|
724
|
+
);
|
|
725
|
+
this.logger?.trackException(mastraError);
|
|
726
|
+
this.logger?.error(mastraError.toString());
|
|
727
|
+
return [];
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
async getMessagesPaginated(args) {
|
|
731
|
+
try {
|
|
732
|
+
const { threadId, selectBy, format = "v1" } = args;
|
|
733
|
+
const { page = 0, perPage = 100 } = selectBy?.pagination || {};
|
|
734
|
+
const messages = format === "v2" ? await this.getMessages({ threadId, selectBy, format: "v2" }) : await this.getMessages({ threadId, selectBy, format: "v1" });
|
|
735
|
+
let filteredMessages = messages;
|
|
736
|
+
if (selectBy?.pagination?.dateRange) {
|
|
737
|
+
const { start: dateStart, end: dateEnd } = selectBy.pagination.dateRange;
|
|
738
|
+
filteredMessages = messages.filter((message) => {
|
|
739
|
+
const messageDate = new Date(message.createdAt);
|
|
740
|
+
if (dateStart && messageDate < dateStart) return false;
|
|
741
|
+
if (dateEnd && messageDate > dateEnd) return false;
|
|
742
|
+
return true;
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
const start = page * perPage;
|
|
746
|
+
const end = start + perPage;
|
|
747
|
+
const paginatedMessages = filteredMessages.slice(start, end);
|
|
748
|
+
return {
|
|
749
|
+
page,
|
|
750
|
+
perPage,
|
|
751
|
+
total: filteredMessages.length,
|
|
752
|
+
hasMore: start + perPage < filteredMessages.length,
|
|
753
|
+
messages: paginatedMessages
|
|
754
|
+
};
|
|
755
|
+
} catch (error) {
|
|
756
|
+
throw new MastraError(
|
|
757
|
+
{
|
|
758
|
+
id: "CLOUDFLARE_STORAGE_GET_MESSAGES_PAGINATED_FAILED",
|
|
759
|
+
domain: ErrorDomain.STORAGE,
|
|
760
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
761
|
+
text: "Failed to get messages with pagination"
|
|
762
|
+
},
|
|
763
|
+
error
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
async updateMessages(args) {
|
|
768
|
+
try {
|
|
769
|
+
const { messages } = args;
|
|
770
|
+
const updatedMessages = [];
|
|
771
|
+
for (const messageUpdate of messages) {
|
|
772
|
+
const { id, content, ...otherFields } = messageUpdate;
|
|
773
|
+
const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
|
|
774
|
+
const keyObjs = await this.operations.listKV(TABLE_MESSAGES, { prefix: `${prefix}${TABLE_MESSAGES}` });
|
|
775
|
+
let existingMessage = null;
|
|
776
|
+
let messageKey = "";
|
|
777
|
+
for (const { name: key } of keyObjs) {
|
|
778
|
+
const data = await this.operations.getKV(TABLE_MESSAGES, key);
|
|
779
|
+
if (data && data.id === id) {
|
|
780
|
+
existingMessage = data;
|
|
781
|
+
messageKey = key;
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (!existingMessage) {
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
const updatedMessage = {
|
|
789
|
+
...existingMessage,
|
|
790
|
+
...otherFields,
|
|
791
|
+
id
|
|
792
|
+
};
|
|
793
|
+
if (content) {
|
|
794
|
+
if (content.metadata !== void 0) {
|
|
795
|
+
updatedMessage.content = {
|
|
796
|
+
...updatedMessage.content,
|
|
797
|
+
metadata: {
|
|
798
|
+
...updatedMessage.content?.metadata,
|
|
799
|
+
...content.metadata
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
if (content.content !== void 0) {
|
|
804
|
+
updatedMessage.content = {
|
|
805
|
+
...updatedMessage.content,
|
|
806
|
+
content: content.content
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
if ("threadId" in messageUpdate && messageUpdate.threadId && messageUpdate.threadId !== existingMessage.threadId) {
|
|
811
|
+
await this.operations.deleteKV(TABLE_MESSAGES, messageKey);
|
|
812
|
+
updatedMessage.threadId = messageUpdate.threadId;
|
|
813
|
+
const newMessageKey = this.getMessageKey(messageUpdate.threadId, id);
|
|
814
|
+
await this.operations.putKV({
|
|
815
|
+
tableName: TABLE_MESSAGES,
|
|
816
|
+
key: newMessageKey,
|
|
817
|
+
value: updatedMessage
|
|
818
|
+
});
|
|
819
|
+
if (existingMessage.threadId) {
|
|
820
|
+
const sourceOrderKey = this.getThreadMessagesKey(existingMessage.threadId);
|
|
821
|
+
const sourceEntries = await this.getSortedMessages(sourceOrderKey);
|
|
822
|
+
const filteredEntries = sourceEntries.filter((entry) => entry.id !== id);
|
|
823
|
+
await this.updateSortedMessages(sourceOrderKey, filteredEntries);
|
|
824
|
+
}
|
|
825
|
+
const destOrderKey = this.getThreadMessagesKey(messageUpdate.threadId);
|
|
826
|
+
const destEntries = await this.getSortedMessages(destOrderKey);
|
|
827
|
+
const newEntry = { id, score: Date.now() };
|
|
828
|
+
destEntries.push(newEntry);
|
|
829
|
+
await this.updateSortedMessages(destOrderKey, destEntries);
|
|
830
|
+
} else {
|
|
831
|
+
await this.operations.putKV({
|
|
832
|
+
tableName: TABLE_MESSAGES,
|
|
833
|
+
key: messageKey,
|
|
834
|
+
value: updatedMessage
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
const threadsToUpdate = /* @__PURE__ */ new Set();
|
|
838
|
+
if (updatedMessage.threadId) {
|
|
839
|
+
threadsToUpdate.add(updatedMessage.threadId);
|
|
840
|
+
}
|
|
841
|
+
if ("threadId" in messageUpdate && messageUpdate.threadId && messageUpdate.threadId !== existingMessage.threadId) {
|
|
842
|
+
if (existingMessage.threadId) {
|
|
843
|
+
threadsToUpdate.add(existingMessage.threadId);
|
|
844
|
+
}
|
|
845
|
+
threadsToUpdate.add(messageUpdate.threadId);
|
|
846
|
+
}
|
|
847
|
+
for (const threadId of threadsToUpdate) {
|
|
848
|
+
const thread = await this.getThreadById({ threadId });
|
|
849
|
+
if (thread) {
|
|
850
|
+
const updatedThread = {
|
|
851
|
+
...thread,
|
|
852
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
853
|
+
};
|
|
854
|
+
await this.operations.putKV({
|
|
855
|
+
tableName: TABLE_THREADS,
|
|
856
|
+
key: this.operations.getKey(TABLE_THREADS, { id: threadId }),
|
|
857
|
+
value: updatedThread
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
updatedMessages.push(updatedMessage);
|
|
60
862
|
}
|
|
863
|
+
return updatedMessages;
|
|
61
864
|
} catch (error) {
|
|
62
865
|
throw new MastraError(
|
|
63
866
|
{
|
|
64
|
-
id: "
|
|
867
|
+
id: "CLOUDFLARE_STORAGE_UPDATE_MESSAGES_FAILED",
|
|
65
868
|
domain: ErrorDomain.STORAGE,
|
|
66
|
-
category: ErrorCategory.THIRD_PARTY
|
|
869
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
870
|
+
text: "Failed to update messages"
|
|
67
871
|
},
|
|
68
872
|
error
|
|
69
873
|
);
|
|
70
874
|
}
|
|
71
875
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (!binding) throw new Error(`No binding found for namespace ${tableName}`);
|
|
78
|
-
return binding;
|
|
79
|
-
}
|
|
80
|
-
async listNamespaces() {
|
|
81
|
-
if (this.bindings) {
|
|
876
|
+
async getResourceById({ resourceId }) {
|
|
877
|
+
try {
|
|
878
|
+
const data = await this.operations.getKV(TABLE_RESOURCES, resourceId);
|
|
879
|
+
if (!data) return null;
|
|
880
|
+
const resource = typeof data === "string" ? JSON.parse(data) : data;
|
|
82
881
|
return {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}))
|
|
882
|
+
...resource,
|
|
883
|
+
createdAt: ensureDate(resource.createdAt),
|
|
884
|
+
updatedAt: ensureDate(resource.updatedAt),
|
|
885
|
+
metadata: this.ensureMetadata(resource.metadata)
|
|
88
886
|
};
|
|
887
|
+
} catch (error) {
|
|
888
|
+
const mastraError = new MastraError(
|
|
889
|
+
{
|
|
890
|
+
id: "CLOUDFLARE_STORAGE_GET_RESOURCE_BY_ID_FAILED",
|
|
891
|
+
domain: ErrorDomain.STORAGE,
|
|
892
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
893
|
+
details: {
|
|
894
|
+
resourceId
|
|
895
|
+
}
|
|
896
|
+
},
|
|
897
|
+
error
|
|
898
|
+
);
|
|
899
|
+
this.logger?.trackException(mastraError);
|
|
900
|
+
this.logger?.error(mastraError.toString());
|
|
901
|
+
return null;
|
|
89
902
|
}
|
|
90
|
-
let allNamespaces = [];
|
|
91
|
-
let currentPage = 1;
|
|
92
|
-
const perPage = 50;
|
|
93
|
-
let morePagesExist = true;
|
|
94
|
-
while (morePagesExist) {
|
|
95
|
-
const response = await this.client.kv.namespaces.list({
|
|
96
|
-
account_id: this.accountId,
|
|
97
|
-
page: currentPage,
|
|
98
|
-
per_page: perPage
|
|
99
|
-
});
|
|
100
|
-
if (response.result) {
|
|
101
|
-
allNamespaces = allNamespaces.concat(response.result);
|
|
102
|
-
}
|
|
103
|
-
morePagesExist = response.result ? response.result.length === perPage : false;
|
|
104
|
-
if (morePagesExist) {
|
|
105
|
-
currentPage++;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return { result: allNamespaces };
|
|
109
903
|
}
|
|
110
|
-
async
|
|
904
|
+
async saveResource({ resource }) {
|
|
111
905
|
try {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return await response.text();
|
|
123
|
-
}
|
|
906
|
+
const resourceToSave = {
|
|
907
|
+
...resource,
|
|
908
|
+
metadata: resource.metadata ? JSON.stringify(resource.metadata) : null
|
|
909
|
+
};
|
|
910
|
+
await this.operations.putKV({
|
|
911
|
+
tableName: TABLE_RESOURCES,
|
|
912
|
+
key: resource.id,
|
|
913
|
+
value: resourceToSave
|
|
914
|
+
});
|
|
915
|
+
return resource;
|
|
124
916
|
} catch (error) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
917
|
+
throw new MastraError(
|
|
918
|
+
{
|
|
919
|
+
id: "CLOUDFLARE_STORAGE_SAVE_RESOURCE_FAILED",
|
|
920
|
+
domain: ErrorDomain.STORAGE,
|
|
921
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
922
|
+
details: {
|
|
923
|
+
resourceId: resource.id
|
|
924
|
+
}
|
|
925
|
+
},
|
|
926
|
+
error
|
|
927
|
+
);
|
|
131
928
|
}
|
|
132
929
|
}
|
|
133
|
-
async
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
value,
|
|
930
|
+
async updateResource({
|
|
931
|
+
resourceId,
|
|
932
|
+
workingMemory,
|
|
137
933
|
metadata
|
|
138
934
|
}) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
value: serializedValue,
|
|
150
|
-
metadata: serializedMetadata
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
} catch (error) {
|
|
154
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
155
|
-
this.logger.error(`Failed to put value for ${tableName} ${key}:`, { message });
|
|
156
|
-
throw error;
|
|
935
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
936
|
+
if (!existingResource) {
|
|
937
|
+
const newResource = {
|
|
938
|
+
id: resourceId,
|
|
939
|
+
workingMemory,
|
|
940
|
+
metadata: metadata || {},
|
|
941
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
942
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
943
|
+
};
|
|
944
|
+
return this.saveResource({ resource: newResource });
|
|
157
945
|
}
|
|
946
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
947
|
+
const updatedResource = {
|
|
948
|
+
...existingResource,
|
|
949
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
950
|
+
metadata: {
|
|
951
|
+
...existingResource.metadata,
|
|
952
|
+
...metadata
|
|
953
|
+
},
|
|
954
|
+
updatedAt
|
|
955
|
+
};
|
|
956
|
+
return this.saveResource({ resource: updatedResource });
|
|
158
957
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
958
|
+
};
|
|
959
|
+
var StoreOperationsCloudflare = class extends StoreOperations {
|
|
960
|
+
bindings;
|
|
961
|
+
client;
|
|
962
|
+
accountId;
|
|
963
|
+
namespacePrefix;
|
|
964
|
+
constructor({
|
|
965
|
+
namespacePrefix,
|
|
966
|
+
bindings,
|
|
967
|
+
client,
|
|
968
|
+
accountId
|
|
969
|
+
}) {
|
|
970
|
+
super();
|
|
971
|
+
this.bindings = bindings;
|
|
972
|
+
this.namespacePrefix = namespacePrefix;
|
|
973
|
+
this.client = client;
|
|
974
|
+
this.accountId = accountId;
|
|
169
975
|
}
|
|
170
|
-
async
|
|
976
|
+
async hasColumn() {
|
|
977
|
+
return true;
|
|
978
|
+
}
|
|
979
|
+
async alterTable(_args) {
|
|
980
|
+
}
|
|
981
|
+
async clearTable({ tableName }) {
|
|
171
982
|
try {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
limit: options?.limit || 1e3,
|
|
176
|
-
prefix: options?.prefix
|
|
177
|
-
});
|
|
178
|
-
return response.keys;
|
|
179
|
-
} else {
|
|
180
|
-
const namespaceId = await this.getNamespaceId(tableName);
|
|
181
|
-
const response = await this.client.kv.namespaces.keys.list(namespaceId, {
|
|
182
|
-
account_id: this.accountId,
|
|
183
|
-
limit: options?.limit || 1e3,
|
|
184
|
-
prefix: options?.prefix
|
|
185
|
-
});
|
|
186
|
-
return response.result;
|
|
983
|
+
const keys = await this.listKV(tableName);
|
|
984
|
+
if (keys.length > 0) {
|
|
985
|
+
await Promise.all(keys.map((keyObj) => this.deleteKV(tableName, keyObj.name)));
|
|
187
986
|
}
|
|
188
987
|
} catch (error) {
|
|
189
988
|
throw new MastraError(
|
|
190
989
|
{
|
|
191
|
-
id: "
|
|
990
|
+
id: "CLOUDFLARE_STORAGE_CLEAR_TABLE_FAILED",
|
|
192
991
|
domain: ErrorDomain.STORAGE,
|
|
193
992
|
category: ErrorCategory.THIRD_PARTY,
|
|
194
993
|
details: {
|
|
@@ -199,110 +998,69 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
199
998
|
);
|
|
200
999
|
}
|
|
201
1000
|
}
|
|
202
|
-
async
|
|
203
|
-
if (this.bindings) {
|
|
204
|
-
return {
|
|
205
|
-
id: title,
|
|
206
|
-
// Use title as ID since that's what we need
|
|
207
|
-
title,
|
|
208
|
-
supports_url_encoding: true
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
return await this.client.kv.namespaces.create({
|
|
212
|
-
account_id: this.accountId,
|
|
213
|
-
title
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
async getNamespaceIdByName(namespaceName) {
|
|
217
|
-
try {
|
|
218
|
-
const response = await this.listNamespaces();
|
|
219
|
-
const namespace = response.result.find((ns) => ns.title === namespaceName);
|
|
220
|
-
return namespace ? namespace.id : null;
|
|
221
|
-
} catch (error) {
|
|
222
|
-
this.logger.error(`Failed to get namespace ID for ${namespaceName}:`, error);
|
|
223
|
-
return null;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
async createNamespace(namespaceName) {
|
|
1001
|
+
async dropTable({ tableName }) {
|
|
227
1002
|
try {
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (error.message && error.message.includes("already exists")) {
|
|
232
|
-
const namespaces = await this.listNamespaces();
|
|
233
|
-
const namespace = namespaces.result.find((ns) => ns.title === namespaceName);
|
|
234
|
-
if (namespace) return namespace.id;
|
|
1003
|
+
const keys = await this.listKV(tableName);
|
|
1004
|
+
if (keys.length > 0) {
|
|
1005
|
+
await Promise.all(keys.map((keyObj) => this.deleteKV(tableName, keyObj.name)));
|
|
235
1006
|
}
|
|
236
|
-
|
|
237
|
-
throw new
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
1007
|
+
} catch (error) {
|
|
1008
|
+
throw new MastraError(
|
|
1009
|
+
{
|
|
1010
|
+
id: "CLOUDFLARE_STORAGE_DROP_TABLE_FAILED",
|
|
1011
|
+
domain: ErrorDomain.STORAGE,
|
|
1012
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1013
|
+
details: {
|
|
1014
|
+
tableName
|
|
1015
|
+
}
|
|
1016
|
+
},
|
|
1017
|
+
error
|
|
1018
|
+
);
|
|
244
1019
|
}
|
|
245
|
-
return namespaceId;
|
|
246
1020
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const legacyNamespaceId = await this.checkLegacyNamespace(tableName, prefix);
|
|
251
|
-
if (legacyNamespaceId) {
|
|
252
|
-
return legacyNamespaceId;
|
|
253
|
-
}
|
|
254
|
-
return await this.getOrCreateNamespaceId(`${prefix}${tableName}`);
|
|
255
|
-
} catch (error) {
|
|
256
|
-
this.logger.error("Error fetching namespace ID:", error);
|
|
257
|
-
throw new Error(`Failed to fetch namespace ID for table ${tableName}: ${error.message}`);
|
|
1021
|
+
getBinding(tableName) {
|
|
1022
|
+
if (!this.bindings) {
|
|
1023
|
+
throw new Error(`Cannot use Workers API binding for ${tableName}: Store initialized with REST API configuration`);
|
|
258
1024
|
}
|
|
1025
|
+
const binding = this.bindings[tableName];
|
|
1026
|
+
if (!binding) throw new Error(`No binding found for namespace ${tableName}`);
|
|
1027
|
+
return binding;
|
|
259
1028
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
*/
|
|
277
|
-
async checkLegacyNamespace(tableName, prefix) {
|
|
278
|
-
const legacyNamespaceBase = this.LEGACY_NAMESPACE_MAP[tableName];
|
|
279
|
-
if (legacyNamespaceBase) {
|
|
280
|
-
const legacyNamespace = `${prefix}${legacyNamespaceBase}`;
|
|
281
|
-
const keyPrefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
|
|
282
|
-
const prefixKey = `${keyPrefix}${tableName}:`;
|
|
283
|
-
const legacyId = await this.getNamespaceIdByName(legacyNamespace);
|
|
284
|
-
if (legacyId) {
|
|
285
|
-
const response = await this.client.kv.namespaces.keys.list(legacyId, {
|
|
286
|
-
account_id: this.accountId,
|
|
287
|
-
prefix: prefixKey
|
|
288
|
-
});
|
|
289
|
-
const keys = response.result;
|
|
290
|
-
const hasTableData = keys.length > 0;
|
|
291
|
-
if (hasTableData) {
|
|
292
|
-
this.logger.warn(
|
|
293
|
-
`Using legacy namespace "${legacyNamespace}" for ${tableName}. Consider migrating to a dedicated namespace "${prefix}${tableName}".`
|
|
294
|
-
);
|
|
295
|
-
return legacyId;
|
|
1029
|
+
getKey(tableName, record) {
|
|
1030
|
+
const prefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
|
|
1031
|
+
switch (tableName) {
|
|
1032
|
+
case TABLE_THREADS:
|
|
1033
|
+
if (!record.id) throw new Error("Thread ID is required");
|
|
1034
|
+
return `${prefix}${tableName}:${record.id}`;
|
|
1035
|
+
case TABLE_MESSAGES:
|
|
1036
|
+
if (!record.threadId || !record.id) throw new Error("Thread ID and Message ID are required");
|
|
1037
|
+
return `${prefix}${tableName}:${record.threadId}:${record.id}`;
|
|
1038
|
+
case TABLE_WORKFLOW_SNAPSHOT:
|
|
1039
|
+
if (!record.workflow_name || !record.run_id) {
|
|
1040
|
+
throw new Error("Workflow name, and run ID are required");
|
|
1041
|
+
}
|
|
1042
|
+
let key = `${prefix}${tableName}:${record.workflow_name}:${record.run_id}`;
|
|
1043
|
+
if (record.resourceId) {
|
|
1044
|
+
key = `${key}:${record.resourceId}`;
|
|
296
1045
|
}
|
|
297
|
-
|
|
1046
|
+
return key;
|
|
1047
|
+
case TABLE_TRACES:
|
|
1048
|
+
if (!record.id) throw new Error("Trace ID is required");
|
|
1049
|
+
return `${prefix}${tableName}:${record.id}`;
|
|
1050
|
+
case TABLE_EVALS:
|
|
1051
|
+
const evalId = record.id || record.run_id;
|
|
1052
|
+
if (!evalId) throw new Error("Eval ID or run_id is required");
|
|
1053
|
+
return `${prefix}${tableName}:${evalId}`;
|
|
1054
|
+
case TABLE_SCORERS:
|
|
1055
|
+
if (!record.id) throw new Error("Score ID is required");
|
|
1056
|
+
return `${prefix}${tableName}:${record.id}`;
|
|
1057
|
+
default:
|
|
1058
|
+
throw new Error(`Unsupported table: ${tableName}`);
|
|
298
1059
|
}
|
|
299
|
-
return null;
|
|
300
1060
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
safeSerialize(data) {
|
|
305
|
-
return typeof data === "string" ? data : JSON.stringify(data);
|
|
1061
|
+
getSchemaKey(tableName) {
|
|
1062
|
+
const prefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
|
|
1063
|
+
return `${prefix}schema:${tableName}`;
|
|
306
1064
|
}
|
|
307
1065
|
/**
|
|
308
1066
|
* Helper to safely parse data from KV storage
|
|
@@ -328,203 +1086,121 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
328
1086
|
return null;
|
|
329
1087
|
}
|
|
330
1088
|
}
|
|
331
|
-
async
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
} catch (error) {
|
|
340
|
-
this.logger.error(`Failed to put KV value for ${tableName}:${key}:`, error);
|
|
341
|
-
throw new Error(`Failed to put KV value: ${error.message}`);
|
|
1089
|
+
async createNamespaceById(title) {
|
|
1090
|
+
if (this.bindings) {
|
|
1091
|
+
return {
|
|
1092
|
+
id: title,
|
|
1093
|
+
// Use title as ID since that's what we need
|
|
1094
|
+
title,
|
|
1095
|
+
supports_url_encoding: true
|
|
1096
|
+
};
|
|
342
1097
|
}
|
|
1098
|
+
return await this.client.kv.namespaces.create({
|
|
1099
|
+
account_id: this.accountId,
|
|
1100
|
+
title
|
|
1101
|
+
});
|
|
343
1102
|
}
|
|
344
|
-
async
|
|
1103
|
+
async createNamespace(namespaceName) {
|
|
345
1104
|
try {
|
|
346
|
-
const
|
|
347
|
-
return
|
|
1105
|
+
const response = await this.createNamespaceById(namespaceName);
|
|
1106
|
+
return response.id;
|
|
348
1107
|
} catch (error) {
|
|
349
|
-
|
|
350
|
-
|
|
1108
|
+
if (error.message && error.message.includes("already exists")) {
|
|
1109
|
+
const namespaces = await this.listNamespaces();
|
|
1110
|
+
const namespace = namespaces.result.find((ns) => ns.title === namespaceName);
|
|
1111
|
+
if (namespace) return namespace.id;
|
|
1112
|
+
}
|
|
1113
|
+
this.logger.error("Error creating namespace:", error);
|
|
1114
|
+
throw new Error(`Failed to create namespace ${namespaceName}: ${error.message}`);
|
|
351
1115
|
}
|
|
352
1116
|
}
|
|
353
|
-
async
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
1117
|
+
async listNamespaces() {
|
|
1118
|
+
if (this.bindings) {
|
|
1119
|
+
return {
|
|
1120
|
+
result: Object.keys(this.bindings).map((name) => ({
|
|
1121
|
+
id: name,
|
|
1122
|
+
title: name,
|
|
1123
|
+
supports_url_encoding: true
|
|
1124
|
+
}))
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
let allNamespaces = [];
|
|
1128
|
+
let currentPage = 1;
|
|
1129
|
+
const perPage = 50;
|
|
1130
|
+
let morePagesExist = true;
|
|
1131
|
+
while (morePagesExist) {
|
|
1132
|
+
const response = await this.client.kv.namespaces.list({
|
|
1133
|
+
account_id: this.accountId,
|
|
1134
|
+
page: currentPage,
|
|
1135
|
+
per_page: perPage
|
|
1136
|
+
});
|
|
1137
|
+
if (response.result) {
|
|
1138
|
+
allNamespaces = allNamespaces.concat(response.result);
|
|
1139
|
+
}
|
|
1140
|
+
morePagesExist = response.result ? response.result.length === perPage : false;
|
|
1141
|
+
if (morePagesExist) {
|
|
1142
|
+
currentPage++;
|
|
1143
|
+
}
|
|
359
1144
|
}
|
|
1145
|
+
return { result: allNamespaces };
|
|
360
1146
|
}
|
|
361
|
-
async
|
|
1147
|
+
async getNamespaceIdByName(namespaceName) {
|
|
362
1148
|
try {
|
|
363
|
-
|
|
1149
|
+
const response = await this.listNamespaces();
|
|
1150
|
+
const namespace = response.result.find((ns) => ns.title === namespaceName);
|
|
1151
|
+
return namespace ? namespace.id : null;
|
|
364
1152
|
} catch (error) {
|
|
365
|
-
this.logger.error(`Failed to
|
|
366
|
-
|
|
1153
|
+
this.logger.error(`Failed to get namespace ID for ${namespaceName}:`, error);
|
|
1154
|
+
return null;
|
|
367
1155
|
}
|
|
368
1156
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
async getSortedMessages(orderKey) {
|
|
374
|
-
const raw = await this.getKV(TABLE_MESSAGES, orderKey);
|
|
375
|
-
if (!raw) return [];
|
|
376
|
-
try {
|
|
377
|
-
const arr = JSON.parse(typeof raw === "string" ? raw : JSON.stringify(raw));
|
|
378
|
-
return Array.isArray(arr) ? arr : [];
|
|
379
|
-
} catch (e) {
|
|
380
|
-
this.logger.error(`Error parsing order data for key ${orderKey}:`, { e });
|
|
381
|
-
return [];
|
|
1157
|
+
async getOrCreateNamespaceId(namespaceName) {
|
|
1158
|
+
let namespaceId = await this.getNamespaceIdByName(namespaceName);
|
|
1159
|
+
if (!namespaceId) {
|
|
1160
|
+
namespaceId = await this.createNamespace(namespaceName);
|
|
382
1161
|
}
|
|
1162
|
+
return namespaceId;
|
|
383
1163
|
}
|
|
384
|
-
async
|
|
385
|
-
|
|
386
|
-
message: msg,
|
|
387
|
-
// Use _index if available, otherwise timestamp, matching Upstash
|
|
388
|
-
score: msg._index !== void 0 ? msg._index : msg.createdAt.getTime()
|
|
389
|
-
})).sort((a, b) => a.score - b.score).map((item) => ({
|
|
390
|
-
id: item.message.id,
|
|
391
|
-
score: item.score
|
|
392
|
-
}));
|
|
393
|
-
}
|
|
394
|
-
async getIncludedMessagesWithContext(threadId, include, messageIds) {
|
|
395
|
-
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
396
|
-
await Promise.all(
|
|
397
|
-
include.map(async (item) => {
|
|
398
|
-
messageIds.add(item.id);
|
|
399
|
-
if (!item.withPreviousMessages && !item.withNextMessages) return;
|
|
400
|
-
const rank = await this.getRank(threadMessagesKey, item.id);
|
|
401
|
-
if (rank === null) return;
|
|
402
|
-
if (item.withPreviousMessages) {
|
|
403
|
-
const prevIds = await this.getRange(
|
|
404
|
-
threadMessagesKey,
|
|
405
|
-
Math.max(0, rank - item.withPreviousMessages),
|
|
406
|
-
rank - 1
|
|
407
|
-
);
|
|
408
|
-
prevIds.forEach((id) => messageIds.add(id));
|
|
409
|
-
}
|
|
410
|
-
if (item.withNextMessages) {
|
|
411
|
-
const nextIds = await this.getRange(threadMessagesKey, rank + 1, rank + item.withNextMessages);
|
|
412
|
-
nextIds.forEach((id) => messageIds.add(id));
|
|
413
|
-
}
|
|
414
|
-
})
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
async getRecentMessages(threadId, limit, messageIds) {
|
|
418
|
-
if (limit <= 0) return;
|
|
1164
|
+
async getNamespaceId(tableName) {
|
|
1165
|
+
const prefix = this.namespacePrefix ? `${this.namespacePrefix}_` : "";
|
|
419
1166
|
try {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
console.log(`No message order found for thread ${threadId}, skipping latest messages`);
|
|
1167
|
+
return await this.getOrCreateNamespaceId(`${prefix}${tableName}`);
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
this.logger.error("Error fetching namespace ID:", error);
|
|
1170
|
+
throw new Error(`Failed to fetch namespace ID for table ${tableName}: ${error.message}`);
|
|
425
1171
|
}
|
|
426
1172
|
}
|
|
427
|
-
async
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
this.
|
|
438
|
-
return null;
|
|
439
|
-
}
|
|
440
|
-
})
|
|
441
|
-
);
|
|
442
|
-
return messages.filter((msg) => msg !== null);
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* Queue for serializing sorted order updates.
|
|
446
|
-
* Updates the sorted order for a given key. This operation is eventually consistent.
|
|
447
|
-
*/
|
|
448
|
-
updateQueue = /* @__PURE__ */ new Map();
|
|
449
|
-
/**
|
|
450
|
-
* Updates the sorted order for a given key. This operation is eventually consistent.
|
|
451
|
-
* Note: Operations on the same orderKey are serialized using a queue to prevent
|
|
452
|
-
* concurrent updates from conflicting with each other.
|
|
453
|
-
*/
|
|
454
|
-
async updateSortedMessages(orderKey, newEntries) {
|
|
455
|
-
const currentPromise = this.updateQueue.get(orderKey) || Promise.resolve();
|
|
456
|
-
const nextPromise = currentPromise.then(async () => {
|
|
457
|
-
try {
|
|
458
|
-
const currentOrder = await this.getSortedMessages(orderKey);
|
|
459
|
-
const orderMap = new Map(currentOrder.map((entry) => [entry.id, entry]));
|
|
460
|
-
for (const entry of newEntries) {
|
|
461
|
-
orderMap.set(entry.id, entry);
|
|
462
|
-
}
|
|
463
|
-
const updatedOrder = Array.from(orderMap.values()).sort((a, b) => a.score - b.score);
|
|
464
|
-
await this.putKV({
|
|
465
|
-
tableName: TABLE_MESSAGES,
|
|
466
|
-
key: orderKey,
|
|
467
|
-
value: JSON.stringify(updatedOrder)
|
|
1173
|
+
async getNamespaceValue(tableName, key) {
|
|
1174
|
+
try {
|
|
1175
|
+
if (this.bindings) {
|
|
1176
|
+
const binding = this.getBinding(tableName);
|
|
1177
|
+
const result = await binding.getWithMetadata(key, "text");
|
|
1178
|
+
if (!result) return null;
|
|
1179
|
+
return JSON.stringify(result);
|
|
1180
|
+
} else {
|
|
1181
|
+
const namespaceId = await this.getNamespaceId(tableName);
|
|
1182
|
+
const response = await this.client.kv.namespaces.values.get(namespaceId, key, {
|
|
1183
|
+
account_id: this.accountId
|
|
468
1184
|
});
|
|
469
|
-
|
|
470
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
471
|
-
this.logger.error(`Error updating sorted order for key ${orderKey}:`, { message });
|
|
472
|
-
throw error;
|
|
473
|
-
} finally {
|
|
474
|
-
if (this.updateQueue.get(orderKey) === nextPromise) {
|
|
475
|
-
this.updateQueue.delete(orderKey);
|
|
476
|
-
}
|
|
1185
|
+
return await response.text();
|
|
477
1186
|
}
|
|
478
|
-
})
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
return index >= 0 ? index : null;
|
|
486
|
-
}
|
|
487
|
-
async getRange(orderKey, start, end) {
|
|
488
|
-
const order = await this.getSortedMessages(orderKey);
|
|
489
|
-
const actualStart = start < 0 ? Math.max(0, order.length + start) : start;
|
|
490
|
-
const actualEnd = end < 0 ? order.length + end : Math.min(end, order.length - 1);
|
|
491
|
-
const sliced = order.slice(actualStart, actualEnd + 1);
|
|
492
|
-
return sliced.map((item) => item.id);
|
|
493
|
-
}
|
|
494
|
-
async getLastN(orderKey, n) {
|
|
495
|
-
return this.getRange(orderKey, -n, -1);
|
|
496
|
-
}
|
|
497
|
-
async getFullOrder(orderKey) {
|
|
498
|
-
return this.getRange(orderKey, 0, -1);
|
|
499
|
-
}
|
|
500
|
-
getKey(tableName, record) {
|
|
501
|
-
const prefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
|
|
502
|
-
switch (tableName) {
|
|
503
|
-
case TABLE_THREADS:
|
|
504
|
-
if (!record.id) throw new Error("Thread ID is required");
|
|
505
|
-
return `${prefix}${tableName}:${record.id}`;
|
|
506
|
-
case TABLE_MESSAGES:
|
|
507
|
-
if (!record.threadId || !record.id) throw new Error("Thread ID and Message ID are required");
|
|
508
|
-
return `${prefix}${tableName}:${record.threadId}:${record.id}`;
|
|
509
|
-
case TABLE_WORKFLOW_SNAPSHOT:
|
|
510
|
-
if (!record.namespace || !record.workflow_name || !record.run_id) {
|
|
511
|
-
throw new Error("Namespace, workflow name, and run ID are required");
|
|
512
|
-
}
|
|
513
|
-
let key = `${prefix}${tableName}:${record.namespace}:${record.workflow_name}:${record.run_id}`;
|
|
514
|
-
if (record.resourceId) {
|
|
515
|
-
key = `${key}:${record.resourceId}`;
|
|
516
|
-
}
|
|
517
|
-
return key;
|
|
518
|
-
case TABLE_TRACES:
|
|
519
|
-
if (!record.id) throw new Error("Trace ID is required");
|
|
520
|
-
return `${prefix}${tableName}:${record.id}`;
|
|
521
|
-
default:
|
|
522
|
-
throw new Error(`Unsupported table: ${tableName}`);
|
|
1187
|
+
} catch (error) {
|
|
1188
|
+
if (error.message && error.message.includes("key not found")) {
|
|
1189
|
+
return null;
|
|
1190
|
+
}
|
|
1191
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1192
|
+
this.logger.error(`Failed to get value for ${tableName} ${key}:`, { message });
|
|
1193
|
+
throw error;
|
|
523
1194
|
}
|
|
524
1195
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
1196
|
+
async getKV(tableName, key) {
|
|
1197
|
+
try {
|
|
1198
|
+
const text = await this.getNamespaceValue(tableName, key);
|
|
1199
|
+
return this.safeParse(text);
|
|
1200
|
+
} catch (error) {
|
|
1201
|
+
this.logger.error(`Failed to get KV value for ${tableName}:${key}:`, error);
|
|
1202
|
+
throw new Error(`Failed to get KV value: ${error.message}`);
|
|
1203
|
+
}
|
|
528
1204
|
}
|
|
529
1205
|
async getTableSchema(tableName) {
|
|
530
1206
|
try {
|
|
@@ -605,7 +1281,7 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
605
1281
|
}
|
|
606
1282
|
break;
|
|
607
1283
|
case TABLE_WORKFLOW_SNAPSHOT:
|
|
608
|
-
if (!("
|
|
1284
|
+
if (!("workflow_name" in recordTyped) || !("run_id" in recordTyped)) {
|
|
609
1285
|
throw new Error("Workflow record missing required fields");
|
|
610
1286
|
}
|
|
611
1287
|
break;
|
|
@@ -614,6 +1290,16 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
614
1290
|
throw new Error("Trace record missing required fields");
|
|
615
1291
|
}
|
|
616
1292
|
break;
|
|
1293
|
+
case TABLE_EVALS:
|
|
1294
|
+
if (!("agent_name" in recordTyped) || !("run_id" in recordTyped)) {
|
|
1295
|
+
throw new Error("Eval record missing required fields");
|
|
1296
|
+
}
|
|
1297
|
+
break;
|
|
1298
|
+
case TABLE_SCORERS:
|
|
1299
|
+
if (!("id" in recordTyped) || !("scorerId" in recordTyped)) {
|
|
1300
|
+
throw new Error("Score record missing required fields");
|
|
1301
|
+
}
|
|
1302
|
+
break;
|
|
617
1303
|
default:
|
|
618
1304
|
throw new Error(`Unknown table type: ${tableName}`);
|
|
619
1305
|
}
|
|
@@ -623,26 +1309,21 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
623
1309
|
throw error;
|
|
624
1310
|
}
|
|
625
1311
|
}
|
|
626
|
-
|
|
627
|
-
if (!metadata) return {};
|
|
628
|
-
return typeof metadata === "string" ? JSON.parse(metadata) : metadata;
|
|
629
|
-
}
|
|
630
|
-
async createTable({
|
|
631
|
-
tableName,
|
|
632
|
-
schema
|
|
633
|
-
}) {
|
|
1312
|
+
async insert({ tableName, record }) {
|
|
634
1313
|
try {
|
|
635
|
-
const
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
1314
|
+
const key = this.getKey(tableName, record);
|
|
1315
|
+
const processedRecord = {
|
|
1316
|
+
...record,
|
|
1317
|
+
createdAt: record.createdAt ? serializeDate(record.createdAt) : void 0,
|
|
1318
|
+
updatedAt: record.updatedAt ? serializeDate(record.updatedAt) : void 0,
|
|
1319
|
+
metadata: record.metadata ? JSON.stringify(record.metadata) : ""
|
|
640
1320
|
};
|
|
641
|
-
await this.
|
|
1321
|
+
await this.validateRecord(processedRecord, tableName);
|
|
1322
|
+
await this.putKV({ tableName, key, value: processedRecord });
|
|
642
1323
|
} catch (error) {
|
|
643
1324
|
throw new MastraError(
|
|
644
1325
|
{
|
|
645
|
-
id: "
|
|
1326
|
+
id: "CLOUDFLARE_STORAGE_INSERT_FAILED",
|
|
646
1327
|
domain: ErrorDomain.STORAGE,
|
|
647
1328
|
category: ErrorCategory.THIRD_PARTY,
|
|
648
1329
|
details: {
|
|
@@ -653,24 +1334,26 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
653
1334
|
);
|
|
654
1335
|
}
|
|
655
1336
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
* @param schema Schema of the table
|
|
660
|
-
* @param ifNotExists Array of column names to add if they don't exist
|
|
661
|
-
*/
|
|
662
|
-
async alterTable(_args) {
|
|
1337
|
+
ensureMetadata(metadata) {
|
|
1338
|
+
if (!metadata) return {};
|
|
1339
|
+
return typeof metadata === "string" ? JSON.parse(metadata) : metadata;
|
|
663
1340
|
}
|
|
664
|
-
async
|
|
1341
|
+
async load({ tableName, keys }) {
|
|
665
1342
|
try {
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
1343
|
+
const key = this.getKey(tableName, keys);
|
|
1344
|
+
const data = await this.getKV(tableName, key);
|
|
1345
|
+
if (!data) return null;
|
|
1346
|
+
const processed = {
|
|
1347
|
+
...data,
|
|
1348
|
+
createdAt: ensureDate(data.createdAt),
|
|
1349
|
+
updatedAt: ensureDate(data.updatedAt),
|
|
1350
|
+
metadata: this.ensureMetadata(data.metadata)
|
|
1351
|
+
};
|
|
1352
|
+
return processed;
|
|
670
1353
|
} catch (error) {
|
|
671
|
-
|
|
1354
|
+
const mastraError = new MastraError(
|
|
672
1355
|
{
|
|
673
|
-
id: "
|
|
1356
|
+
id: "CLOUDFLARE_STORAGE_LOAD_FAILED",
|
|
674
1357
|
domain: ErrorDomain.STORAGE,
|
|
675
1358
|
category: ErrorCategory.THIRD_PARTY,
|
|
676
1359
|
details: {
|
|
@@ -679,52 +1362,102 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
679
1362
|
},
|
|
680
1363
|
error
|
|
681
1364
|
);
|
|
1365
|
+
this.logger?.trackException(mastraError);
|
|
1366
|
+
this.logger?.error(mastraError.toString());
|
|
1367
|
+
return null;
|
|
682
1368
|
}
|
|
683
1369
|
}
|
|
684
|
-
async
|
|
685
|
-
|
|
686
|
-
record
|
|
687
|
-
}) {
|
|
1370
|
+
async batchInsert(input) {
|
|
1371
|
+
if (!input.records || input.records.length === 0) return;
|
|
688
1372
|
try {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
1373
|
+
await Promise.all(
|
|
1374
|
+
input.records.map(async (record) => {
|
|
1375
|
+
const key = this.getKey(input.tableName, record);
|
|
1376
|
+
const processedRecord = {
|
|
1377
|
+
...record,
|
|
1378
|
+
createdAt: record.createdAt ? serializeDate(record.createdAt) : void 0,
|
|
1379
|
+
updatedAt: record.updatedAt ? serializeDate(record.updatedAt) : void 0,
|
|
1380
|
+
metadata: record.metadata ? JSON.stringify(record.metadata) : void 0
|
|
1381
|
+
};
|
|
1382
|
+
await this.putKV({ tableName: input.tableName, key, value: processedRecord });
|
|
1383
|
+
})
|
|
1384
|
+
);
|
|
698
1385
|
} catch (error) {
|
|
699
1386
|
throw new MastraError(
|
|
700
1387
|
{
|
|
701
|
-
id: "
|
|
1388
|
+
id: "CLOUDFLARE_STORAGE_BATCH_INSERT_FAILED",
|
|
702
1389
|
domain: ErrorDomain.STORAGE,
|
|
703
1390
|
category: ErrorCategory.THIRD_PARTY,
|
|
1391
|
+
text: `Error in batch insert for table ${input.tableName}`,
|
|
704
1392
|
details: {
|
|
705
|
-
tableName
|
|
1393
|
+
tableName: input.tableName
|
|
706
1394
|
}
|
|
707
1395
|
},
|
|
708
1396
|
error
|
|
709
1397
|
);
|
|
710
1398
|
}
|
|
711
1399
|
}
|
|
712
|
-
|
|
1400
|
+
/**
|
|
1401
|
+
* Helper to safely serialize data for KV storage
|
|
1402
|
+
*/
|
|
1403
|
+
safeSerialize(data) {
|
|
1404
|
+
return typeof data === "string" ? data : JSON.stringify(data);
|
|
1405
|
+
}
|
|
1406
|
+
async putNamespaceValue({
|
|
1407
|
+
tableName,
|
|
1408
|
+
key,
|
|
1409
|
+
value,
|
|
1410
|
+
metadata
|
|
1411
|
+
}) {
|
|
713
1412
|
try {
|
|
714
|
-
const
|
|
715
|
-
const
|
|
716
|
-
if (
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1413
|
+
const serializedValue = this.safeSerialize(value);
|
|
1414
|
+
const serializedMetadata = metadata ? this.safeSerialize(metadata) : "";
|
|
1415
|
+
if (this.bindings) {
|
|
1416
|
+
const binding = this.getBinding(tableName);
|
|
1417
|
+
await binding.put(key, serializedValue, { metadata: serializedMetadata });
|
|
1418
|
+
} else {
|
|
1419
|
+
const namespaceId = await this.getNamespaceId(tableName);
|
|
1420
|
+
await this.client.kv.namespaces.values.update(namespaceId, key, {
|
|
1421
|
+
account_id: this.accountId,
|
|
1422
|
+
value: serializedValue,
|
|
1423
|
+
metadata: serializedMetadata
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1428
|
+
this.logger.error(`Failed to put value for ${tableName} ${key}:`, { message });
|
|
1429
|
+
throw error;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
async putKV({
|
|
1433
|
+
tableName,
|
|
1434
|
+
key,
|
|
1435
|
+
value,
|
|
1436
|
+
metadata
|
|
1437
|
+
}) {
|
|
1438
|
+
try {
|
|
1439
|
+
await this.putNamespaceValue({ tableName, key, value, metadata });
|
|
1440
|
+
} catch (error) {
|
|
1441
|
+
this.logger.error(`Failed to put KV value for ${tableName}:${key}:`, error);
|
|
1442
|
+
throw new Error(`Failed to put KV value: ${error.message}`);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
async createTable({
|
|
1446
|
+
tableName,
|
|
1447
|
+
schema
|
|
1448
|
+
}) {
|
|
1449
|
+
try {
|
|
1450
|
+
const schemaKey = this.getSchemaKey(tableName);
|
|
1451
|
+
const metadata = {
|
|
1452
|
+
type: "table_schema",
|
|
1453
|
+
tableName,
|
|
1454
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
722
1455
|
};
|
|
723
|
-
|
|
1456
|
+
await this.putKV({ tableName, key: schemaKey, value: schema, metadata });
|
|
724
1457
|
} catch (error) {
|
|
725
|
-
|
|
1458
|
+
throw new MastraError(
|
|
726
1459
|
{
|
|
727
|
-
id: "
|
|
1460
|
+
id: "CLOUDFLARE_STORAGE_CREATE_TABLE_FAILED",
|
|
728
1461
|
domain: ErrorDomain.STORAGE,
|
|
729
1462
|
category: ErrorCategory.THIRD_PARTY,
|
|
730
1463
|
details: {
|
|
@@ -733,395 +1466,432 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
733
1466
|
},
|
|
734
1467
|
error
|
|
735
1468
|
);
|
|
736
|
-
this.logger?.trackException(mastraError);
|
|
737
|
-
this.logger?.error(mastraError.toString());
|
|
738
|
-
return null;
|
|
739
1469
|
}
|
|
740
1470
|
}
|
|
741
|
-
async
|
|
742
|
-
const thread = await this.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
|
|
743
|
-
if (!thread) return null;
|
|
1471
|
+
async listNamespaceKeys(tableName, options) {
|
|
744
1472
|
try {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
1473
|
+
if (this.bindings) {
|
|
1474
|
+
const binding = this.getBinding(tableName);
|
|
1475
|
+
const response = await binding.list({
|
|
1476
|
+
limit: options?.limit || 1e3,
|
|
1477
|
+
prefix: options?.prefix
|
|
1478
|
+
});
|
|
1479
|
+
return response.keys;
|
|
1480
|
+
} else {
|
|
1481
|
+
const namespaceId = await this.getNamespaceId(tableName);
|
|
1482
|
+
const response = await this.client.kv.namespaces.keys.list(namespaceId, {
|
|
1483
|
+
account_id: this.accountId,
|
|
1484
|
+
limit: options?.limit || 1e3,
|
|
1485
|
+
prefix: options?.prefix
|
|
1486
|
+
});
|
|
1487
|
+
return response.result;
|
|
1488
|
+
}
|
|
751
1489
|
} catch (error) {
|
|
752
|
-
|
|
1490
|
+
throw new MastraError(
|
|
753
1491
|
{
|
|
754
|
-
id: "
|
|
1492
|
+
id: "CLOUDFLARE_STORAGE_LIST_NAMESPACE_KEYS_FAILED",
|
|
755
1493
|
domain: ErrorDomain.STORAGE,
|
|
756
1494
|
category: ErrorCategory.THIRD_PARTY,
|
|
757
1495
|
details: {
|
|
758
|
-
|
|
1496
|
+
tableName
|
|
759
1497
|
}
|
|
760
1498
|
},
|
|
761
1499
|
error
|
|
762
1500
|
);
|
|
763
|
-
this.logger?.trackException(mastraError);
|
|
764
|
-
this.logger?.error(mastraError.toString());
|
|
765
|
-
return null;
|
|
766
1501
|
}
|
|
767
1502
|
}
|
|
768
|
-
async
|
|
1503
|
+
async deleteNamespaceValue(tableName, key) {
|
|
1504
|
+
if (this.bindings) {
|
|
1505
|
+
const binding = this.getBinding(tableName);
|
|
1506
|
+
await binding.delete(key);
|
|
1507
|
+
} else {
|
|
1508
|
+
const namespaceId = await this.getNamespaceId(tableName);
|
|
1509
|
+
await this.client.kv.namespaces.values.delete(namespaceId, key, {
|
|
1510
|
+
account_id: this.accountId
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
async deleteKV(tableName, key) {
|
|
769
1515
|
try {
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
1516
|
+
await this.deleteNamespaceValue(tableName, key);
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
this.logger.error(`Failed to delete KV value for ${tableName}:${key}:`, error);
|
|
1519
|
+
throw new Error(`Failed to delete KV value: ${error.message}`);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
async listKV(tableName, options) {
|
|
1523
|
+
try {
|
|
1524
|
+
return await this.listNamespaceKeys(tableName, options);
|
|
1525
|
+
} catch (error) {
|
|
1526
|
+
this.logger.error(`Failed to list KV for ${tableName}:`, error);
|
|
1527
|
+
throw new Error(`Failed to list KV: ${error.message}`);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
};
|
|
1531
|
+
function transformScoreRow(row) {
|
|
1532
|
+
let input = void 0;
|
|
1533
|
+
if (row.input) {
|
|
1534
|
+
try {
|
|
1535
|
+
input = JSON.parse(row.input);
|
|
1536
|
+
} catch {
|
|
1537
|
+
input = row.input;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
return {
|
|
1541
|
+
...row,
|
|
1542
|
+
input
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
var ScoresStorageCloudflare = class extends ScoresStorage {
|
|
1546
|
+
operations;
|
|
1547
|
+
constructor({ operations }) {
|
|
1548
|
+
super();
|
|
1549
|
+
this.operations = operations;
|
|
1550
|
+
}
|
|
1551
|
+
async getScoreById({ id }) {
|
|
1552
|
+
try {
|
|
1553
|
+
const score = await this.operations.getKV(TABLE_SCORERS, id);
|
|
1554
|
+
if (!score) {
|
|
1555
|
+
return null;
|
|
1556
|
+
}
|
|
1557
|
+
return transformScoreRow(score);
|
|
803
1558
|
} catch (error) {
|
|
804
1559
|
const mastraError = new MastraError(
|
|
805
1560
|
{
|
|
806
|
-
id: "
|
|
1561
|
+
id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORE_BY_ID_FAILED",
|
|
807
1562
|
domain: ErrorDomain.STORAGE,
|
|
808
1563
|
category: ErrorCategory.THIRD_PARTY,
|
|
809
|
-
|
|
810
|
-
resourceId
|
|
811
|
-
}
|
|
1564
|
+
text: `Failed to get score by id: ${id}`
|
|
812
1565
|
},
|
|
813
1566
|
error
|
|
814
1567
|
);
|
|
815
|
-
this.logger
|
|
816
|
-
this.logger
|
|
817
|
-
return
|
|
1568
|
+
this.logger.trackException(mastraError);
|
|
1569
|
+
this.logger.error(mastraError.toString());
|
|
1570
|
+
return null;
|
|
818
1571
|
}
|
|
819
1572
|
}
|
|
820
|
-
async
|
|
1573
|
+
async saveScore(score) {
|
|
821
1574
|
try {
|
|
822
|
-
|
|
823
|
-
|
|
1575
|
+
const { input, ...rest } = score;
|
|
1576
|
+
const serializedRecord = {};
|
|
1577
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
1578
|
+
if (value !== null && value !== void 0) {
|
|
1579
|
+
if (typeof value === "object") {
|
|
1580
|
+
serializedRecord[key] = JSON.stringify(value);
|
|
1581
|
+
} else {
|
|
1582
|
+
serializedRecord[key] = value;
|
|
1583
|
+
}
|
|
1584
|
+
} else {
|
|
1585
|
+
serializedRecord[key] = null;
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
serializedRecord.input = JSON.stringify(input);
|
|
1589
|
+
serializedRecord.createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1590
|
+
serializedRecord.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1591
|
+
await this.operations.putKV({
|
|
1592
|
+
tableName: TABLE_SCORERS,
|
|
1593
|
+
key: score.id,
|
|
1594
|
+
value: serializedRecord
|
|
1595
|
+
});
|
|
1596
|
+
const scoreFromDb = await this.getScoreById({ id: score.id });
|
|
1597
|
+
return { score: scoreFromDb };
|
|
824
1598
|
} catch (error) {
|
|
825
|
-
|
|
1599
|
+
const mastraError = new MastraError(
|
|
826
1600
|
{
|
|
827
|
-
id: "
|
|
1601
|
+
id: "CLOUDFLARE_STORAGE_SCORES_SAVE_SCORE_FAILED",
|
|
828
1602
|
domain: ErrorDomain.STORAGE,
|
|
829
1603
|
category: ErrorCategory.THIRD_PARTY,
|
|
830
|
-
|
|
831
|
-
threadId: thread.id
|
|
832
|
-
}
|
|
1604
|
+
text: `Failed to save score: ${score.id}`
|
|
833
1605
|
},
|
|
834
1606
|
error
|
|
835
1607
|
);
|
|
1608
|
+
this.logger.trackException(mastraError);
|
|
1609
|
+
this.logger.error(mastraError.toString());
|
|
1610
|
+
throw mastraError;
|
|
836
1611
|
}
|
|
837
1612
|
}
|
|
838
|
-
async
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
metadata
|
|
1613
|
+
async getScoresByScorerId({
|
|
1614
|
+
scorerId,
|
|
1615
|
+
pagination
|
|
842
1616
|
}) {
|
|
843
1617
|
try {
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
|
|
1618
|
+
const keys = await this.operations.listKV(TABLE_SCORERS);
|
|
1619
|
+
const scores = [];
|
|
1620
|
+
for (const { name: key } of keys) {
|
|
1621
|
+
const score = await this.operations.getKV(TABLE_SCORERS, key);
|
|
1622
|
+
if (score && score.scorerId === scorerId) {
|
|
1623
|
+
scores.push(transformScoreRow(score));
|
|
1624
|
+
}
|
|
847
1625
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1626
|
+
scores.sort((a, b) => {
|
|
1627
|
+
const dateA = new Date(a.createdAt || 0).getTime();
|
|
1628
|
+
const dateB = new Date(b.createdAt || 0).getTime();
|
|
1629
|
+
return dateB - dateA;
|
|
1630
|
+
});
|
|
1631
|
+
const total = scores.length;
|
|
1632
|
+
const start = pagination.page * pagination.perPage;
|
|
1633
|
+
const end = start + pagination.perPage;
|
|
1634
|
+
const pagedScores = scores.slice(start, end);
|
|
1635
|
+
return {
|
|
1636
|
+
pagination: {
|
|
1637
|
+
total,
|
|
1638
|
+
page: pagination.page,
|
|
1639
|
+
perPage: pagination.perPage,
|
|
1640
|
+
hasMore: end < total
|
|
1641
|
+
},
|
|
1642
|
+
scores: pagedScores
|
|
856
1643
|
};
|
|
857
|
-
await this.insert({ tableName: TABLE_THREADS, record: updatedThread });
|
|
858
|
-
return updatedThread;
|
|
859
1644
|
} catch (error) {
|
|
860
|
-
|
|
1645
|
+
const mastraError = new MastraError(
|
|
861
1646
|
{
|
|
862
|
-
id: "
|
|
1647
|
+
id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_SCORER_ID_FAILED",
|
|
863
1648
|
domain: ErrorDomain.STORAGE,
|
|
864
1649
|
category: ErrorCategory.THIRD_PARTY,
|
|
865
|
-
|
|
866
|
-
threadId: id,
|
|
867
|
-
title
|
|
868
|
-
}
|
|
1650
|
+
text: `Failed to get scores by scorer id: ${scorerId}`
|
|
869
1651
|
},
|
|
870
1652
|
error
|
|
871
1653
|
);
|
|
1654
|
+
this.logger?.trackException(mastraError);
|
|
1655
|
+
this.logger?.error(mastraError.toString());
|
|
1656
|
+
return { pagination: { total: 0, page: 0, perPage: 100, hasMore: false }, scores: [] };
|
|
872
1657
|
}
|
|
873
1658
|
}
|
|
874
|
-
async
|
|
1659
|
+
async getScoresByRunId({
|
|
1660
|
+
runId,
|
|
1661
|
+
pagination
|
|
1662
|
+
}) {
|
|
875
1663
|
try {
|
|
876
|
-
const
|
|
877
|
-
|
|
878
|
-
|
|
1664
|
+
const keys = await this.operations.listKV(TABLE_SCORERS);
|
|
1665
|
+
const scores = [];
|
|
1666
|
+
for (const { name: key } of keys) {
|
|
1667
|
+
const score = await this.operations.getKV(TABLE_SCORERS, key);
|
|
1668
|
+
if (score && score.runId === runId) {
|
|
1669
|
+
scores.push(transformScoreRow(score));
|
|
1670
|
+
}
|
|
879
1671
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1672
|
+
scores.sort((a, b) => {
|
|
1673
|
+
const dateA = new Date(a.createdAt || 0).getTime();
|
|
1674
|
+
const dateB = new Date(b.createdAt || 0).getTime();
|
|
1675
|
+
return dateB - dateA;
|
|
1676
|
+
});
|
|
1677
|
+
const total = scores.length;
|
|
1678
|
+
const start = pagination.page * pagination.perPage;
|
|
1679
|
+
const end = start + pagination.perPage;
|
|
1680
|
+
const pagedScores = scores.slice(start, end);
|
|
1681
|
+
return {
|
|
1682
|
+
pagination: {
|
|
1683
|
+
total,
|
|
1684
|
+
page: pagination.page,
|
|
1685
|
+
perPage: pagination.perPage,
|
|
1686
|
+
hasMore: end < total
|
|
1687
|
+
},
|
|
1688
|
+
scores: pagedScores
|
|
1689
|
+
};
|
|
890
1690
|
} catch (error) {
|
|
891
|
-
|
|
1691
|
+
const mastraError = new MastraError(
|
|
892
1692
|
{
|
|
893
|
-
id: "
|
|
1693
|
+
id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_RUN_ID_FAILED",
|
|
894
1694
|
domain: ErrorDomain.STORAGE,
|
|
895
1695
|
category: ErrorCategory.THIRD_PARTY,
|
|
896
|
-
|
|
897
|
-
threadId
|
|
898
|
-
}
|
|
1696
|
+
text: `Failed to get scores by run id: ${runId}`
|
|
899
1697
|
},
|
|
900
1698
|
error
|
|
901
1699
|
);
|
|
1700
|
+
this.logger.trackException(mastraError);
|
|
1701
|
+
this.logger.error(mastraError.toString());
|
|
1702
|
+
return { pagination: { total: 0, page: 0, perPage: 100, hasMore: false }, scores: [] };
|
|
902
1703
|
}
|
|
903
1704
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
this.logger.error(`Error getting message key for thread ${threadId} and message ${messageId}:`, { message });
|
|
910
|
-
throw error;
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
getThreadMessagesKey(threadId) {
|
|
1705
|
+
async getScoresByEntityId({
|
|
1706
|
+
entityId,
|
|
1707
|
+
entityType,
|
|
1708
|
+
pagination
|
|
1709
|
+
}) {
|
|
914
1710
|
try {
|
|
915
|
-
|
|
1711
|
+
const keys = await this.operations.listKV(TABLE_SCORERS);
|
|
1712
|
+
const scores = [];
|
|
1713
|
+
for (const { name: key } of keys) {
|
|
1714
|
+
const score = await this.operations.getKV(TABLE_SCORERS, key);
|
|
1715
|
+
if (score && score.entityId === entityId && score.entityType === entityType) {
|
|
1716
|
+
scores.push(transformScoreRow(score));
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
scores.sort((a, b) => {
|
|
1720
|
+
const dateA = new Date(a.createdAt || 0).getTime();
|
|
1721
|
+
const dateB = new Date(b.createdAt || 0).getTime();
|
|
1722
|
+
return dateB - dateA;
|
|
1723
|
+
});
|
|
1724
|
+
const total = scores.length;
|
|
1725
|
+
const start = pagination.page * pagination.perPage;
|
|
1726
|
+
const end = start + pagination.perPage;
|
|
1727
|
+
const pagedScores = scores.slice(start, end);
|
|
1728
|
+
return {
|
|
1729
|
+
pagination: {
|
|
1730
|
+
total,
|
|
1731
|
+
page: pagination.page,
|
|
1732
|
+
perPage: pagination.perPage,
|
|
1733
|
+
hasMore: end < total
|
|
1734
|
+
},
|
|
1735
|
+
scores: pagedScores
|
|
1736
|
+
};
|
|
916
1737
|
} catch (error) {
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
|
|
1738
|
+
const mastraError = new MastraError(
|
|
1739
|
+
{
|
|
1740
|
+
id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_ENTITY_ID_FAILED",
|
|
1741
|
+
domain: ErrorDomain.STORAGE,
|
|
1742
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1743
|
+
text: `Failed to get scores by entity id: ${entityId}, type: ${entityType}`
|
|
1744
|
+
},
|
|
1745
|
+
error
|
|
1746
|
+
);
|
|
1747
|
+
this.logger.trackException(mastraError);
|
|
1748
|
+
this.logger.error(mastraError.toString());
|
|
1749
|
+
return { pagination: { total: 0, page: 0, perPage: 100, hasMore: false }, scores: [] };
|
|
920
1750
|
}
|
|
921
1751
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1752
|
+
};
|
|
1753
|
+
var TracesStorageCloudflare = class extends TracesStorage {
|
|
1754
|
+
operations;
|
|
1755
|
+
constructor({ operations }) {
|
|
1756
|
+
super();
|
|
1757
|
+
this.operations = operations;
|
|
1758
|
+
}
|
|
1759
|
+
async getTraces(args) {
|
|
1760
|
+
const paginatedArgs = {
|
|
1761
|
+
name: args.name,
|
|
1762
|
+
scope: args.scope,
|
|
1763
|
+
page: args.page,
|
|
1764
|
+
perPage: args.perPage,
|
|
1765
|
+
attributes: args.attributes,
|
|
1766
|
+
filters: args.filters,
|
|
1767
|
+
dateRange: args.fromDate || args.toDate ? {
|
|
1768
|
+
start: args.fromDate,
|
|
1769
|
+
end: args.toDate
|
|
1770
|
+
} : void 0
|
|
1771
|
+
};
|
|
925
1772
|
try {
|
|
926
|
-
const
|
|
927
|
-
|
|
928
|
-
if (!message.id) errors.push("id is required");
|
|
929
|
-
if (!message.threadId) errors.push("threadId is required");
|
|
930
|
-
if (!message.content) errors.push("content is required");
|
|
931
|
-
if (!message.role) errors.push("role is required");
|
|
932
|
-
if (!message.createdAt) errors.push("createdAt is required");
|
|
933
|
-
if (errors.length > 0) {
|
|
934
|
-
throw new Error(`Invalid message at index ${index}: ${errors.join(", ")}`);
|
|
935
|
-
}
|
|
936
|
-
return {
|
|
937
|
-
...message,
|
|
938
|
-
createdAt: this.ensureDate(message.createdAt),
|
|
939
|
-
type: message.type || "v2",
|
|
940
|
-
_index: index
|
|
941
|
-
};
|
|
942
|
-
}).filter((m) => !!m);
|
|
943
|
-
const messagesByThread = validatedMessages.reduce((acc, message) => {
|
|
944
|
-
if (message.threadId && !acc.has(message.threadId)) {
|
|
945
|
-
acc.set(message.threadId, []);
|
|
946
|
-
}
|
|
947
|
-
if (message.threadId) {
|
|
948
|
-
acc.get(message.threadId).push(message);
|
|
949
|
-
}
|
|
950
|
-
return acc;
|
|
951
|
-
}, /* @__PURE__ */ new Map());
|
|
952
|
-
await Promise.all(
|
|
953
|
-
Array.from(messagesByThread.entries()).map(async ([threadId, threadMessages]) => {
|
|
954
|
-
try {
|
|
955
|
-
const thread = await this.getThreadById({ threadId });
|
|
956
|
-
if (!thread) {
|
|
957
|
-
throw new Error(`Thread ${threadId} not found`);
|
|
958
|
-
}
|
|
959
|
-
await Promise.all(
|
|
960
|
-
threadMessages.map(async (message) => {
|
|
961
|
-
const key = this.getMessageKey(threadId, message.id);
|
|
962
|
-
const { _index, ...cleanMessage } = message;
|
|
963
|
-
const serializedMessage = {
|
|
964
|
-
...cleanMessage,
|
|
965
|
-
createdAt: this.serializeDate(cleanMessage.createdAt)
|
|
966
|
-
};
|
|
967
|
-
await this.putKV({ tableName: TABLE_MESSAGES, key, value: serializedMessage });
|
|
968
|
-
})
|
|
969
|
-
);
|
|
970
|
-
const orderKey = this.getThreadMessagesKey(threadId);
|
|
971
|
-
const entries = await this.updateSorting(threadMessages);
|
|
972
|
-
await this.updateSortedMessages(orderKey, entries);
|
|
973
|
-
} catch (error) {
|
|
974
|
-
throw new MastraError(
|
|
975
|
-
{
|
|
976
|
-
id: "CLOUDFLARE_STORAGE_SAVE_MESSAGES_FAILED",
|
|
977
|
-
domain: ErrorDomain.STORAGE,
|
|
978
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
979
|
-
details: {
|
|
980
|
-
threadId
|
|
981
|
-
}
|
|
982
|
-
},
|
|
983
|
-
error
|
|
984
|
-
);
|
|
985
|
-
}
|
|
986
|
-
})
|
|
987
|
-
);
|
|
988
|
-
const prepared = validatedMessages.map(
|
|
989
|
-
({ _index, ...message }) => ({ ...message, type: message.type !== "v2" ? message.type : void 0 })
|
|
990
|
-
);
|
|
991
|
-
const list = new MessageList().add(prepared, "memory");
|
|
992
|
-
if (format === `v2`) return list.get.all.v2();
|
|
993
|
-
return list.get.all.v1();
|
|
1773
|
+
const result = await this.getTracesPaginated(paginatedArgs);
|
|
1774
|
+
return result.traces;
|
|
994
1775
|
} catch (error) {
|
|
995
1776
|
throw new MastraError(
|
|
996
1777
|
{
|
|
997
|
-
id: "
|
|
1778
|
+
id: "CLOUDFLARE_STORAGE_GET_TRACES_ERROR",
|
|
998
1779
|
domain: ErrorDomain.STORAGE,
|
|
999
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1780
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1781
|
+
text: `Failed to retrieve traces: ${error instanceof Error ? error.message : String(error)}`,
|
|
1782
|
+
details: {
|
|
1783
|
+
name: args.name ?? "",
|
|
1784
|
+
scope: args.scope ?? ""
|
|
1785
|
+
}
|
|
1000
1786
|
},
|
|
1001
1787
|
error
|
|
1002
1788
|
);
|
|
1003
1789
|
}
|
|
1004
1790
|
}
|
|
1005
|
-
async
|
|
1006
|
-
threadId,
|
|
1007
|
-
resourceId,
|
|
1008
|
-
selectBy,
|
|
1009
|
-
format
|
|
1010
|
-
}) {
|
|
1011
|
-
if (!threadId) throw new Error("threadId is required");
|
|
1012
|
-
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
1013
|
-
const messageIds = /* @__PURE__ */ new Set();
|
|
1014
|
-
if (limit === 0 && !selectBy?.include?.length) return [];
|
|
1791
|
+
async getTracesPaginated(args) {
|
|
1015
1792
|
try {
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
]
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
{
|
|
1035
|
-
id: "CLOUDFLARE_STORAGE_SORT_MESSAGES_FAILED",
|
|
1036
|
-
domain: ErrorDomain.STORAGE,
|
|
1037
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1038
|
-
text: `Error sorting messages for thread ${threadId} falling back to creation time`,
|
|
1039
|
-
details: {
|
|
1040
|
-
threadId
|
|
1793
|
+
const { name, scope, attributes, filters, page = 0, perPage = 100, dateRange } = args;
|
|
1794
|
+
const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
|
|
1795
|
+
const keyObjs = await this.operations.listKV(TABLE_TRACES, { prefix: `${prefix}${TABLE_TRACES}` });
|
|
1796
|
+
const traces = [];
|
|
1797
|
+
for (const { name: key } of keyObjs) {
|
|
1798
|
+
try {
|
|
1799
|
+
const data = await this.operations.getKV(TABLE_TRACES, key);
|
|
1800
|
+
if (!data) continue;
|
|
1801
|
+
if (name && data.name !== name) continue;
|
|
1802
|
+
if (scope && data.scope !== scope) continue;
|
|
1803
|
+
if (attributes) {
|
|
1804
|
+
const dataAttributes = data.attributes || {};
|
|
1805
|
+
let shouldSkip = false;
|
|
1806
|
+
for (const [key2, value] of Object.entries(attributes)) {
|
|
1807
|
+
if (dataAttributes[key2] !== value) {
|
|
1808
|
+
shouldSkip = true;
|
|
1809
|
+
break;
|
|
1810
|
+
}
|
|
1041
1811
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1812
|
+
if (shouldSkip) continue;
|
|
1813
|
+
}
|
|
1814
|
+
if (dateRange?.start || dateRange?.end) {
|
|
1815
|
+
const traceDate = new Date(data.createdAt || 0);
|
|
1816
|
+
if (dateRange.start && traceDate < dateRange.start) continue;
|
|
1817
|
+
if (dateRange.end && traceDate > dateRange.end) continue;
|
|
1818
|
+
}
|
|
1819
|
+
if (filters) {
|
|
1820
|
+
let shouldSkip = false;
|
|
1821
|
+
for (const [key2, value] of Object.entries(filters)) {
|
|
1822
|
+
if (data[key2] !== value) {
|
|
1823
|
+
shouldSkip = true;
|
|
1824
|
+
break;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
if (shouldSkip) continue;
|
|
1828
|
+
}
|
|
1829
|
+
traces.push(data);
|
|
1830
|
+
} catch (err) {
|
|
1831
|
+
this.logger.error("Failed to parse trace:", { key, error: err });
|
|
1832
|
+
}
|
|
1048
1833
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
})
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1834
|
+
traces.sort((a, b) => {
|
|
1835
|
+
const aTime = new Date(a.createdAt || 0).getTime();
|
|
1836
|
+
const bTime = new Date(b.createdAt || 0).getTime();
|
|
1837
|
+
return bTime - aTime;
|
|
1838
|
+
});
|
|
1839
|
+
const total = traces.length;
|
|
1840
|
+
const start = page * perPage;
|
|
1841
|
+
const end = start + perPage;
|
|
1842
|
+
const pagedTraces = traces.slice(start, end);
|
|
1843
|
+
return {
|
|
1844
|
+
traces: pagedTraces,
|
|
1845
|
+
total,
|
|
1846
|
+
page,
|
|
1847
|
+
perPage,
|
|
1848
|
+
hasMore: end < total
|
|
1849
|
+
};
|
|
1057
1850
|
} catch (error) {
|
|
1058
1851
|
const mastraError = new MastraError(
|
|
1059
1852
|
{
|
|
1060
|
-
id: "
|
|
1853
|
+
id: "CLOUDFLARE_STORAGE_GET_TRACES_PAGINATED_FAILED",
|
|
1061
1854
|
domain: ErrorDomain.STORAGE,
|
|
1062
1855
|
category: ErrorCategory.THIRD_PARTY,
|
|
1063
|
-
text:
|
|
1064
|
-
details: {
|
|
1065
|
-
threadId
|
|
1066
|
-
}
|
|
1856
|
+
text: "Error getting traces with pagination"
|
|
1067
1857
|
},
|
|
1068
1858
|
error
|
|
1069
1859
|
);
|
|
1070
|
-
this.logger?.
|
|
1071
|
-
this.logger
|
|
1072
|
-
return [];
|
|
1860
|
+
this.logger.trackException?.(mastraError);
|
|
1861
|
+
this.logger.error(mastraError.toString());
|
|
1862
|
+
return { traces: [], total: 0, page: 0, perPage: 100, hasMore: false };
|
|
1073
1863
|
}
|
|
1074
1864
|
}
|
|
1865
|
+
async batchTraceInsert({ records }) {
|
|
1866
|
+
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
1867
|
+
await this.operations.batchInsert({
|
|
1868
|
+
tableName: TABLE_TRACES,
|
|
1869
|
+
records
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
var WorkflowsStorageCloudflare = class extends WorkflowsStorage {
|
|
1874
|
+
operations;
|
|
1875
|
+
constructor({ operations }) {
|
|
1876
|
+
super();
|
|
1877
|
+
this.operations = operations;
|
|
1878
|
+
}
|
|
1075
1879
|
validateWorkflowParams(params) {
|
|
1076
|
-
const {
|
|
1077
|
-
if (!
|
|
1880
|
+
const { workflowName, runId } = params;
|
|
1881
|
+
if (!workflowName || !runId) {
|
|
1078
1882
|
throw new Error("Invalid workflow snapshot parameters");
|
|
1079
1883
|
}
|
|
1080
1884
|
}
|
|
1081
|
-
validateWorkflowState(state) {
|
|
1082
|
-
if (!state?.runId || !state?.value || !state?.context?.input || !state?.activePaths) {
|
|
1083
|
-
throw new Error("Invalid workflow state structure");
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
normalizeSteps(steps) {
|
|
1087
|
-
const normalizedSteps = {};
|
|
1088
|
-
for (const [stepId, step] of Object.entries(steps)) {
|
|
1089
|
-
normalizedSteps[stepId] = {
|
|
1090
|
-
status: step.status,
|
|
1091
|
-
payload: step.payload || step.result,
|
|
1092
|
-
error: step.error
|
|
1093
|
-
};
|
|
1094
|
-
}
|
|
1095
|
-
return normalizedSteps;
|
|
1096
|
-
}
|
|
1097
|
-
normalizeWorkflowState(data) {
|
|
1098
|
-
return {
|
|
1099
|
-
runId: data.runId,
|
|
1100
|
-
value: data.value,
|
|
1101
|
-
context: data.context,
|
|
1102
|
-
serializedStepGraph: data.serializedStepGraph,
|
|
1103
|
-
suspendedPaths: data.suspendedPaths || {},
|
|
1104
|
-
activePaths: data.activePaths || [],
|
|
1105
|
-
timestamp: data.timestamp || Date.now(),
|
|
1106
|
-
status: data.status,
|
|
1107
|
-
result: data.result,
|
|
1108
|
-
error: data.error,
|
|
1109
|
-
runtimeContext: data.runtimeContext || {}
|
|
1110
|
-
};
|
|
1111
|
-
}
|
|
1112
1885
|
async persistWorkflowSnapshot(params) {
|
|
1113
1886
|
try {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
const normalizedState = this.normalizeWorkflowState(snapshot);
|
|
1117
|
-
this.validateWorkflowState(normalizedState);
|
|
1118
|
-
await this.insert({
|
|
1887
|
+
const { workflowName, runId, snapshot } = params;
|
|
1888
|
+
await this.operations.putKV({
|
|
1119
1889
|
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
1120
|
-
|
|
1121
|
-
|
|
1890
|
+
key: this.operations.getKey(TABLE_WORKFLOW_SNAPSHOT, { workflow_name: workflowName, run_id: runId }),
|
|
1891
|
+
value: {
|
|
1122
1892
|
workflow_name: workflowName,
|
|
1123
1893
|
run_id: runId,
|
|
1124
|
-
snapshot:
|
|
1894
|
+
snapshot: typeof snapshot === "string" ? snapshot : JSON.stringify(snapshot),
|
|
1125
1895
|
createdAt: /* @__PURE__ */ new Date(),
|
|
1126
1896
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1127
1897
|
}
|
|
@@ -1132,9 +1902,8 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
1132
1902
|
id: "CLOUDFLARE_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
|
|
1133
1903
|
domain: ErrorDomain.STORAGE,
|
|
1134
1904
|
category: ErrorCategory.THIRD_PARTY,
|
|
1135
|
-
text: `Error persisting workflow snapshot for
|
|
1905
|
+
text: `Error persisting workflow snapshot for workflow ${params.workflowName}, run ${params.runId}`,
|
|
1136
1906
|
details: {
|
|
1137
|
-
namespace: params.namespace,
|
|
1138
1907
|
workflowName: params.workflowName,
|
|
1139
1908
|
runId: params.runId
|
|
1140
1909
|
}
|
|
@@ -1146,172 +1915,31 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
1146
1915
|
async loadWorkflowSnapshot(params) {
|
|
1147
1916
|
try {
|
|
1148
1917
|
this.validateWorkflowParams(params);
|
|
1149
|
-
const {
|
|
1150
|
-
const key = this.getKey(TABLE_WORKFLOW_SNAPSHOT, {
|
|
1151
|
-
const data = await this.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
|
|
1918
|
+
const { workflowName, runId } = params;
|
|
1919
|
+
const key = this.operations.getKey(TABLE_WORKFLOW_SNAPSHOT, { workflow_name: workflowName, run_id: runId });
|
|
1920
|
+
const data = await this.operations.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
|
|
1152
1921
|
if (!data) return null;
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1155
|
-
return state;
|
|
1922
|
+
const snapshotData = typeof data.snapshot === "string" ? JSON.parse(data.snapshot) : data.snapshot;
|
|
1923
|
+
return snapshotData;
|
|
1156
1924
|
} catch (error) {
|
|
1157
1925
|
const mastraError = new MastraError(
|
|
1158
1926
|
{
|
|
1159
1927
|
id: "CLOUDFLARE_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
|
|
1160
1928
|
domain: ErrorDomain.STORAGE,
|
|
1161
1929
|
category: ErrorCategory.THIRD_PARTY,
|
|
1162
|
-
text: `Error loading workflow snapshot for
|
|
1930
|
+
text: `Error loading workflow snapshot for workflow ${params.workflowName}, run ${params.runId}`,
|
|
1163
1931
|
details: {
|
|
1164
|
-
namespace: params.namespace,
|
|
1165
1932
|
workflowName: params.workflowName,
|
|
1166
1933
|
runId: params.runId
|
|
1167
1934
|
}
|
|
1168
1935
|
},
|
|
1169
1936
|
error
|
|
1170
1937
|
);
|
|
1171
|
-
this.logger?.
|
|
1172
|
-
this.logger
|
|
1938
|
+
this.logger.trackException?.(mastraError);
|
|
1939
|
+
this.logger.error(mastraError.toString());
|
|
1173
1940
|
return null;
|
|
1174
1941
|
}
|
|
1175
1942
|
}
|
|
1176
|
-
async batchInsert(input) {
|
|
1177
|
-
if (!input.records || input.records.length === 0) return;
|
|
1178
|
-
try {
|
|
1179
|
-
await Promise.all(
|
|
1180
|
-
input.records.map(async (record) => {
|
|
1181
|
-
const key = this.getKey(input.tableName, record);
|
|
1182
|
-
const processedRecord = {
|
|
1183
|
-
...record,
|
|
1184
|
-
createdAt: record.createdAt ? this.serializeDate(record.createdAt) : void 0,
|
|
1185
|
-
updatedAt: record.updatedAt ? this.serializeDate(record.updatedAt) : void 0,
|
|
1186
|
-
metadata: record.metadata ? JSON.stringify(record.metadata) : void 0
|
|
1187
|
-
};
|
|
1188
|
-
await this.putKV({ tableName: input.tableName, key, value: processedRecord });
|
|
1189
|
-
})
|
|
1190
|
-
);
|
|
1191
|
-
} catch (error) {
|
|
1192
|
-
throw new MastraError(
|
|
1193
|
-
{
|
|
1194
|
-
id: "CLOUDFLARE_STORAGE_BATCH_INSERT_FAILED",
|
|
1195
|
-
domain: ErrorDomain.STORAGE,
|
|
1196
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1197
|
-
text: `Error in batch insert for table ${input.tableName}`,
|
|
1198
|
-
details: {
|
|
1199
|
-
tableName: input.tableName
|
|
1200
|
-
}
|
|
1201
|
-
},
|
|
1202
|
-
error
|
|
1203
|
-
);
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
async getTraces({
|
|
1207
|
-
name,
|
|
1208
|
-
scope,
|
|
1209
|
-
page = 0,
|
|
1210
|
-
perPage = 100,
|
|
1211
|
-
attributes,
|
|
1212
|
-
fromDate,
|
|
1213
|
-
toDate
|
|
1214
|
-
}) {
|
|
1215
|
-
try {
|
|
1216
|
-
let keys;
|
|
1217
|
-
if (this.bindings) {
|
|
1218
|
-
keys = (await this.listKV(TABLE_TRACES))?.map((k) => k.name) || [];
|
|
1219
|
-
} else {
|
|
1220
|
-
const namespaceId = await this.getNamespaceId(TABLE_TRACES);
|
|
1221
|
-
const result = await this.client.kv.namespaces.keys.list(namespaceId, {
|
|
1222
|
-
prefix: "",
|
|
1223
|
-
limit: 1e3,
|
|
1224
|
-
account_id: this.accountId
|
|
1225
|
-
});
|
|
1226
|
-
keys = result.result?.map((k) => k.name) || [];
|
|
1227
|
-
}
|
|
1228
|
-
const traceRecords = await Promise.all(
|
|
1229
|
-
keys.map(async (key) => {
|
|
1230
|
-
const record = await this.getKV(TABLE_TRACES, key);
|
|
1231
|
-
if (!record) return null;
|
|
1232
|
-
return record;
|
|
1233
|
-
})
|
|
1234
|
-
);
|
|
1235
|
-
let filteredTraces = traceRecords.filter(
|
|
1236
|
-
(record) => record !== null && typeof record === "object"
|
|
1237
|
-
);
|
|
1238
|
-
if (name) {
|
|
1239
|
-
filteredTraces = filteredTraces.filter((record) => record.name?.toLowerCase().startsWith(name.toLowerCase()));
|
|
1240
|
-
}
|
|
1241
|
-
if (scope) {
|
|
1242
|
-
filteredTraces = filteredTraces.filter((record) => record.scope === scope);
|
|
1243
|
-
}
|
|
1244
|
-
if (attributes) {
|
|
1245
|
-
filteredTraces = filteredTraces.filter((record) => {
|
|
1246
|
-
if (!record.attributes) return false;
|
|
1247
|
-
const recordAttrs = this.parseJSON(record.attributes);
|
|
1248
|
-
if (!recordAttrs) return false;
|
|
1249
|
-
return Object.entries(attributes).every(([key, value]) => recordAttrs[key] === value);
|
|
1250
|
-
});
|
|
1251
|
-
}
|
|
1252
|
-
if (fromDate) {
|
|
1253
|
-
filteredTraces = filteredTraces.filter((record) => new Date(record.createdAt).getTime() >= fromDate.getTime());
|
|
1254
|
-
}
|
|
1255
|
-
if (toDate) {
|
|
1256
|
-
filteredTraces = filteredTraces.filter((record) => new Date(record.createdAt).getTime() <= toDate.getTime());
|
|
1257
|
-
}
|
|
1258
|
-
filteredTraces.sort((a, b) => {
|
|
1259
|
-
const dateA = new Date(a.createdAt).getTime();
|
|
1260
|
-
const dateB = new Date(b.createdAt).getTime();
|
|
1261
|
-
return dateB - dateA;
|
|
1262
|
-
});
|
|
1263
|
-
const start = page * perPage;
|
|
1264
|
-
const end = start + perPage;
|
|
1265
|
-
const paginatedTraces = filteredTraces.slice(start, end);
|
|
1266
|
-
return paginatedTraces.map((record) => ({
|
|
1267
|
-
id: record.id,
|
|
1268
|
-
parentSpanId: record.parentSpanId,
|
|
1269
|
-
traceId: record.traceId,
|
|
1270
|
-
name: record.name,
|
|
1271
|
-
scope: record.scope,
|
|
1272
|
-
kind: record.kind,
|
|
1273
|
-
status: this.parseJSON(record.status),
|
|
1274
|
-
events: this.parseJSON(record.events) || [],
|
|
1275
|
-
links: this.parseJSON(record.links) || [],
|
|
1276
|
-
attributes: this.parseJSON(record?.attributes) || {},
|
|
1277
|
-
startTime: record.startTime,
|
|
1278
|
-
endTime: record.endTime,
|
|
1279
|
-
other: this.parseJSON(record.other) || {},
|
|
1280
|
-
createdAt: record.createdAt
|
|
1281
|
-
}));
|
|
1282
|
-
} catch (error) {
|
|
1283
|
-
const mastraError = new MastraError(
|
|
1284
|
-
{
|
|
1285
|
-
id: "CLOUDFLARE_STORAGE_GET_TRACES_FAILED",
|
|
1286
|
-
domain: ErrorDomain.STORAGE,
|
|
1287
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1288
|
-
text: `Failed to get traces`
|
|
1289
|
-
},
|
|
1290
|
-
error
|
|
1291
|
-
);
|
|
1292
|
-
this.logger?.trackException(mastraError);
|
|
1293
|
-
this.logger?.error(mastraError.toString());
|
|
1294
|
-
return [];
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
parseJSON(value) {
|
|
1298
|
-
if (typeof value === "string") {
|
|
1299
|
-
try {
|
|
1300
|
-
return JSON.parse(value);
|
|
1301
|
-
} catch {
|
|
1302
|
-
return value;
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
return value;
|
|
1306
|
-
}
|
|
1307
|
-
getEvalsByAgentName(_agentName, _type) {
|
|
1308
|
-
throw new MastraError({
|
|
1309
|
-
id: "CLOUDFLARE_STORAGE_GET_EVALS_BY_AGENT_NAME_FAILED",
|
|
1310
|
-
domain: ErrorDomain.STORAGE,
|
|
1311
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1312
|
-
text: `Failed to get evals by agent name`
|
|
1313
|
-
});
|
|
1314
|
-
}
|
|
1315
1943
|
parseWorkflowRun(row) {
|
|
1316
1944
|
let parsedSnapshot = row.snapshot;
|
|
1317
1945
|
if (typeof parsedSnapshot === "string") {
|
|
@@ -1325,28 +1953,24 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
1325
1953
|
workflowName: row.workflow_name,
|
|
1326
1954
|
runId: row.run_id,
|
|
1327
1955
|
snapshot: parsedSnapshot,
|
|
1328
|
-
createdAt:
|
|
1329
|
-
updatedAt:
|
|
1956
|
+
createdAt: ensureDate(row.createdAt),
|
|
1957
|
+
updatedAt: ensureDate(row.updatedAt),
|
|
1330
1958
|
resourceId: row.resourceId
|
|
1331
1959
|
};
|
|
1332
1960
|
}
|
|
1333
1961
|
buildWorkflowSnapshotPrefix({
|
|
1334
|
-
namespace,
|
|
1335
1962
|
workflowName,
|
|
1336
1963
|
runId,
|
|
1337
1964
|
resourceId
|
|
1338
1965
|
}) {
|
|
1339
|
-
const prefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
|
|
1966
|
+
const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
|
|
1340
1967
|
let key = `${prefix}${TABLE_WORKFLOW_SNAPSHOT}`;
|
|
1341
|
-
if (namespace) key += `:${namespace}`;
|
|
1342
1968
|
if (workflowName) key += `:${workflowName}`;
|
|
1343
1969
|
if (runId) key += `:${runId}`;
|
|
1344
1970
|
if (resourceId) key += `:${resourceId}`;
|
|
1345
|
-
if (!resourceId && (runId || workflowName || namespace)) key += ":";
|
|
1346
1971
|
return key;
|
|
1347
1972
|
}
|
|
1348
1973
|
async getWorkflowRuns({
|
|
1349
|
-
namespace,
|
|
1350
1974
|
workflowName,
|
|
1351
1975
|
limit = 20,
|
|
1352
1976
|
offset = 0,
|
|
@@ -1355,28 +1979,32 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
1355
1979
|
toDate
|
|
1356
1980
|
} = {}) {
|
|
1357
1981
|
try {
|
|
1358
|
-
const prefix = this.buildWorkflowSnapshotPrefix({
|
|
1359
|
-
const keyObjs = await this.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
|
|
1982
|
+
const prefix = this.buildWorkflowSnapshotPrefix({ workflowName });
|
|
1983
|
+
const keyObjs = await this.operations.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
|
|
1360
1984
|
const runs = [];
|
|
1361
1985
|
for (const { name: key } of keyObjs) {
|
|
1362
1986
|
const parts = key.split(":");
|
|
1363
1987
|
const idx = parts.indexOf(TABLE_WORKFLOW_SNAPSHOT);
|
|
1364
|
-
if (idx === -1 || parts.length < idx +
|
|
1365
|
-
const
|
|
1366
|
-
const
|
|
1367
|
-
|
|
1368
|
-
if (
|
|
1369
|
-
|
|
1370
|
-
const data = await this.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
|
|
1988
|
+
if (idx === -1 || parts.length < idx + 3) continue;
|
|
1989
|
+
const wfName = parts[idx + 1];
|
|
1990
|
+
const keyResourceId = parts.length > idx + 3 ? parts[idx + 3] : void 0;
|
|
1991
|
+
if (workflowName && wfName !== workflowName) continue;
|
|
1992
|
+
if (resourceId && keyResourceId !== resourceId) continue;
|
|
1993
|
+
const data = await this.operations.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
|
|
1371
1994
|
if (!data) continue;
|
|
1372
1995
|
try {
|
|
1373
|
-
if (resourceId &&
|
|
1374
|
-
const createdAt =
|
|
1996
|
+
if (resourceId && !keyResourceId) continue;
|
|
1997
|
+
const createdAt = ensureDate(data.createdAt);
|
|
1375
1998
|
if (fromDate && createdAt && createdAt < fromDate) continue;
|
|
1376
1999
|
if (toDate && createdAt && createdAt > toDate) continue;
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1379
|
-
const run = this.parseWorkflowRun({
|
|
2000
|
+
const snapshotData = typeof data.snapshot === "string" ? JSON.parse(data.snapshot) : data.snapshot;
|
|
2001
|
+
const resourceIdToUse = keyResourceId || data.resourceId;
|
|
2002
|
+
const run = this.parseWorkflowRun({
|
|
2003
|
+
...data,
|
|
2004
|
+
workflow_name: wfName,
|
|
2005
|
+
resourceId: resourceIdToUse,
|
|
2006
|
+
snapshot: snapshotData
|
|
2007
|
+
});
|
|
1380
2008
|
runs.push(run);
|
|
1381
2009
|
} catch (err) {
|
|
1382
2010
|
this.logger.error("Failed to parse workflow snapshot:", { key, error: err });
|
|
@@ -1401,29 +2029,35 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
1401
2029
|
},
|
|
1402
2030
|
error
|
|
1403
2031
|
);
|
|
1404
|
-
this.logger?.
|
|
1405
|
-
this.logger
|
|
2032
|
+
this.logger.trackException?.(mastraError);
|
|
2033
|
+
this.logger.error(mastraError.toString());
|
|
1406
2034
|
return { runs: [], total: 0 };
|
|
1407
2035
|
}
|
|
1408
2036
|
}
|
|
1409
2037
|
async getWorkflowRunById({
|
|
1410
|
-
namespace,
|
|
1411
2038
|
runId,
|
|
1412
2039
|
workflowName
|
|
1413
2040
|
}) {
|
|
1414
2041
|
try {
|
|
1415
|
-
if (!runId || !workflowName
|
|
1416
|
-
throw new Error("runId, workflowName,
|
|
2042
|
+
if (!runId || !workflowName) {
|
|
2043
|
+
throw new Error("runId, workflowName, are required");
|
|
1417
2044
|
}
|
|
1418
|
-
const prefix = this.buildWorkflowSnapshotPrefix({
|
|
1419
|
-
const keyObjs = await this.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
|
|
2045
|
+
const prefix = this.buildWorkflowSnapshotPrefix({ workflowName, runId });
|
|
2046
|
+
const keyObjs = await this.operations.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
|
|
1420
2047
|
if (!keyObjs.length) return null;
|
|
1421
|
-
const
|
|
1422
|
-
|
|
2048
|
+
const exactKey = keyObjs.find((k) => {
|
|
2049
|
+
const parts = k.name.split(":");
|
|
2050
|
+
const idx = parts.indexOf(TABLE_WORKFLOW_SNAPSHOT);
|
|
2051
|
+
if (idx === -1 || parts.length < idx + 3) return false;
|
|
2052
|
+
const wfName = parts[idx + 1];
|
|
2053
|
+
const rId = parts[idx + 2];
|
|
2054
|
+
return wfName === workflowName && rId === runId;
|
|
2055
|
+
});
|
|
2056
|
+
if (!exactKey) return null;
|
|
2057
|
+
const data = await this.operations.getKV(TABLE_WORKFLOW_SNAPSHOT, exactKey.name);
|
|
1423
2058
|
if (!data) return null;
|
|
1424
|
-
const
|
|
1425
|
-
this.
|
|
1426
|
-
return this.parseWorkflowRun({ ...data, snapshot: state });
|
|
2059
|
+
const snapshotData = typeof data.snapshot === "string" ? JSON.parse(data.snapshot) : data.snapshot;
|
|
2060
|
+
return this.parseWorkflowRun({ ...data, snapshot: snapshotData });
|
|
1427
2061
|
} catch (error) {
|
|
1428
2062
|
const mastraError = new MastraError(
|
|
1429
2063
|
{
|
|
@@ -1431,47 +2065,282 @@ var CloudflareStore = class extends MastraStorage {
|
|
|
1431
2065
|
domain: ErrorDomain.STORAGE,
|
|
1432
2066
|
category: ErrorCategory.THIRD_PARTY,
|
|
1433
2067
|
details: {
|
|
1434
|
-
namespace,
|
|
1435
2068
|
workflowName,
|
|
1436
2069
|
runId
|
|
1437
2070
|
}
|
|
1438
2071
|
},
|
|
1439
2072
|
error
|
|
1440
2073
|
);
|
|
1441
|
-
this.logger?.
|
|
1442
|
-
this.logger
|
|
2074
|
+
this.logger.trackException?.(mastraError);
|
|
2075
|
+
this.logger.error(mastraError.toString());
|
|
1443
2076
|
return null;
|
|
1444
2077
|
}
|
|
1445
2078
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
2079
|
+
};
|
|
2080
|
+
|
|
2081
|
+
// src/storage/types.ts
|
|
2082
|
+
function isWorkersConfig(config) {
|
|
2083
|
+
return "bindings" in config;
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
// src/storage/index.ts
|
|
2087
|
+
var CloudflareStore = class extends MastraStorage {
|
|
2088
|
+
stores;
|
|
2089
|
+
client;
|
|
2090
|
+
accountId;
|
|
2091
|
+
namespacePrefix;
|
|
2092
|
+
bindings;
|
|
2093
|
+
validateWorkersConfig(config) {
|
|
2094
|
+
if (!isWorkersConfig(config)) {
|
|
2095
|
+
throw new Error("Invalid Workers API configuration");
|
|
2096
|
+
}
|
|
2097
|
+
if (!config.bindings) {
|
|
2098
|
+
throw new Error("KV bindings are required when using Workers Binding API");
|
|
2099
|
+
}
|
|
2100
|
+
const requiredTables = [
|
|
2101
|
+
TABLE_THREADS,
|
|
2102
|
+
TABLE_MESSAGES,
|
|
2103
|
+
TABLE_WORKFLOW_SNAPSHOT,
|
|
2104
|
+
TABLE_EVALS,
|
|
2105
|
+
TABLE_SCORERS,
|
|
2106
|
+
TABLE_TRACES
|
|
2107
|
+
];
|
|
2108
|
+
for (const table of requiredTables) {
|
|
2109
|
+
if (!(table in config.bindings)) {
|
|
2110
|
+
throw new Error(`Missing KV binding for table: ${table}`);
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
validateRestConfig(config) {
|
|
2115
|
+
if (isWorkersConfig(config)) {
|
|
2116
|
+
throw new Error("Invalid REST API configuration");
|
|
2117
|
+
}
|
|
2118
|
+
if (!config.accountId?.trim()) {
|
|
2119
|
+
throw new Error("accountId is required for REST API");
|
|
2120
|
+
}
|
|
2121
|
+
if (!config.apiToken?.trim()) {
|
|
2122
|
+
throw new Error("apiToken is required for REST API");
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
constructor(config) {
|
|
2126
|
+
super({ name: "Cloudflare" });
|
|
2127
|
+
try {
|
|
2128
|
+
if (isWorkersConfig(config)) {
|
|
2129
|
+
this.validateWorkersConfig(config);
|
|
2130
|
+
this.bindings = config.bindings;
|
|
2131
|
+
this.namespacePrefix = config.keyPrefix?.trim() || "";
|
|
2132
|
+
this.logger.info("Using Cloudflare KV Workers Binding API");
|
|
2133
|
+
} else {
|
|
2134
|
+
this.validateRestConfig(config);
|
|
2135
|
+
this.accountId = config.accountId.trim();
|
|
2136
|
+
this.namespacePrefix = config.namespacePrefix?.trim() || "";
|
|
2137
|
+
this.client = new Cloudflare({
|
|
2138
|
+
apiToken: config.apiToken.trim()
|
|
2139
|
+
});
|
|
2140
|
+
this.logger.info("Using Cloudflare KV REST API");
|
|
2141
|
+
}
|
|
2142
|
+
const operations = new StoreOperationsCloudflare({
|
|
2143
|
+
accountId: this.accountId,
|
|
2144
|
+
client: this.client,
|
|
2145
|
+
namespacePrefix: this.namespacePrefix,
|
|
2146
|
+
bindings: this.bindings
|
|
2147
|
+
});
|
|
2148
|
+
const legacyEvals = new LegacyEvalsStorageCloudflare({
|
|
2149
|
+
operations
|
|
2150
|
+
});
|
|
2151
|
+
const workflows = new WorkflowsStorageCloudflare({
|
|
2152
|
+
operations
|
|
2153
|
+
});
|
|
2154
|
+
const traces = new TracesStorageCloudflare({
|
|
2155
|
+
operations
|
|
2156
|
+
});
|
|
2157
|
+
const memory = new MemoryStorageCloudflare({
|
|
2158
|
+
operations
|
|
2159
|
+
});
|
|
2160
|
+
const scores = new ScoresStorageCloudflare({
|
|
2161
|
+
operations
|
|
2162
|
+
});
|
|
2163
|
+
this.stores = {
|
|
2164
|
+
operations,
|
|
2165
|
+
legacyEvals,
|
|
2166
|
+
workflows,
|
|
2167
|
+
traces,
|
|
2168
|
+
memory,
|
|
2169
|
+
scores
|
|
2170
|
+
};
|
|
2171
|
+
} catch (error) {
|
|
2172
|
+
throw new MastraError(
|
|
2173
|
+
{
|
|
2174
|
+
id: "CLOUDFLARE_STORAGE_INIT_FAILED",
|
|
2175
|
+
domain: ErrorDomain.STORAGE,
|
|
2176
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2177
|
+
},
|
|
2178
|
+
error
|
|
2179
|
+
);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
async createTable({
|
|
2183
|
+
tableName,
|
|
2184
|
+
schema
|
|
2185
|
+
}) {
|
|
2186
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
2187
|
+
}
|
|
2188
|
+
async alterTable(_args) {
|
|
2189
|
+
return this.stores.operations.alterTable(_args);
|
|
2190
|
+
}
|
|
2191
|
+
async clearTable({ tableName }) {
|
|
2192
|
+
return this.stores.operations.clearTable({ tableName });
|
|
2193
|
+
}
|
|
2194
|
+
async dropTable({ tableName }) {
|
|
2195
|
+
return this.stores.operations.dropTable({ tableName });
|
|
2196
|
+
}
|
|
2197
|
+
async insert({
|
|
2198
|
+
tableName,
|
|
2199
|
+
record
|
|
2200
|
+
}) {
|
|
2201
|
+
return this.stores.operations.insert({ tableName, record });
|
|
2202
|
+
}
|
|
2203
|
+
async load({ tableName, keys }) {
|
|
2204
|
+
return this.stores.operations.load({ tableName, keys });
|
|
2205
|
+
}
|
|
2206
|
+
async getThreadById({ threadId }) {
|
|
2207
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
2208
|
+
}
|
|
2209
|
+
async getThreadsByResourceId({ resourceId }) {
|
|
2210
|
+
return this.stores.memory.getThreadsByResourceId({ resourceId });
|
|
2211
|
+
}
|
|
2212
|
+
async saveThread({ thread }) {
|
|
2213
|
+
return this.stores.memory.saveThread({ thread });
|
|
2214
|
+
}
|
|
2215
|
+
async updateThread({
|
|
2216
|
+
id,
|
|
2217
|
+
title,
|
|
2218
|
+
metadata
|
|
2219
|
+
}) {
|
|
2220
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
2221
|
+
}
|
|
2222
|
+
async deleteThread({ threadId }) {
|
|
2223
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
2224
|
+
}
|
|
2225
|
+
async saveMessages(args) {
|
|
2226
|
+
return this.stores.memory.saveMessages(args);
|
|
2227
|
+
}
|
|
2228
|
+
async getMessages({
|
|
2229
|
+
threadId,
|
|
2230
|
+
resourceId,
|
|
2231
|
+
selectBy,
|
|
2232
|
+
format
|
|
2233
|
+
}) {
|
|
2234
|
+
return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format });
|
|
2235
|
+
}
|
|
2236
|
+
async persistWorkflowSnapshot(params) {
|
|
2237
|
+
return this.stores.workflows.persistWorkflowSnapshot(params);
|
|
1453
2238
|
}
|
|
1454
|
-
async
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
2239
|
+
async loadWorkflowSnapshot(params) {
|
|
2240
|
+
return this.stores.workflows.loadWorkflowSnapshot(params);
|
|
2241
|
+
}
|
|
2242
|
+
async batchInsert(input) {
|
|
2243
|
+
return this.stores.operations.batchInsert(input);
|
|
2244
|
+
}
|
|
2245
|
+
async getTraces({
|
|
2246
|
+
name,
|
|
2247
|
+
scope,
|
|
2248
|
+
page = 0,
|
|
2249
|
+
perPage = 100,
|
|
2250
|
+
attributes,
|
|
2251
|
+
fromDate,
|
|
2252
|
+
toDate
|
|
2253
|
+
}) {
|
|
2254
|
+
return this.stores.traces.getTraces({
|
|
2255
|
+
name,
|
|
2256
|
+
scope,
|
|
2257
|
+
page,
|
|
2258
|
+
perPage,
|
|
2259
|
+
attributes,
|
|
2260
|
+
fromDate,
|
|
2261
|
+
toDate
|
|
1460
2262
|
});
|
|
1461
2263
|
}
|
|
1462
|
-
async
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
2264
|
+
async getEvalsByAgentName(agentName, type) {
|
|
2265
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
2266
|
+
}
|
|
2267
|
+
async getEvals(options) {
|
|
2268
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
2269
|
+
}
|
|
2270
|
+
async getWorkflowRuns({
|
|
2271
|
+
workflowName,
|
|
2272
|
+
limit = 20,
|
|
2273
|
+
offset = 0,
|
|
2274
|
+
resourceId,
|
|
2275
|
+
fromDate,
|
|
2276
|
+
toDate
|
|
2277
|
+
} = {}) {
|
|
2278
|
+
return this.stores.workflows.getWorkflowRuns({
|
|
2279
|
+
workflowName,
|
|
2280
|
+
limit,
|
|
2281
|
+
offset,
|
|
2282
|
+
resourceId,
|
|
2283
|
+
fromDate,
|
|
2284
|
+
toDate
|
|
1468
2285
|
});
|
|
1469
2286
|
}
|
|
1470
|
-
async
|
|
2287
|
+
async getWorkflowRunById({
|
|
2288
|
+
runId,
|
|
2289
|
+
workflowName
|
|
2290
|
+
}) {
|
|
2291
|
+
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
2292
|
+
}
|
|
2293
|
+
async getTracesPaginated(args) {
|
|
2294
|
+
return this.stores.traces.getTracesPaginated(args);
|
|
2295
|
+
}
|
|
2296
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
2297
|
+
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
2298
|
+
}
|
|
2299
|
+
async getMessagesPaginated(args) {
|
|
2300
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
2301
|
+
}
|
|
2302
|
+
async updateMessages(args) {
|
|
2303
|
+
return this.stores.memory.updateMessages(args);
|
|
1471
2304
|
}
|
|
1472
|
-
async
|
|
1473
|
-
this.
|
|
1474
|
-
|
|
2305
|
+
async getScoreById({ id }) {
|
|
2306
|
+
return this.stores.scores.getScoreById({ id });
|
|
2307
|
+
}
|
|
2308
|
+
async saveScore(score) {
|
|
2309
|
+
return this.stores.scores.saveScore(score);
|
|
2310
|
+
}
|
|
2311
|
+
async getScoresByRunId({
|
|
2312
|
+
runId,
|
|
2313
|
+
pagination
|
|
2314
|
+
}) {
|
|
2315
|
+
return this.stores.scores.getScoresByRunId({ runId, pagination });
|
|
2316
|
+
}
|
|
2317
|
+
async getScoresByEntityId({
|
|
2318
|
+
entityId,
|
|
2319
|
+
entityType,
|
|
2320
|
+
pagination
|
|
2321
|
+
}) {
|
|
2322
|
+
return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
|
|
2323
|
+
}
|
|
2324
|
+
async getScoresByScorerId({
|
|
2325
|
+
scorerId,
|
|
2326
|
+
pagination
|
|
2327
|
+
}) {
|
|
2328
|
+
return this.stores.scores.getScoresByScorerId({ scorerId, pagination });
|
|
2329
|
+
}
|
|
2330
|
+
async getResourceById({ resourceId }) {
|
|
2331
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
2332
|
+
}
|
|
2333
|
+
async saveResource({ resource }) {
|
|
2334
|
+
return this.stores.memory.saveResource({ resource });
|
|
2335
|
+
}
|
|
2336
|
+
async updateResource({
|
|
2337
|
+
resourceId,
|
|
2338
|
+
workingMemory,
|
|
2339
|
+
metadata
|
|
2340
|
+
}) {
|
|
2341
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
2342
|
+
}
|
|
2343
|
+
async close() {
|
|
1475
2344
|
}
|
|
1476
2345
|
};
|
|
1477
2346
|
|