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