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