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