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