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