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