@mastra/clickhouse 0.0.0-vnext-inngest-20250508131921 → 0.0.0-vnext-20251119160359
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1418 -2
- package/LICENSE.md +12 -4
- package/README.md +47 -21
- package/dist/index.cjs +1958 -475
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1943 -460
- package/dist/index.js.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +62 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -0
- package/dist/storage/domains/operations/index.d.ts +42 -0
- package/dist/storage/domains/operations/index.d.ts.map +1 -0
- package/dist/storage/domains/scores/index.d.ts +54 -0
- package/dist/storage/domains/scores/index.d.ts.map +1 -0
- package/dist/storage/domains/utils.d.ts +28 -0
- package/dist/storage/domains/utils.d.ts.map +1 -0
- package/dist/storage/domains/workflows/index.d.ts +48 -0
- package/dist/storage/domains/workflows/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +192 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/package.json +37 -14
- package/dist/_tsup-dts-rollup.d.cts +0 -138
- package/dist/_tsup-dts-rollup.d.ts +0 -138
- package/dist/index.d.cts +0 -4
- package/docker-compose.yaml +0 -15
- package/eslint.config.js +0 -6
- package/src/index.ts +0 -1
- package/src/storage/index.test.ts +0 -856
- package/src/storage/index.ts +0 -1058
- package/tsconfig.json +0 -5
- package/vitest.config.ts +0 -12
package/dist/index.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { createClient } from '@clickhouse/client';
|
|
2
|
-
import {
|
|
2
|
+
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
3
|
+
import { TABLE_SPANS, TABLE_RESOURCES, TABLE_SCORERS, TABLE_THREADS, TABLE_TRACES, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, MastraStorage, StoreOperations, TABLE_SCHEMAS, WorkflowsStorage, normalizePerPage, ScoresStorage, safelyParseJSON, calculatePagination, MemoryStorage } from '@mastra/core/storage';
|
|
4
|
+
import { MessageList } from '@mastra/core/agent';
|
|
5
|
+
import { saveScorePayloadSchema } from '@mastra/core/evals';
|
|
3
6
|
|
|
4
7
|
// src/storage/index.ts
|
|
5
|
-
function safelyParseJSON(jsonString) {
|
|
6
|
-
try {
|
|
7
|
-
return JSON.parse(jsonString);
|
|
8
|
-
} catch {
|
|
9
|
-
return {};
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
8
|
var TABLE_ENGINES = {
|
|
13
9
|
[TABLE_MESSAGES]: `MergeTree()`,
|
|
14
10
|
[TABLE_WORKFLOW_SNAPSHOT]: `ReplacingMergeTree()`,
|
|
15
11
|
[TABLE_TRACES]: `MergeTree()`,
|
|
16
12
|
[TABLE_THREADS]: `ReplacingMergeTree()`,
|
|
17
|
-
[
|
|
13
|
+
[TABLE_SCORERS]: `MergeTree()`,
|
|
14
|
+
[TABLE_RESOURCES]: `ReplacingMergeTree()`,
|
|
15
|
+
// TODO: verify this is the correct engine for Spans when implementing clickhouse storage
|
|
16
|
+
[TABLE_SPANS]: `ReplacingMergeTree()`
|
|
18
17
|
};
|
|
19
18
|
var COLUMN_TYPES = {
|
|
20
19
|
text: "String",
|
|
@@ -22,11 +21,10 @@ var COLUMN_TYPES = {
|
|
|
22
21
|
uuid: "String",
|
|
23
22
|
jsonb: "String",
|
|
24
23
|
integer: "Int64",
|
|
25
|
-
|
|
24
|
+
float: "Float64",
|
|
25
|
+
bigint: "Int64",
|
|
26
|
+
boolean: "Bool"
|
|
26
27
|
};
|
|
27
|
-
function transformRows(rows) {
|
|
28
|
-
return rows.map((row) => transformRow(row));
|
|
29
|
-
}
|
|
30
28
|
function transformRow(row) {
|
|
31
29
|
if (!row) {
|
|
32
30
|
return row;
|
|
@@ -37,54 +35,730 @@ function transformRow(row) {
|
|
|
37
35
|
if (row.updatedAt) {
|
|
38
36
|
row.updatedAt = new Date(row.updatedAt);
|
|
39
37
|
}
|
|
38
|
+
if (row.content && typeof row.content === "string") {
|
|
39
|
+
row.content = safelyParseJSON(row.content);
|
|
40
|
+
}
|
|
40
41
|
return row;
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
43
|
+
function transformRows(rows) {
|
|
44
|
+
return rows.map((row) => transformRow(row));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/storage/domains/memory/index.ts
|
|
48
|
+
function serializeMetadata(metadata) {
|
|
49
|
+
if (!metadata || Object.keys(metadata).length === 0) {
|
|
50
|
+
return "{}";
|
|
51
|
+
}
|
|
52
|
+
return JSON.stringify(metadata);
|
|
53
|
+
}
|
|
54
|
+
function parseMetadata(metadata) {
|
|
55
|
+
if (!metadata) return {};
|
|
56
|
+
if (typeof metadata === "object") return metadata;
|
|
57
|
+
if (typeof metadata !== "string") return {};
|
|
58
|
+
const trimmed = metadata.trim();
|
|
59
|
+
if (trimmed === "" || trimmed === "null") return {};
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse(trimmed);
|
|
62
|
+
} catch {
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
var MemoryStorageClickhouse = class extends MemoryStorage {
|
|
67
|
+
client;
|
|
68
|
+
operations;
|
|
69
|
+
constructor({ client, operations }) {
|
|
70
|
+
super();
|
|
71
|
+
this.client = client;
|
|
72
|
+
this.operations = operations;
|
|
73
|
+
}
|
|
74
|
+
async listMessagesById({ messageIds }) {
|
|
75
|
+
if (messageIds.length === 0) return { messages: [] };
|
|
76
|
+
try {
|
|
77
|
+
const result = await this.client.query({
|
|
78
|
+
query: `
|
|
79
|
+
SELECT
|
|
80
|
+
id,
|
|
81
|
+
content,
|
|
82
|
+
role,
|
|
83
|
+
type,
|
|
84
|
+
toDateTime64(createdAt, 3) as createdAt,
|
|
85
|
+
thread_id AS "threadId",
|
|
86
|
+
"resourceId"
|
|
87
|
+
FROM "${TABLE_MESSAGES}"
|
|
88
|
+
WHERE id IN {messageIds:Array(String)}
|
|
89
|
+
ORDER BY "createdAt" DESC
|
|
90
|
+
`,
|
|
91
|
+
query_params: {
|
|
92
|
+
messageIds
|
|
93
|
+
},
|
|
94
|
+
clickhouse_settings: {
|
|
95
|
+
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
96
|
+
date_time_input_format: "best_effort",
|
|
97
|
+
date_time_output_format: "iso",
|
|
98
|
+
use_client_time_zone: 1,
|
|
99
|
+
output_format_json_quote_64bit_integers: 0
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const rows = await result.json();
|
|
103
|
+
const messages = transformRows(rows.data);
|
|
104
|
+
messages.forEach((message) => {
|
|
105
|
+
if (typeof message.content === "string") {
|
|
106
|
+
try {
|
|
107
|
+
message.content = JSON.parse(message.content);
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
const list = new MessageList().add(messages, "memory");
|
|
113
|
+
return { messages: list.get.all.db() };
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new MastraError(
|
|
116
|
+
{
|
|
117
|
+
id: "CLICKHOUSE_STORAGE_LIST_MESSAGES_BY_ID_FAILED",
|
|
118
|
+
domain: ErrorDomain.STORAGE,
|
|
119
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
120
|
+
details: { messageIds: JSON.stringify(messageIds) }
|
|
121
|
+
},
|
|
122
|
+
error
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async listMessages(args) {
|
|
127
|
+
const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
|
|
128
|
+
if (page < 0) {
|
|
129
|
+
throw new MastraError(
|
|
130
|
+
{
|
|
131
|
+
id: "STORAGE_CLICKHOUSE_LIST_MESSAGES_INVALID_PAGE",
|
|
132
|
+
domain: ErrorDomain.STORAGE,
|
|
133
|
+
category: ErrorCategory.USER,
|
|
134
|
+
details: { page }
|
|
135
|
+
},
|
|
136
|
+
new Error("page must be >= 0")
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (!threadId.trim()) {
|
|
140
|
+
throw new MastraError(
|
|
141
|
+
{
|
|
142
|
+
id: "STORAGE_CLICKHOUSE_LIST_MESSAGES_INVALID_THREAD_ID",
|
|
143
|
+
domain: ErrorDomain.STORAGE,
|
|
144
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
145
|
+
details: { threadId }
|
|
146
|
+
},
|
|
147
|
+
new Error("threadId must be a non-empty string")
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
const perPageForQuery = normalizePerPage(perPageInput, 40);
|
|
151
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPageForQuery);
|
|
152
|
+
try {
|
|
153
|
+
let dataQuery = `
|
|
154
|
+
SELECT
|
|
155
|
+
id,
|
|
156
|
+
content,
|
|
157
|
+
role,
|
|
158
|
+
type,
|
|
159
|
+
toDateTime64(createdAt, 3) as createdAt,
|
|
160
|
+
thread_id AS "threadId",
|
|
161
|
+
resourceId
|
|
162
|
+
FROM ${TABLE_MESSAGES}
|
|
163
|
+
WHERE thread_id = {threadId:String}
|
|
164
|
+
`;
|
|
165
|
+
const dataParams = { threadId };
|
|
166
|
+
if (resourceId) {
|
|
167
|
+
dataQuery += ` AND resourceId = {resourceId:String}`;
|
|
168
|
+
dataParams.resourceId = resourceId;
|
|
57
169
|
}
|
|
58
|
-
|
|
59
|
-
|
|
170
|
+
if (filter?.dateRange?.start) {
|
|
171
|
+
const startDate = filter.dateRange.start instanceof Date ? filter.dateRange.start.toISOString() : new Date(filter.dateRange.start).toISOString();
|
|
172
|
+
dataQuery += ` AND createdAt >= parseDateTime64BestEffort({fromDate:String}, 3)`;
|
|
173
|
+
dataParams.fromDate = startDate;
|
|
174
|
+
}
|
|
175
|
+
if (filter?.dateRange?.end) {
|
|
176
|
+
const endDate = filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : new Date(filter.dateRange.end).toISOString();
|
|
177
|
+
dataQuery += ` AND createdAt <= parseDateTime64BestEffort({toDate:String}, 3)`;
|
|
178
|
+
dataParams.toDate = endDate;
|
|
179
|
+
}
|
|
180
|
+
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
181
|
+
dataQuery += ` ORDER BY "${field}" ${direction}`;
|
|
182
|
+
if (perPageForResponse === false) ; else {
|
|
183
|
+
dataQuery += ` LIMIT {limit:Int64} OFFSET {offset:Int64}`;
|
|
184
|
+
dataParams.limit = perPageForQuery;
|
|
185
|
+
dataParams.offset = offset;
|
|
186
|
+
}
|
|
187
|
+
const result = await this.client.query({
|
|
188
|
+
query: dataQuery,
|
|
189
|
+
query_params: dataParams,
|
|
190
|
+
clickhouse_settings: {
|
|
191
|
+
date_time_input_format: "best_effort",
|
|
192
|
+
date_time_output_format: "iso",
|
|
193
|
+
use_client_time_zone: 1,
|
|
194
|
+
output_format_json_quote_64bit_integers: 0
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
const rows = await result.json();
|
|
198
|
+
const paginatedMessages = transformRows(rows.data);
|
|
199
|
+
const paginatedCount = paginatedMessages.length;
|
|
200
|
+
let countQuery = `SELECT count() as total FROM ${TABLE_MESSAGES} WHERE thread_id = {threadId:String}`;
|
|
201
|
+
const countParams = { threadId };
|
|
202
|
+
if (resourceId) {
|
|
203
|
+
countQuery += ` AND resourceId = {resourceId:String}`;
|
|
204
|
+
countParams.resourceId = resourceId;
|
|
205
|
+
}
|
|
206
|
+
if (filter?.dateRange?.start) {
|
|
207
|
+
const startDate = filter.dateRange.start instanceof Date ? filter.dateRange.start.toISOString() : new Date(filter.dateRange.start).toISOString();
|
|
208
|
+
countQuery += ` AND createdAt >= parseDateTime64BestEffort({fromDate:String}, 3)`;
|
|
209
|
+
countParams.fromDate = startDate;
|
|
210
|
+
}
|
|
211
|
+
if (filter?.dateRange?.end) {
|
|
212
|
+
const endDate = filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : new Date(filter.dateRange.end).toISOString();
|
|
213
|
+
countQuery += ` AND createdAt <= parseDateTime64BestEffort({toDate:String}, 3)`;
|
|
214
|
+
countParams.toDate = endDate;
|
|
215
|
+
}
|
|
216
|
+
const countResult = await this.client.query({
|
|
217
|
+
query: countQuery,
|
|
218
|
+
query_params: countParams,
|
|
219
|
+
clickhouse_settings: {
|
|
220
|
+
date_time_input_format: "best_effort",
|
|
221
|
+
date_time_output_format: "iso",
|
|
222
|
+
use_client_time_zone: 1,
|
|
223
|
+
output_format_json_quote_64bit_integers: 0
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
const countData = await countResult.json();
|
|
227
|
+
const total = countData.data[0].total;
|
|
228
|
+
if (total === 0 && paginatedCount === 0 && (!include || include.length === 0)) {
|
|
229
|
+
return {
|
|
230
|
+
messages: [],
|
|
231
|
+
total: 0,
|
|
232
|
+
page,
|
|
233
|
+
perPage: perPageForResponse,
|
|
234
|
+
hasMore: false
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const messageIds = new Set(paginatedMessages.map((m) => m.id));
|
|
238
|
+
let includeMessages = [];
|
|
239
|
+
if (include && include.length > 0) {
|
|
240
|
+
const unionQueries = [];
|
|
241
|
+
const params = [];
|
|
242
|
+
let paramIdx = 1;
|
|
243
|
+
for (const inc of include) {
|
|
244
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
245
|
+
const searchId = inc.threadId || threadId;
|
|
246
|
+
unionQueries.push(`
|
|
247
|
+
SELECT * FROM (
|
|
248
|
+
WITH numbered_messages AS (
|
|
249
|
+
SELECT
|
|
250
|
+
id, content, role, type, "createdAt", thread_id, "resourceId",
|
|
251
|
+
ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
|
|
252
|
+
FROM "${TABLE_MESSAGES}"
|
|
253
|
+
WHERE thread_id = {var_thread_id_${paramIdx}:String}
|
|
254
|
+
),
|
|
255
|
+
target_positions AS (
|
|
256
|
+
SELECT row_num as target_pos
|
|
257
|
+
FROM numbered_messages
|
|
258
|
+
WHERE id = {var_include_id_${paramIdx}:String}
|
|
259
|
+
)
|
|
260
|
+
SELECT DISTINCT m.id, m.content, m.role, m.type, m."createdAt", m.thread_id AS "threadId", m."resourceId"
|
|
261
|
+
FROM numbered_messages m
|
|
262
|
+
CROSS JOIN target_positions t
|
|
263
|
+
WHERE m.row_num BETWEEN (t.target_pos - {var_withPreviousMessages_${paramIdx}:Int64}) AND (t.target_pos + {var_withNextMessages_${paramIdx}:Int64})
|
|
264
|
+
) AS query_${paramIdx}
|
|
265
|
+
`);
|
|
266
|
+
params.push(
|
|
267
|
+
{ [`var_thread_id_${paramIdx}`]: searchId },
|
|
268
|
+
{ [`var_include_id_${paramIdx}`]: id },
|
|
269
|
+
{ [`var_withPreviousMessages_${paramIdx}`]: withPreviousMessages },
|
|
270
|
+
{ [`var_withNextMessages_${paramIdx}`]: withNextMessages }
|
|
271
|
+
);
|
|
272
|
+
paramIdx++;
|
|
273
|
+
}
|
|
274
|
+
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
275
|
+
const mergedParams = params.reduce((acc, paramObj) => ({ ...acc, ...paramObj }), {});
|
|
276
|
+
const includeResult = await this.client.query({
|
|
277
|
+
query: finalQuery,
|
|
278
|
+
query_params: mergedParams,
|
|
279
|
+
clickhouse_settings: {
|
|
280
|
+
date_time_input_format: "best_effort",
|
|
281
|
+
date_time_output_format: "iso",
|
|
282
|
+
use_client_time_zone: 1,
|
|
283
|
+
output_format_json_quote_64bit_integers: 0
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
const includeRows = await includeResult.json();
|
|
287
|
+
includeMessages = transformRows(includeRows.data);
|
|
288
|
+
for (const includeMsg of includeMessages) {
|
|
289
|
+
if (!messageIds.has(includeMsg.id)) {
|
|
290
|
+
paginatedMessages.push(includeMsg);
|
|
291
|
+
messageIds.add(includeMsg.id);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const list = new MessageList().add(paginatedMessages, "memory");
|
|
296
|
+
let finalMessages = list.get.all.db();
|
|
297
|
+
finalMessages = finalMessages.sort((a, b) => {
|
|
298
|
+
const isDateField = field === "createdAt" || field === "updatedAt";
|
|
299
|
+
const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
|
|
300
|
+
const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
|
|
301
|
+
if (aValue === bValue) {
|
|
302
|
+
return a.id.localeCompare(b.id);
|
|
303
|
+
}
|
|
304
|
+
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
305
|
+
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
306
|
+
}
|
|
307
|
+
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
308
|
+
});
|
|
309
|
+
const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
|
|
310
|
+
const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
|
|
311
|
+
const hasMore = perPageForResponse === false ? false : allThreadMessagesReturned ? false : offset + paginatedCount < total;
|
|
312
|
+
return {
|
|
313
|
+
messages: finalMessages,
|
|
314
|
+
total,
|
|
315
|
+
page,
|
|
316
|
+
perPage: perPageForResponse,
|
|
317
|
+
hasMore
|
|
318
|
+
};
|
|
319
|
+
} catch (error) {
|
|
320
|
+
const mastraError = new MastraError(
|
|
321
|
+
{
|
|
322
|
+
id: "STORAGE_CLICKHOUSE_STORE_LIST_MESSAGES_FAILED",
|
|
323
|
+
domain: ErrorDomain.STORAGE,
|
|
324
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
325
|
+
details: {
|
|
326
|
+
threadId,
|
|
327
|
+
resourceId: resourceId ?? ""
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
error
|
|
331
|
+
);
|
|
332
|
+
this.logger?.error?.(mastraError.toString());
|
|
333
|
+
this.logger?.trackException?.(mastraError);
|
|
334
|
+
return {
|
|
335
|
+
messages: [],
|
|
336
|
+
total: 0,
|
|
337
|
+
page,
|
|
338
|
+
perPage: perPageForResponse,
|
|
339
|
+
hasMore: false
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
async saveMessages(args) {
|
|
344
|
+
const { messages } = args;
|
|
345
|
+
if (messages.length === 0) return { messages };
|
|
346
|
+
for (const message of messages) {
|
|
347
|
+
const resourceId = message.resourceId;
|
|
348
|
+
if (!resourceId) {
|
|
349
|
+
throw new Error("Resource ID is required");
|
|
350
|
+
}
|
|
351
|
+
if (!message.threadId) {
|
|
352
|
+
throw new Error("Thread ID is required");
|
|
353
|
+
}
|
|
354
|
+
const thread = await this.getThreadById({ threadId: message.threadId });
|
|
355
|
+
if (!thread) {
|
|
356
|
+
throw new Error(`Thread ${message.threadId} not found`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const threadIdSet = /* @__PURE__ */ new Map();
|
|
360
|
+
await Promise.all(
|
|
361
|
+
messages.map(async (m) => {
|
|
362
|
+
const resourceId = m.resourceId;
|
|
363
|
+
if (!resourceId) {
|
|
364
|
+
throw new Error("Resource ID is required");
|
|
365
|
+
}
|
|
366
|
+
if (!m.threadId) {
|
|
367
|
+
throw new Error("Thread ID is required");
|
|
368
|
+
}
|
|
369
|
+
const thread = await this.getThreadById({ threadId: m.threadId });
|
|
370
|
+
if (!thread) {
|
|
371
|
+
throw new Error(`Thread ${m.threadId} not found`);
|
|
372
|
+
}
|
|
373
|
+
threadIdSet.set(m.threadId, thread);
|
|
374
|
+
})
|
|
375
|
+
);
|
|
376
|
+
try {
|
|
377
|
+
const existingResult = await this.client.query({
|
|
378
|
+
query: `SELECT id, thread_id FROM ${TABLE_MESSAGES} WHERE id IN ({ids:Array(String)})`,
|
|
379
|
+
query_params: {
|
|
380
|
+
ids: messages.map((m) => m.id)
|
|
381
|
+
},
|
|
382
|
+
clickhouse_settings: {
|
|
383
|
+
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
384
|
+
date_time_input_format: "best_effort",
|
|
385
|
+
date_time_output_format: "iso",
|
|
386
|
+
use_client_time_zone: 1,
|
|
387
|
+
output_format_json_quote_64bit_integers: 0
|
|
388
|
+
},
|
|
389
|
+
format: "JSONEachRow"
|
|
390
|
+
});
|
|
391
|
+
const existingRows = await existingResult.json();
|
|
392
|
+
const existingSet = new Set(existingRows.map((row) => `${row.id}::${row.thread_id}`));
|
|
393
|
+
const toInsert = messages.filter((m) => !existingSet.has(`${m.id}::${m.threadId}`));
|
|
394
|
+
const toUpdate = messages.filter((m) => existingSet.has(`${m.id}::${m.threadId}`));
|
|
395
|
+
const toMove = messages.filter((m) => {
|
|
396
|
+
const existingRow = existingRows.find((row) => row.id === m.id);
|
|
397
|
+
return existingRow && existingRow.thread_id !== m.threadId;
|
|
398
|
+
});
|
|
399
|
+
const deletePromises = toMove.map((message) => {
|
|
400
|
+
const existingRow = existingRows.find((row) => row.id === message.id);
|
|
401
|
+
if (!existingRow) return Promise.resolve();
|
|
402
|
+
return this.client.command({
|
|
403
|
+
query: `DELETE FROM ${TABLE_MESSAGES} WHERE id = {var_id:String} AND thread_id = {var_old_thread_id:String}`,
|
|
404
|
+
query_params: {
|
|
405
|
+
var_id: message.id,
|
|
406
|
+
var_old_thread_id: existingRow.thread_id
|
|
407
|
+
},
|
|
408
|
+
clickhouse_settings: {
|
|
409
|
+
date_time_input_format: "best_effort",
|
|
410
|
+
use_client_time_zone: 1,
|
|
411
|
+
output_format_json_quote_64bit_integers: 0
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
const updatePromises = toUpdate.map(
|
|
416
|
+
(message) => this.client.command({
|
|
417
|
+
query: `
|
|
418
|
+
ALTER TABLE ${TABLE_MESSAGES}
|
|
419
|
+
UPDATE content = {var_content:String}, role = {var_role:String}, type = {var_type:String}, resourceId = {var_resourceId:String}
|
|
420
|
+
WHERE id = {var_id:String} AND thread_id = {var_thread_id:String}
|
|
421
|
+
`,
|
|
422
|
+
query_params: {
|
|
423
|
+
var_content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
424
|
+
var_role: message.role,
|
|
425
|
+
var_type: message.type || "v2",
|
|
426
|
+
var_resourceId: message.resourceId,
|
|
427
|
+
var_id: message.id,
|
|
428
|
+
var_thread_id: message.threadId
|
|
429
|
+
},
|
|
430
|
+
clickhouse_settings: {
|
|
431
|
+
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
432
|
+
date_time_input_format: "best_effort",
|
|
433
|
+
use_client_time_zone: 1,
|
|
434
|
+
output_format_json_quote_64bit_integers: 0
|
|
435
|
+
}
|
|
436
|
+
})
|
|
437
|
+
);
|
|
438
|
+
await Promise.all([
|
|
439
|
+
// Insert new messages (including moved messages)
|
|
440
|
+
this.client.insert({
|
|
441
|
+
table: TABLE_MESSAGES,
|
|
442
|
+
format: "JSONEachRow",
|
|
443
|
+
values: toInsert.map((message) => ({
|
|
444
|
+
id: message.id,
|
|
445
|
+
thread_id: message.threadId,
|
|
446
|
+
resourceId: message.resourceId,
|
|
447
|
+
content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
448
|
+
createdAt: message.createdAt.toISOString(),
|
|
449
|
+
role: message.role,
|
|
450
|
+
type: message.type || "v2"
|
|
451
|
+
})),
|
|
452
|
+
clickhouse_settings: {
|
|
453
|
+
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
454
|
+
date_time_input_format: "best_effort",
|
|
455
|
+
use_client_time_zone: 1,
|
|
456
|
+
output_format_json_quote_64bit_integers: 0
|
|
457
|
+
}
|
|
458
|
+
}),
|
|
459
|
+
...updatePromises,
|
|
460
|
+
...deletePromises,
|
|
461
|
+
// Update thread's updatedAt timestamp
|
|
462
|
+
this.client.insert({
|
|
463
|
+
table: TABLE_THREADS,
|
|
464
|
+
format: "JSONEachRow",
|
|
465
|
+
values: Array.from(threadIdSet.values()).map((thread) => ({
|
|
466
|
+
id: thread.id,
|
|
467
|
+
resourceId: thread.resourceId,
|
|
468
|
+
title: thread.title,
|
|
469
|
+
metadata: serializeMetadata(thread.metadata),
|
|
470
|
+
createdAt: thread.createdAt,
|
|
471
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
472
|
+
})),
|
|
473
|
+
clickhouse_settings: {
|
|
474
|
+
date_time_input_format: "best_effort",
|
|
475
|
+
use_client_time_zone: 1,
|
|
476
|
+
output_format_json_quote_64bit_integers: 0
|
|
477
|
+
}
|
|
478
|
+
})
|
|
479
|
+
]);
|
|
480
|
+
const list = new MessageList().add(messages, "memory");
|
|
481
|
+
return { messages: list.get.all.db() };
|
|
482
|
+
} catch (error) {
|
|
483
|
+
throw new MastraError(
|
|
484
|
+
{
|
|
485
|
+
id: "CLICKHOUSE_STORAGE_SAVE_MESSAGES_FAILED",
|
|
486
|
+
domain: ErrorDomain.STORAGE,
|
|
487
|
+
category: ErrorCategory.THIRD_PARTY
|
|
488
|
+
},
|
|
489
|
+
error
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
async getThreadById({ threadId }) {
|
|
494
|
+
try {
|
|
495
|
+
const result = await this.client.query({
|
|
496
|
+
query: `SELECT
|
|
497
|
+
id,
|
|
498
|
+
"resourceId",
|
|
499
|
+
title,
|
|
500
|
+
metadata,
|
|
501
|
+
toDateTime64(createdAt, 3) as createdAt,
|
|
502
|
+
toDateTime64(updatedAt, 3) as updatedAt
|
|
503
|
+
FROM "${TABLE_THREADS}"
|
|
504
|
+
WHERE id = {var_id:String}
|
|
505
|
+
ORDER BY updatedAt DESC
|
|
506
|
+
LIMIT 1`,
|
|
507
|
+
query_params: { var_id: threadId },
|
|
508
|
+
clickhouse_settings: {
|
|
509
|
+
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
510
|
+
date_time_input_format: "best_effort",
|
|
511
|
+
date_time_output_format: "iso",
|
|
512
|
+
use_client_time_zone: 1,
|
|
513
|
+
output_format_json_quote_64bit_integers: 0
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
const rows = await result.json();
|
|
517
|
+
const thread = transformRow(rows.data[0]);
|
|
518
|
+
if (!thread) {
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
return {
|
|
522
|
+
...thread,
|
|
523
|
+
metadata: parseMetadata(thread.metadata),
|
|
524
|
+
createdAt: thread.createdAt,
|
|
525
|
+
updatedAt: thread.updatedAt
|
|
526
|
+
};
|
|
527
|
+
} catch (error) {
|
|
528
|
+
throw new MastraError(
|
|
529
|
+
{
|
|
530
|
+
id: "CLICKHOUSE_STORAGE_GET_THREAD_BY_ID_FAILED",
|
|
531
|
+
domain: ErrorDomain.STORAGE,
|
|
532
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
533
|
+
details: { threadId }
|
|
534
|
+
},
|
|
535
|
+
error
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
async saveThread({ thread }) {
|
|
540
|
+
try {
|
|
541
|
+
await this.client.insert({
|
|
542
|
+
table: TABLE_THREADS,
|
|
543
|
+
values: [
|
|
544
|
+
{
|
|
545
|
+
...thread,
|
|
546
|
+
metadata: serializeMetadata(thread.metadata),
|
|
547
|
+
createdAt: thread.createdAt.toISOString(),
|
|
548
|
+
updatedAt: thread.updatedAt.toISOString()
|
|
549
|
+
}
|
|
550
|
+
],
|
|
551
|
+
format: "JSONEachRow",
|
|
552
|
+
clickhouse_settings: {
|
|
553
|
+
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
554
|
+
date_time_input_format: "best_effort",
|
|
555
|
+
use_client_time_zone: 1,
|
|
556
|
+
output_format_json_quote_64bit_integers: 0
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
return thread;
|
|
560
|
+
} catch (error) {
|
|
561
|
+
throw new MastraError(
|
|
562
|
+
{
|
|
563
|
+
id: "CLICKHOUSE_STORAGE_SAVE_THREAD_FAILED",
|
|
564
|
+
domain: ErrorDomain.STORAGE,
|
|
565
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
566
|
+
details: { threadId: thread.id }
|
|
567
|
+
},
|
|
568
|
+
error
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
async updateThread({
|
|
573
|
+
id,
|
|
574
|
+
title,
|
|
575
|
+
metadata
|
|
576
|
+
}) {
|
|
577
|
+
try {
|
|
578
|
+
const existingThread = await this.getThreadById({ threadId: id });
|
|
579
|
+
if (!existingThread) {
|
|
580
|
+
throw new Error(`Thread ${id} not found`);
|
|
581
|
+
}
|
|
582
|
+
const mergedMetadata = {
|
|
583
|
+
...existingThread.metadata,
|
|
584
|
+
...metadata
|
|
585
|
+
};
|
|
586
|
+
const updatedThread = {
|
|
587
|
+
...existingThread,
|
|
588
|
+
title,
|
|
589
|
+
metadata: mergedMetadata,
|
|
590
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
591
|
+
};
|
|
592
|
+
await this.client.insert({
|
|
593
|
+
table: TABLE_THREADS,
|
|
594
|
+
format: "JSONEachRow",
|
|
595
|
+
values: [
|
|
596
|
+
{
|
|
597
|
+
id: updatedThread.id,
|
|
598
|
+
resourceId: updatedThread.resourceId,
|
|
599
|
+
title: updatedThread.title,
|
|
600
|
+
metadata: serializeMetadata(updatedThread.metadata),
|
|
601
|
+
createdAt: updatedThread.createdAt,
|
|
602
|
+
updatedAt: updatedThread.updatedAt.toISOString()
|
|
603
|
+
}
|
|
604
|
+
],
|
|
605
|
+
clickhouse_settings: {
|
|
606
|
+
date_time_input_format: "best_effort",
|
|
607
|
+
use_client_time_zone: 1,
|
|
608
|
+
output_format_json_quote_64bit_integers: 0
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
return updatedThread;
|
|
612
|
+
} catch (error) {
|
|
613
|
+
throw new MastraError(
|
|
614
|
+
{
|
|
615
|
+
id: "CLICKHOUSE_STORAGE_UPDATE_THREAD_FAILED",
|
|
616
|
+
domain: ErrorDomain.STORAGE,
|
|
617
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
618
|
+
details: { threadId: id, title }
|
|
619
|
+
},
|
|
620
|
+
error
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async deleteThread({ threadId }) {
|
|
625
|
+
try {
|
|
626
|
+
await this.client.command({
|
|
627
|
+
query: `DELETE FROM "${TABLE_MESSAGES}" WHERE thread_id = {var_thread_id:String};`,
|
|
628
|
+
query_params: { var_thread_id: threadId },
|
|
629
|
+
clickhouse_settings: {
|
|
630
|
+
output_format_json_quote_64bit_integers: 0
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
await this.client.command({
|
|
634
|
+
query: `DELETE FROM "${TABLE_THREADS}" WHERE id = {var_id:String};`,
|
|
635
|
+
query_params: { var_id: threadId },
|
|
636
|
+
clickhouse_settings: {
|
|
637
|
+
output_format_json_quote_64bit_integers: 0
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
} catch (error) {
|
|
641
|
+
throw new MastraError(
|
|
642
|
+
{
|
|
643
|
+
id: "CLICKHOUSE_STORAGE_DELETE_THREAD_FAILED",
|
|
644
|
+
domain: ErrorDomain.STORAGE,
|
|
645
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
646
|
+
details: { threadId }
|
|
647
|
+
},
|
|
648
|
+
error
|
|
649
|
+
);
|
|
650
|
+
}
|
|
60
651
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
652
|
+
async listThreadsByResourceId(args) {
|
|
653
|
+
const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
|
|
654
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
655
|
+
if (page < 0) {
|
|
656
|
+
throw new MastraError(
|
|
657
|
+
{
|
|
658
|
+
id: "STORAGE_CLICKHOUSE_LIST_THREADS_BY_RESOURCE_ID_INVALID_PAGE",
|
|
659
|
+
domain: ErrorDomain.STORAGE,
|
|
660
|
+
category: ErrorCategory.USER,
|
|
661
|
+
details: { page }
|
|
662
|
+
},
|
|
663
|
+
new Error("page must be >= 0")
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
667
|
+
const { field, direction } = this.parseOrderBy(orderBy);
|
|
668
|
+
try {
|
|
669
|
+
const countResult = await this.client.query({
|
|
670
|
+
query: `SELECT count(DISTINCT id) as total FROM ${TABLE_THREADS} WHERE resourceId = {resourceId:String}`,
|
|
671
|
+
query_params: { resourceId },
|
|
672
|
+
clickhouse_settings: {
|
|
673
|
+
date_time_input_format: "best_effort",
|
|
674
|
+
date_time_output_format: "iso",
|
|
675
|
+
use_client_time_zone: 1,
|
|
676
|
+
output_format_json_quote_64bit_integers: 0
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
const countData = await countResult.json();
|
|
680
|
+
const total = countData.data[0].total;
|
|
681
|
+
if (total === 0) {
|
|
682
|
+
return {
|
|
683
|
+
threads: [],
|
|
684
|
+
total: 0,
|
|
685
|
+
page,
|
|
686
|
+
perPage: perPageForResponse,
|
|
687
|
+
hasMore: false
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
const dataResult = await this.client.query({
|
|
691
|
+
query: `
|
|
692
|
+
WITH ranked_threads AS (
|
|
693
|
+
SELECT
|
|
694
|
+
id,
|
|
695
|
+
resourceId,
|
|
696
|
+
title,
|
|
697
|
+
metadata,
|
|
698
|
+
toDateTime64(createdAt, 3) as createdAt,
|
|
699
|
+
toDateTime64(updatedAt, 3) as updatedAt,
|
|
700
|
+
ROW_NUMBER() OVER (PARTITION BY id ORDER BY updatedAt DESC) as row_num
|
|
701
|
+
FROM ${TABLE_THREADS}
|
|
702
|
+
WHERE resourceId = {resourceId:String}
|
|
703
|
+
)
|
|
704
|
+
SELECT
|
|
705
|
+
id,
|
|
706
|
+
resourceId,
|
|
707
|
+
title,
|
|
708
|
+
metadata,
|
|
709
|
+
createdAt,
|
|
710
|
+
updatedAt
|
|
711
|
+
FROM ranked_threads
|
|
712
|
+
WHERE row_num = 1
|
|
713
|
+
ORDER BY "${field}" ${direction === "DESC" ? "DESC" : "ASC"}
|
|
714
|
+
LIMIT {perPage:Int64} OFFSET {offset:Int64}
|
|
715
|
+
`,
|
|
716
|
+
query_params: {
|
|
717
|
+
resourceId,
|
|
718
|
+
perPage,
|
|
719
|
+
offset
|
|
720
|
+
},
|
|
721
|
+
clickhouse_settings: {
|
|
722
|
+
date_time_input_format: "best_effort",
|
|
723
|
+
date_time_output_format: "iso",
|
|
724
|
+
use_client_time_zone: 1,
|
|
725
|
+
output_format_json_quote_64bit_integers: 0
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
const rows = await dataResult.json();
|
|
729
|
+
const threads = transformRows(rows.data).map((thread) => ({
|
|
730
|
+
...thread,
|
|
731
|
+
metadata: parseMetadata(thread.metadata)
|
|
732
|
+
}));
|
|
733
|
+
return {
|
|
734
|
+
threads,
|
|
735
|
+
total,
|
|
736
|
+
page,
|
|
737
|
+
perPage: perPageForResponse,
|
|
738
|
+
hasMore: offset + perPage < total
|
|
739
|
+
};
|
|
740
|
+
} catch (error) {
|
|
741
|
+
throw new MastraError(
|
|
742
|
+
{
|
|
743
|
+
id: "CLICKHOUSE_STORAGE_LIST_THREADS_BY_RESOURCE_ID_FAILED",
|
|
744
|
+
domain: ErrorDomain.STORAGE,
|
|
745
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
746
|
+
details: { resourceId, page }
|
|
747
|
+
},
|
|
748
|
+
error
|
|
749
|
+
);
|
|
67
750
|
}
|
|
68
|
-
return {
|
|
69
|
-
input: row.input,
|
|
70
|
-
output: row.output,
|
|
71
|
-
result: resultValue,
|
|
72
|
-
agentName: row.agent_name,
|
|
73
|
-
metricName: row.metric_name,
|
|
74
|
-
instructions: row.instructions,
|
|
75
|
-
testInfo: testInfoValue,
|
|
76
|
-
globalRunId: row.global_run_id,
|
|
77
|
-
runId: row.run_id,
|
|
78
|
-
createdAt: row.created_at
|
|
79
|
-
};
|
|
80
751
|
}
|
|
81
|
-
async
|
|
752
|
+
async updateMessages(args) {
|
|
753
|
+
const { messages } = args;
|
|
754
|
+
if (messages.length === 0) {
|
|
755
|
+
return [];
|
|
756
|
+
}
|
|
82
757
|
try {
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
query_params: { var_agent_name: agentName },
|
|
758
|
+
const messageIds = messages.map((m) => m.id);
|
|
759
|
+
const existingResult = await this.client.query({
|
|
760
|
+
query: `SELECT id, content, role, type, "createdAt", thread_id AS "threadId", "resourceId" FROM ${TABLE_MESSAGES} WHERE id IN (${messageIds.map((_, i) => `{id_${i}:String}`).join(",")})`,
|
|
761
|
+
query_params: messageIds.reduce((acc, m, i) => ({ ...acc, [`id_${i}`]: m }), {}),
|
|
88
762
|
clickhouse_settings: {
|
|
89
763
|
date_time_input_format: "best_effort",
|
|
90
764
|
date_time_output_format: "iso",
|
|
@@ -92,131 +766,437 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
92
766
|
output_format_json_quote_64bit_integers: 0
|
|
93
767
|
}
|
|
94
768
|
});
|
|
95
|
-
|
|
769
|
+
const existingRows = await existingResult.json();
|
|
770
|
+
const existingMessages = transformRows(existingRows.data);
|
|
771
|
+
if (existingMessages.length === 0) {
|
|
96
772
|
return [];
|
|
97
773
|
}
|
|
98
|
-
const
|
|
99
|
-
|
|
774
|
+
const parsedExistingMessages = existingMessages.map((msg) => {
|
|
775
|
+
if (typeof msg.content === "string") {
|
|
776
|
+
try {
|
|
777
|
+
msg.content = JSON.parse(msg.content);
|
|
778
|
+
} catch {
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
return msg;
|
|
782
|
+
});
|
|
783
|
+
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
784
|
+
const updatePromises = [];
|
|
785
|
+
for (const existingMessage of parsedExistingMessages) {
|
|
786
|
+
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
787
|
+
if (!updatePayload) continue;
|
|
788
|
+
const { id, ...fieldsToUpdate } = updatePayload;
|
|
789
|
+
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
790
|
+
threadIdsToUpdate.add(existingMessage.threadId);
|
|
791
|
+
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
792
|
+
threadIdsToUpdate.add(updatePayload.threadId);
|
|
793
|
+
}
|
|
794
|
+
const setClauses = [];
|
|
795
|
+
const values = {};
|
|
796
|
+
let paramIdx = 1;
|
|
797
|
+
let newContent = null;
|
|
798
|
+
const updatableFields = { ...fieldsToUpdate };
|
|
799
|
+
if (updatableFields.content) {
|
|
800
|
+
const existingContent = existingMessage.content || {};
|
|
801
|
+
const existingMetadata = existingContent.metadata || {};
|
|
802
|
+
const updateMetadata = updatableFields.content.metadata || {};
|
|
803
|
+
newContent = {
|
|
804
|
+
...existingContent,
|
|
805
|
+
...updatableFields.content,
|
|
806
|
+
// Deep merge metadata
|
|
807
|
+
metadata: {
|
|
808
|
+
...existingMetadata,
|
|
809
|
+
...updateMetadata
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
setClauses.push(`content = {var_content_${paramIdx}:String}`);
|
|
813
|
+
values[`var_content_${paramIdx}`] = JSON.stringify(newContent);
|
|
814
|
+
paramIdx++;
|
|
815
|
+
delete updatableFields.content;
|
|
816
|
+
}
|
|
817
|
+
for (const key in updatableFields) {
|
|
818
|
+
if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
819
|
+
const dbColumn = key === "threadId" ? "thread_id" : key;
|
|
820
|
+
setClauses.push(`"${dbColumn}" = {var_${key}_${paramIdx}:String}`);
|
|
821
|
+
values[`var_${key}_${paramIdx}`] = updatableFields[key];
|
|
822
|
+
paramIdx++;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (setClauses.length > 0) {
|
|
826
|
+
values[`var_id_${paramIdx}`] = id;
|
|
827
|
+
const updateQuery = `
|
|
828
|
+
ALTER TABLE ${TABLE_MESSAGES}
|
|
829
|
+
UPDATE ${setClauses.join(", ")}
|
|
830
|
+
WHERE id = {var_id_${paramIdx}:String}
|
|
831
|
+
`;
|
|
832
|
+
console.info("Updating message:", id, "with query:", updateQuery, "values:", values);
|
|
833
|
+
updatePromises.push(
|
|
834
|
+
this.client.command({
|
|
835
|
+
query: updateQuery,
|
|
836
|
+
query_params: values,
|
|
837
|
+
clickhouse_settings: {
|
|
838
|
+
date_time_input_format: "best_effort",
|
|
839
|
+
use_client_time_zone: 1,
|
|
840
|
+
output_format_json_quote_64bit_integers: 0
|
|
841
|
+
}
|
|
842
|
+
})
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
if (updatePromises.length > 0) {
|
|
847
|
+
await Promise.all(updatePromises);
|
|
848
|
+
}
|
|
849
|
+
await this.client.command({
|
|
850
|
+
query: `OPTIMIZE TABLE ${TABLE_MESSAGES} FINAL`,
|
|
851
|
+
clickhouse_settings: {
|
|
852
|
+
date_time_input_format: "best_effort",
|
|
853
|
+
use_client_time_zone: 1,
|
|
854
|
+
output_format_json_quote_64bit_integers: 0
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
for (const existingMessage of parsedExistingMessages) {
|
|
858
|
+
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
859
|
+
if (!updatePayload) continue;
|
|
860
|
+
const { id, ...fieldsToUpdate } = updatePayload;
|
|
861
|
+
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
862
|
+
const verifyResult = await this.client.query({
|
|
863
|
+
query: `SELECT id, content, role, type, "createdAt", thread_id AS "threadId", "resourceId" FROM ${TABLE_MESSAGES} WHERE id = {messageId:String}`,
|
|
864
|
+
query_params: { messageId: id },
|
|
865
|
+
clickhouse_settings: {
|
|
866
|
+
date_time_input_format: "best_effort",
|
|
867
|
+
date_time_output_format: "iso",
|
|
868
|
+
use_client_time_zone: 1,
|
|
869
|
+
output_format_json_quote_64bit_integers: 0
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
const verifyRows = await verifyResult.json();
|
|
873
|
+
if (verifyRows.data.length > 0) {
|
|
874
|
+
const updatedMessage = transformRows(verifyRows.data)[0];
|
|
875
|
+
if (updatedMessage) {
|
|
876
|
+
let needsRetry = false;
|
|
877
|
+
for (const [key, value] of Object.entries(fieldsToUpdate)) {
|
|
878
|
+
if (key === "content") {
|
|
879
|
+
const expectedContent = typeof value === "string" ? value : JSON.stringify(value);
|
|
880
|
+
const actualContent = typeof updatedMessage.content === "string" ? updatedMessage.content : JSON.stringify(updatedMessage.content);
|
|
881
|
+
if (actualContent !== expectedContent) {
|
|
882
|
+
needsRetry = true;
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
} else if (updatedMessage[key] !== value) {
|
|
886
|
+
needsRetry = true;
|
|
887
|
+
break;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (needsRetry) {
|
|
891
|
+
console.info("Update not applied correctly, retrying with DELETE + INSERT for message:", id);
|
|
892
|
+
await this.client.command({
|
|
893
|
+
query: `DELETE FROM ${TABLE_MESSAGES} WHERE id = {messageId:String}`,
|
|
894
|
+
query_params: { messageId: id },
|
|
895
|
+
clickhouse_settings: {
|
|
896
|
+
date_time_input_format: "best_effort",
|
|
897
|
+
use_client_time_zone: 1,
|
|
898
|
+
output_format_json_quote_64bit_integers: 0
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
let updatedContent = existingMessage.content || {};
|
|
902
|
+
if (fieldsToUpdate.content) {
|
|
903
|
+
const existingContent = existingMessage.content || {};
|
|
904
|
+
const existingMetadata = existingContent.metadata || {};
|
|
905
|
+
const updateMetadata = fieldsToUpdate.content.metadata || {};
|
|
906
|
+
updatedContent = {
|
|
907
|
+
...existingContent,
|
|
908
|
+
...fieldsToUpdate.content,
|
|
909
|
+
metadata: {
|
|
910
|
+
...existingMetadata,
|
|
911
|
+
...updateMetadata
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
const updatedMessageData = {
|
|
916
|
+
...existingMessage,
|
|
917
|
+
...fieldsToUpdate,
|
|
918
|
+
content: updatedContent
|
|
919
|
+
};
|
|
920
|
+
await this.client.insert({
|
|
921
|
+
table: TABLE_MESSAGES,
|
|
922
|
+
format: "JSONEachRow",
|
|
923
|
+
values: [
|
|
924
|
+
{
|
|
925
|
+
id: updatedMessageData.id,
|
|
926
|
+
thread_id: updatedMessageData.threadId,
|
|
927
|
+
resourceId: updatedMessageData.resourceId,
|
|
928
|
+
content: typeof updatedMessageData.content === "string" ? updatedMessageData.content : JSON.stringify(updatedMessageData.content),
|
|
929
|
+
createdAt: updatedMessageData.createdAt.toISOString(),
|
|
930
|
+
role: updatedMessageData.role,
|
|
931
|
+
type: updatedMessageData.type || "v2"
|
|
932
|
+
}
|
|
933
|
+
],
|
|
934
|
+
clickhouse_settings: {
|
|
935
|
+
date_time_input_format: "best_effort",
|
|
936
|
+
use_client_time_zone: 1,
|
|
937
|
+
output_format_json_quote_64bit_integers: 0
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
if (threadIdsToUpdate.size > 0) {
|
|
945
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
946
|
+
const now = (/* @__PURE__ */ new Date()).toISOString().replace("Z", "");
|
|
947
|
+
const threadUpdatePromises = Array.from(threadIdsToUpdate).map(async (threadId) => {
|
|
948
|
+
const threadResult = await this.client.query({
|
|
949
|
+
query: `SELECT id, resourceId, title, metadata, createdAt FROM ${TABLE_THREADS} WHERE id = {threadId:String} ORDER BY updatedAt DESC LIMIT 1`,
|
|
950
|
+
query_params: { threadId },
|
|
951
|
+
clickhouse_settings: {
|
|
952
|
+
date_time_input_format: "best_effort",
|
|
953
|
+
date_time_output_format: "iso",
|
|
954
|
+
use_client_time_zone: 1,
|
|
955
|
+
output_format_json_quote_64bit_integers: 0
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
const threadRows = await threadResult.json();
|
|
959
|
+
if (threadRows.data.length > 0) {
|
|
960
|
+
const existingThread = threadRows.data[0];
|
|
961
|
+
await this.client.command({
|
|
962
|
+
query: `DELETE FROM ${TABLE_THREADS} WHERE id = {threadId:String}`,
|
|
963
|
+
query_params: { threadId },
|
|
964
|
+
clickhouse_settings: {
|
|
965
|
+
date_time_input_format: "best_effort",
|
|
966
|
+
use_client_time_zone: 1,
|
|
967
|
+
output_format_json_quote_64bit_integers: 0
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
await this.client.insert({
|
|
971
|
+
table: TABLE_THREADS,
|
|
972
|
+
format: "JSONEachRow",
|
|
973
|
+
values: [
|
|
974
|
+
{
|
|
975
|
+
id: existingThread.id,
|
|
976
|
+
resourceId: existingThread.resourceId,
|
|
977
|
+
title: existingThread.title,
|
|
978
|
+
metadata: typeof existingThread.metadata === "string" ? existingThread.metadata : serializeMetadata(existingThread.metadata),
|
|
979
|
+
createdAt: existingThread.createdAt,
|
|
980
|
+
updatedAt: now
|
|
981
|
+
}
|
|
982
|
+
],
|
|
983
|
+
clickhouse_settings: {
|
|
984
|
+
date_time_input_format: "best_effort",
|
|
985
|
+
use_client_time_zone: 1,
|
|
986
|
+
output_format_json_quote_64bit_integers: 0
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
await Promise.all(threadUpdatePromises);
|
|
992
|
+
}
|
|
993
|
+
const updatedMessages = [];
|
|
994
|
+
for (const messageId of messageIds) {
|
|
995
|
+
const updatedResult = await this.client.query({
|
|
996
|
+
query: `SELECT id, content, role, type, "createdAt", thread_id AS "threadId", "resourceId" FROM ${TABLE_MESSAGES} WHERE id = {messageId:String}`,
|
|
997
|
+
query_params: { messageId },
|
|
998
|
+
clickhouse_settings: {
|
|
999
|
+
date_time_input_format: "best_effort",
|
|
1000
|
+
date_time_output_format: "iso",
|
|
1001
|
+
use_client_time_zone: 1,
|
|
1002
|
+
output_format_json_quote_64bit_integers: 0
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
const updatedRows = await updatedResult.json();
|
|
1006
|
+
if (updatedRows.data.length > 0) {
|
|
1007
|
+
const message = transformRows(updatedRows.data)[0];
|
|
1008
|
+
if (message) {
|
|
1009
|
+
updatedMessages.push(message);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
return updatedMessages.map((message) => {
|
|
1014
|
+
if (typeof message.content === "string") {
|
|
1015
|
+
try {
|
|
1016
|
+
message.content = JSON.parse(message.content);
|
|
1017
|
+
} catch {
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
return message;
|
|
1021
|
+
});
|
|
100
1022
|
} catch (error) {
|
|
101
|
-
|
|
102
|
-
|
|
1023
|
+
throw new MastraError(
|
|
1024
|
+
{
|
|
1025
|
+
id: "CLICKHOUSE_STORAGE_UPDATE_MESSAGES_FAILED",
|
|
1026
|
+
domain: ErrorDomain.STORAGE,
|
|
1027
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1028
|
+
details: { messageIds: messages.map((m) => m.id).join(",") }
|
|
1029
|
+
},
|
|
1030
|
+
error
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
async getResourceById({ resourceId }) {
|
|
1035
|
+
try {
|
|
1036
|
+
const result = await this.client.query({
|
|
1037
|
+
query: `SELECT id, workingMemory, metadata, createdAt, updatedAt FROM ${TABLE_RESOURCES} WHERE id = {resourceId:String} ORDER BY updatedAt DESC LIMIT 1`,
|
|
1038
|
+
query_params: { resourceId },
|
|
1039
|
+
clickhouse_settings: {
|
|
1040
|
+
date_time_input_format: "best_effort",
|
|
1041
|
+
date_time_output_format: "iso",
|
|
1042
|
+
use_client_time_zone: 1,
|
|
1043
|
+
output_format_json_quote_64bit_integers: 0
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
const rows = await result.json();
|
|
1047
|
+
if (rows.data.length === 0) {
|
|
1048
|
+
return null;
|
|
103
1049
|
}
|
|
104
|
-
|
|
105
|
-
|
|
1050
|
+
const resource = rows.data[0];
|
|
1051
|
+
return {
|
|
1052
|
+
id: resource.id,
|
|
1053
|
+
workingMemory: resource.workingMemory && typeof resource.workingMemory === "object" ? JSON.stringify(resource.workingMemory) : resource.workingMemory,
|
|
1054
|
+
metadata: resource.metadata && typeof resource.metadata === "string" ? JSON.parse(resource.metadata) : resource.metadata,
|
|
1055
|
+
createdAt: new Date(resource.createdAt),
|
|
1056
|
+
updatedAt: new Date(resource.updatedAt)
|
|
1057
|
+
};
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
throw new MastraError(
|
|
1060
|
+
{
|
|
1061
|
+
id: "CLICKHOUSE_STORAGE_GET_RESOURCE_BY_ID_FAILED",
|
|
1062
|
+
domain: ErrorDomain.STORAGE,
|
|
1063
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1064
|
+
details: { resourceId }
|
|
1065
|
+
},
|
|
1066
|
+
error
|
|
1067
|
+
);
|
|
106
1068
|
}
|
|
107
1069
|
}
|
|
108
|
-
async
|
|
1070
|
+
async saveResource({ resource }) {
|
|
109
1071
|
try {
|
|
110
|
-
await this.
|
|
111
|
-
table:
|
|
112
|
-
values: records.map((record) => ({
|
|
113
|
-
...Object.fromEntries(
|
|
114
|
-
Object.entries(record).map(([key, value]) => [
|
|
115
|
-
key,
|
|
116
|
-
TABLE_SCHEMAS[tableName]?.[key]?.type === "timestamp" ? new Date(value).toISOString() : value
|
|
117
|
-
])
|
|
118
|
-
)
|
|
119
|
-
})),
|
|
1072
|
+
await this.client.insert({
|
|
1073
|
+
table: TABLE_RESOURCES,
|
|
120
1074
|
format: "JSONEachRow",
|
|
1075
|
+
values: [
|
|
1076
|
+
{
|
|
1077
|
+
id: resource.id,
|
|
1078
|
+
workingMemory: resource.workingMemory,
|
|
1079
|
+
metadata: JSON.stringify(resource.metadata),
|
|
1080
|
+
createdAt: resource.createdAt.toISOString(),
|
|
1081
|
+
updatedAt: resource.updatedAt.toISOString()
|
|
1082
|
+
}
|
|
1083
|
+
],
|
|
121
1084
|
clickhouse_settings: {
|
|
122
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
123
1085
|
date_time_input_format: "best_effort",
|
|
124
1086
|
use_client_time_zone: 1,
|
|
125
1087
|
output_format_json_quote_64bit_integers: 0
|
|
126
1088
|
}
|
|
127
1089
|
});
|
|
1090
|
+
return resource;
|
|
128
1091
|
} catch (error) {
|
|
129
|
-
|
|
130
|
-
|
|
1092
|
+
throw new MastraError(
|
|
1093
|
+
{
|
|
1094
|
+
id: "CLICKHOUSE_STORAGE_SAVE_RESOURCE_FAILED",
|
|
1095
|
+
domain: ErrorDomain.STORAGE,
|
|
1096
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1097
|
+
details: { resourceId: resource.id }
|
|
1098
|
+
},
|
|
1099
|
+
error
|
|
1100
|
+
);
|
|
131
1101
|
}
|
|
132
1102
|
}
|
|
133
|
-
async
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
perPage,
|
|
138
|
-
attributes,
|
|
139
|
-
filters,
|
|
140
|
-
fromDate,
|
|
141
|
-
toDate
|
|
1103
|
+
async updateResource({
|
|
1104
|
+
resourceId,
|
|
1105
|
+
workingMemory,
|
|
1106
|
+
metadata
|
|
142
1107
|
}) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
if (attributes) {
|
|
156
|
-
Object.entries(attributes).forEach(([key, value]) => {
|
|
157
|
-
conditions.push(`JSONExtractString(attributes, '${key}') = {var_attr_${key}:String}`);
|
|
158
|
-
args[`var_attr_${key}`] = value;
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
if (filters) {
|
|
162
|
-
Object.entries(filters).forEach(([key, value]) => {
|
|
163
|
-
conditions.push(
|
|
164
|
-
`${key} = {var_col_${key}:${COLUMN_TYPES[TABLE_SCHEMAS.mastra_traces?.[key]?.type ?? "text"]}}`
|
|
165
|
-
);
|
|
166
|
-
args[`var_col_${key}`] = value;
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
if (fromDate) {
|
|
170
|
-
conditions.push(`createdAt >= {var_from_date:DateTime64(3)}`);
|
|
171
|
-
args.var_from_date = fromDate.getTime() / 1e3;
|
|
172
|
-
}
|
|
173
|
-
if (toDate) {
|
|
174
|
-
conditions.push(`createdAt <= {var_to_date:DateTime64(3)}`);
|
|
175
|
-
args.var_to_date = toDate.getTime() / 1e3;
|
|
176
|
-
}
|
|
177
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
178
|
-
const result = await this.db.query({
|
|
179
|
-
query: `SELECT *, toDateTime64(createdAt, 3) as createdAt FROM ${TABLE_TRACES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ${limit} OFFSET ${offset}`,
|
|
180
|
-
query_params: args,
|
|
181
|
-
clickhouse_settings: {
|
|
182
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
183
|
-
date_time_input_format: "best_effort",
|
|
184
|
-
date_time_output_format: "iso",
|
|
185
|
-
use_client_time_zone: 1,
|
|
186
|
-
output_format_json_quote_64bit_integers: 0
|
|
1108
|
+
try {
|
|
1109
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1110
|
+
if (!existingResource) {
|
|
1111
|
+
const newResource = {
|
|
1112
|
+
id: resourceId,
|
|
1113
|
+
workingMemory,
|
|
1114
|
+
metadata: metadata || {},
|
|
1115
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1116
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1117
|
+
};
|
|
1118
|
+
return this.saveResource({ resource: newResource });
|
|
187
1119
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
1120
|
+
const updatedResource = {
|
|
1121
|
+
...existingResource,
|
|
1122
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
1123
|
+
metadata: {
|
|
1124
|
+
...existingResource.metadata,
|
|
1125
|
+
...metadata
|
|
1126
|
+
},
|
|
1127
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1128
|
+
};
|
|
1129
|
+
const updateQuery = `
|
|
1130
|
+
ALTER TABLE ${TABLE_RESOURCES}
|
|
1131
|
+
UPDATE workingMemory = {workingMemory:String}, metadata = {metadata:String}, updatedAt = {updatedAt:String}
|
|
1132
|
+
WHERE id = {resourceId:String}
|
|
1133
|
+
`;
|
|
1134
|
+
await this.client.command({
|
|
1135
|
+
query: updateQuery,
|
|
1136
|
+
query_params: {
|
|
1137
|
+
workingMemory: updatedResource.workingMemory,
|
|
1138
|
+
metadata: JSON.stringify(updatedResource.metadata),
|
|
1139
|
+
updatedAt: updatedResource.updatedAt.toISOString().replace("Z", ""),
|
|
1140
|
+
resourceId
|
|
1141
|
+
},
|
|
1142
|
+
clickhouse_settings: {
|
|
1143
|
+
date_time_input_format: "best_effort",
|
|
1144
|
+
use_client_time_zone: 1,
|
|
1145
|
+
output_format_json_quote_64bit_integers: 0
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
await this.client.command({
|
|
1149
|
+
query: `OPTIMIZE TABLE ${TABLE_RESOURCES} FINAL`,
|
|
1150
|
+
clickhouse_settings: {
|
|
1151
|
+
date_time_input_format: "best_effort",
|
|
1152
|
+
use_client_time_zone: 1,
|
|
1153
|
+
output_format_json_quote_64bit_integers: 0
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
return updatedResource;
|
|
1157
|
+
} catch (error) {
|
|
1158
|
+
throw new MastraError(
|
|
1159
|
+
{
|
|
1160
|
+
id: "CLICKHOUSE_STORAGE_UPDATE_RESOURCE_FAILED",
|
|
1161
|
+
domain: ErrorDomain.STORAGE,
|
|
1162
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1163
|
+
details: { resourceId }
|
|
1164
|
+
},
|
|
1165
|
+
error
|
|
1166
|
+
);
|
|
191
1167
|
}
|
|
192
|
-
const resp = await result.json();
|
|
193
|
-
const rows = resp.data;
|
|
194
|
-
return rows.map((row) => ({
|
|
195
|
-
id: row.id,
|
|
196
|
-
parentSpanId: row.parentSpanId,
|
|
197
|
-
traceId: row.traceId,
|
|
198
|
-
name: row.name,
|
|
199
|
-
scope: row.scope,
|
|
200
|
-
kind: row.kind,
|
|
201
|
-
status: safelyParseJSON(row.status),
|
|
202
|
-
events: safelyParseJSON(row.events),
|
|
203
|
-
links: safelyParseJSON(row.links),
|
|
204
|
-
attributes: safelyParseJSON(row.attributes),
|
|
205
|
-
startTime: row.startTime,
|
|
206
|
-
endTime: row.endTime,
|
|
207
|
-
other: safelyParseJSON(row.other),
|
|
208
|
-
createdAt: row.createdAt
|
|
209
|
-
}));
|
|
210
1168
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
1169
|
+
};
|
|
1170
|
+
var StoreOperationsClickhouse = class extends StoreOperations {
|
|
1171
|
+
ttl;
|
|
1172
|
+
client;
|
|
1173
|
+
constructor({ client, ttl }) {
|
|
1174
|
+
super();
|
|
1175
|
+
this.ttl = ttl;
|
|
1176
|
+
this.client = client;
|
|
215
1177
|
}
|
|
216
|
-
async
|
|
217
|
-
await this.
|
|
218
|
-
query: `
|
|
1178
|
+
async hasColumn(table, column) {
|
|
1179
|
+
const result = await this.client.query({
|
|
1180
|
+
query: `DESCRIBE TABLE ${table}`,
|
|
1181
|
+
format: "JSONEachRow"
|
|
219
1182
|
});
|
|
1183
|
+
const columns = await result.json();
|
|
1184
|
+
return columns.some((c) => c.name === column);
|
|
1185
|
+
}
|
|
1186
|
+
getSqlType(type) {
|
|
1187
|
+
switch (type) {
|
|
1188
|
+
case "text":
|
|
1189
|
+
return "String";
|
|
1190
|
+
case "timestamp":
|
|
1191
|
+
return "DateTime64(3)";
|
|
1192
|
+
case "integer":
|
|
1193
|
+
case "bigint":
|
|
1194
|
+
return "Int64";
|
|
1195
|
+
case "jsonb":
|
|
1196
|
+
return "String";
|
|
1197
|
+
default:
|
|
1198
|
+
return super.getSqlType(type);
|
|
1199
|
+
}
|
|
220
1200
|
}
|
|
221
1201
|
async createTable({
|
|
222
1202
|
tableName,
|
|
@@ -226,32 +1206,33 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
226
1206
|
const columns = Object.entries(schema).map(([name, def]) => {
|
|
227
1207
|
const constraints = [];
|
|
228
1208
|
if (!def.nullable) constraints.push("NOT NULL");
|
|
1209
|
+
if (name === "metadata" && def.type === "text" && def.nullable) {
|
|
1210
|
+
constraints.push("DEFAULT '{}'");
|
|
1211
|
+
}
|
|
229
1212
|
const columnTtl = this.ttl?.[tableName]?.columns?.[name];
|
|
230
1213
|
return `"${name}" ${COLUMN_TYPES[def.type]} ${constraints.join(" ")} ${columnTtl ? `TTL toDateTime(${columnTtl.ttlKey ?? "createdAt"}) + INTERVAL ${columnTtl.interval} ${columnTtl.unit}` : ""}`;
|
|
231
1214
|
}).join(",\n");
|
|
232
1215
|
const rowTtl = this.ttl?.[tableName]?.row;
|
|
233
1216
|
const sql = tableName === TABLE_WORKFLOW_SNAPSHOT ? `
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
`;
|
|
254
|
-
await this.db.query({
|
|
1217
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
1218
|
+
${["id String"].concat(columns)}
|
|
1219
|
+
)
|
|
1220
|
+
ENGINE = ${TABLE_ENGINES[tableName] ?? "MergeTree()"}
|
|
1221
|
+
PRIMARY KEY (createdAt, run_id, workflow_name)
|
|
1222
|
+
ORDER BY (createdAt, run_id, workflow_name)
|
|
1223
|
+
${rowTtl ? `TTL toDateTime(${rowTtl.ttlKey ?? "createdAt"}) + INTERVAL ${rowTtl.interval} ${rowTtl.unit}` : ""}
|
|
1224
|
+
SETTINGS index_granularity = 8192
|
|
1225
|
+
` : `
|
|
1226
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
1227
|
+
${columns}
|
|
1228
|
+
)
|
|
1229
|
+
ENGINE = ${TABLE_ENGINES[tableName] ?? "MergeTree()"}
|
|
1230
|
+
PRIMARY KEY (createdAt, ${"id"})
|
|
1231
|
+
ORDER BY (createdAt, ${"id"})
|
|
1232
|
+
${this.ttl?.[tableName]?.row ? `TTL toDateTime(createdAt) + INTERVAL ${this.ttl[tableName].row.interval} ${this.ttl[tableName].row.unit}` : ""}
|
|
1233
|
+
SETTINGS index_granularity = 8192
|
|
1234
|
+
`;
|
|
1235
|
+
await this.client.query({
|
|
255
1236
|
query: sql,
|
|
256
1237
|
clickhouse_settings: {
|
|
257
1238
|
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
@@ -262,13 +1243,59 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
262
1243
|
}
|
|
263
1244
|
});
|
|
264
1245
|
} catch (error) {
|
|
265
|
-
|
|
266
|
-
|
|
1246
|
+
throw new MastraError(
|
|
1247
|
+
{
|
|
1248
|
+
id: "CLICKHOUSE_STORAGE_CREATE_TABLE_FAILED",
|
|
1249
|
+
domain: ErrorDomain.STORAGE,
|
|
1250
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1251
|
+
details: { tableName }
|
|
1252
|
+
},
|
|
1253
|
+
error
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
async alterTable({
|
|
1258
|
+
tableName,
|
|
1259
|
+
schema,
|
|
1260
|
+
ifNotExists
|
|
1261
|
+
}) {
|
|
1262
|
+
try {
|
|
1263
|
+
const describeSql = `DESCRIBE TABLE ${tableName}`;
|
|
1264
|
+
const result = await this.client.query({
|
|
1265
|
+
query: describeSql
|
|
1266
|
+
});
|
|
1267
|
+
const rows = await result.json();
|
|
1268
|
+
const existingColumnNames = new Set(rows.data.map((row) => row.name.toLowerCase()));
|
|
1269
|
+
for (const columnName of ifNotExists) {
|
|
1270
|
+
if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
|
|
1271
|
+
const columnDef = schema[columnName];
|
|
1272
|
+
let sqlType = this.getSqlType(columnDef.type);
|
|
1273
|
+
if (columnDef.nullable !== false) {
|
|
1274
|
+
sqlType = `Nullable(${sqlType})`;
|
|
1275
|
+
}
|
|
1276
|
+
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
1277
|
+
const alterSql = `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS "${columnName}" ${sqlType} ${defaultValue}`.trim();
|
|
1278
|
+
await this.client.query({
|
|
1279
|
+
query: alterSql
|
|
1280
|
+
});
|
|
1281
|
+
this.logger?.debug?.(`Added column ${columnName} to table ${tableName}`);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
} catch (error) {
|
|
1285
|
+
throw new MastraError(
|
|
1286
|
+
{
|
|
1287
|
+
id: "CLICKHOUSE_STORAGE_ALTER_TABLE_FAILED",
|
|
1288
|
+
domain: ErrorDomain.STORAGE,
|
|
1289
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1290
|
+
details: { tableName }
|
|
1291
|
+
},
|
|
1292
|
+
error
|
|
1293
|
+
);
|
|
267
1294
|
}
|
|
268
1295
|
}
|
|
269
1296
|
async clearTable({ tableName }) {
|
|
270
1297
|
try {
|
|
271
|
-
await this.
|
|
1298
|
+
await this.client.query({
|
|
272
1299
|
query: `TRUNCATE TABLE ${tableName}`,
|
|
273
1300
|
clickhouse_settings: {
|
|
274
1301
|
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
@@ -279,19 +1306,33 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
279
1306
|
}
|
|
280
1307
|
});
|
|
281
1308
|
} catch (error) {
|
|
282
|
-
|
|
283
|
-
|
|
1309
|
+
throw new MastraError(
|
|
1310
|
+
{
|
|
1311
|
+
id: "CLICKHOUSE_STORAGE_CLEAR_TABLE_FAILED",
|
|
1312
|
+
domain: ErrorDomain.STORAGE,
|
|
1313
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1314
|
+
details: { tableName }
|
|
1315
|
+
},
|
|
1316
|
+
error
|
|
1317
|
+
);
|
|
284
1318
|
}
|
|
285
1319
|
}
|
|
1320
|
+
async dropTable({ tableName }) {
|
|
1321
|
+
await this.client.query({
|
|
1322
|
+
query: `DROP TABLE IF EXISTS ${tableName}`
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
286
1325
|
async insert({ tableName, record }) {
|
|
1326
|
+
const createdAt = (record.createdAt || record.created_at || /* @__PURE__ */ new Date()).toISOString();
|
|
1327
|
+
const updatedAt = (record.updatedAt || /* @__PURE__ */ new Date()).toISOString();
|
|
287
1328
|
try {
|
|
288
|
-
await this.
|
|
1329
|
+
const result = await this.client.insert({
|
|
289
1330
|
table: tableName,
|
|
290
1331
|
values: [
|
|
291
1332
|
{
|
|
292
1333
|
...record,
|
|
293
|
-
createdAt
|
|
294
|
-
updatedAt
|
|
1334
|
+
createdAt,
|
|
1335
|
+
updatedAt
|
|
295
1336
|
}
|
|
296
1337
|
],
|
|
297
1338
|
format: "JSONEachRow",
|
|
@@ -302,13 +1343,55 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
302
1343
|
use_client_time_zone: 1
|
|
303
1344
|
}
|
|
304
1345
|
});
|
|
1346
|
+
console.info("INSERT RESULT", result);
|
|
1347
|
+
} catch (error) {
|
|
1348
|
+
throw new MastraError(
|
|
1349
|
+
{
|
|
1350
|
+
id: "CLICKHOUSE_STORAGE_INSERT_FAILED",
|
|
1351
|
+
domain: ErrorDomain.STORAGE,
|
|
1352
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1353
|
+
details: { tableName }
|
|
1354
|
+
},
|
|
1355
|
+
error
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
async batchInsert({ tableName, records }) {
|
|
1360
|
+
const recordsToBeInserted = records.map((record) => ({
|
|
1361
|
+
...Object.fromEntries(
|
|
1362
|
+
Object.entries(record).map(([key, value]) => [
|
|
1363
|
+
key,
|
|
1364
|
+
TABLE_SCHEMAS[tableName]?.[key]?.type === "timestamp" ? new Date(value).toISOString() : value
|
|
1365
|
+
])
|
|
1366
|
+
)
|
|
1367
|
+
}));
|
|
1368
|
+
try {
|
|
1369
|
+
await this.client.insert({
|
|
1370
|
+
table: tableName,
|
|
1371
|
+
values: recordsToBeInserted,
|
|
1372
|
+
format: "JSONEachRow",
|
|
1373
|
+
clickhouse_settings: {
|
|
1374
|
+
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
1375
|
+
date_time_input_format: "best_effort",
|
|
1376
|
+
use_client_time_zone: 1,
|
|
1377
|
+
output_format_json_quote_64bit_integers: 0
|
|
1378
|
+
}
|
|
1379
|
+
});
|
|
305
1380
|
} catch (error) {
|
|
306
|
-
|
|
307
|
-
|
|
1381
|
+
throw new MastraError(
|
|
1382
|
+
{
|
|
1383
|
+
id: "CLICKHOUSE_STORAGE_BATCH_INSERT_FAILED",
|
|
1384
|
+
domain: ErrorDomain.STORAGE,
|
|
1385
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1386
|
+
details: { tableName }
|
|
1387
|
+
},
|
|
1388
|
+
error
|
|
1389
|
+
);
|
|
308
1390
|
}
|
|
309
1391
|
}
|
|
310
1392
|
async load({ tableName, keys }) {
|
|
311
1393
|
try {
|
|
1394
|
+
const engine = TABLE_ENGINES[tableName] ?? "MergeTree()";
|
|
312
1395
|
const keyEntries = Object.entries(keys);
|
|
313
1396
|
const conditions = keyEntries.map(
|
|
314
1397
|
([key]) => `"${key}" = {var_${key}:${COLUMN_TYPES[TABLE_SCHEMAS[tableName]?.[key]?.type ?? "text"]}}`
|
|
@@ -316,8 +1399,10 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
316
1399
|
const values = keyEntries.reduce((acc, [key, value]) => {
|
|
317
1400
|
return { ...acc, [`var_${key}`]: value };
|
|
318
1401
|
}, {});
|
|
319
|
-
const
|
|
320
|
-
|
|
1402
|
+
const hasUpdatedAt = TABLE_SCHEMAS[tableName]?.updatedAt;
|
|
1403
|
+
const selectClause = `SELECT *, toDateTime64(createdAt, 3) as createdAt${hasUpdatedAt ? ", toDateTime64(updatedAt, 3) as updatedAt" : ""}`;
|
|
1404
|
+
const result = await this.client.query({
|
|
1405
|
+
query: `${selectClause} FROM ${tableName} ${engine.startsWith("ReplacingMergeTree") ? "FINAL" : ""} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
|
|
321
1406
|
query_params: values,
|
|
322
1407
|
clickhouse_settings: {
|
|
323
1408
|
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
@@ -344,24 +1429,57 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
344
1429
|
const data = transformRow(rows.data[0]);
|
|
345
1430
|
return data;
|
|
346
1431
|
} catch (error) {
|
|
347
|
-
|
|
348
|
-
|
|
1432
|
+
throw new MastraError(
|
|
1433
|
+
{
|
|
1434
|
+
id: "CLICKHOUSE_STORAGE_LOAD_FAILED",
|
|
1435
|
+
domain: ErrorDomain.STORAGE,
|
|
1436
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1437
|
+
details: { tableName }
|
|
1438
|
+
},
|
|
1439
|
+
error
|
|
1440
|
+
);
|
|
349
1441
|
}
|
|
350
1442
|
}
|
|
351
|
-
|
|
1443
|
+
};
|
|
1444
|
+
var ScoresStorageClickhouse = class extends ScoresStorage {
|
|
1445
|
+
client;
|
|
1446
|
+
operations;
|
|
1447
|
+
constructor({ client, operations }) {
|
|
1448
|
+
super();
|
|
1449
|
+
this.client = client;
|
|
1450
|
+
this.operations = operations;
|
|
1451
|
+
}
|
|
1452
|
+
transformScoreRow(row) {
|
|
1453
|
+
const scorer = safelyParseJSON(row.scorer);
|
|
1454
|
+
const preprocessStepResult = safelyParseJSON(row.preprocessStepResult);
|
|
1455
|
+
const analyzeStepResult = safelyParseJSON(row.analyzeStepResult);
|
|
1456
|
+
const metadata = safelyParseJSON(row.metadata);
|
|
1457
|
+
const input = safelyParseJSON(row.input);
|
|
1458
|
+
const output = safelyParseJSON(row.output);
|
|
1459
|
+
const additionalContext = safelyParseJSON(row.additionalContext);
|
|
1460
|
+
const requestContext = safelyParseJSON(row.requestContext);
|
|
1461
|
+
const entity = safelyParseJSON(row.entity);
|
|
1462
|
+
return {
|
|
1463
|
+
...row,
|
|
1464
|
+
scorer,
|
|
1465
|
+
preprocessStepResult,
|
|
1466
|
+
analyzeStepResult,
|
|
1467
|
+
metadata,
|
|
1468
|
+
input,
|
|
1469
|
+
output,
|
|
1470
|
+
additionalContext,
|
|
1471
|
+
requestContext,
|
|
1472
|
+
entity,
|
|
1473
|
+
createdAt: new Date(row.createdAt),
|
|
1474
|
+
updatedAt: new Date(row.updatedAt)
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
async getScoreById({ id }) {
|
|
352
1478
|
try {
|
|
353
|
-
const result = await this.
|
|
354
|
-
query: `SELECT
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
title,
|
|
358
|
-
metadata,
|
|
359
|
-
toDateTime64(createdAt, 3) as createdAt,
|
|
360
|
-
toDateTime64(updatedAt, 3) as updatedAt
|
|
361
|
-
FROM "${TABLE_THREADS}"
|
|
362
|
-
FINAL
|
|
363
|
-
WHERE id = {var_id:String}`,
|
|
364
|
-
query_params: { var_id: threadId },
|
|
1479
|
+
const result = await this.client.query({
|
|
1480
|
+
query: `SELECT * FROM ${TABLE_SCORERS} WHERE id = {var_id:String}`,
|
|
1481
|
+
query_params: { var_id: id },
|
|
1482
|
+
format: "JSONEachRow",
|
|
365
1483
|
clickhouse_settings: {
|
|
366
1484
|
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
367
1485
|
date_time_input_format: "best_effort",
|
|
@@ -370,223 +1488,269 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
370
1488
|
output_format_json_quote_64bit_integers: 0
|
|
371
1489
|
}
|
|
372
1490
|
});
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
if (!thread) {
|
|
1491
|
+
const resultJson = await result.json();
|
|
1492
|
+
if (!Array.isArray(resultJson) || resultJson.length === 0) {
|
|
376
1493
|
return null;
|
|
377
1494
|
}
|
|
378
|
-
return
|
|
379
|
-
...thread,
|
|
380
|
-
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
381
|
-
createdAt: thread.createdAt,
|
|
382
|
-
updatedAt: thread.updatedAt
|
|
383
|
-
};
|
|
1495
|
+
return this.transformScoreRow(resultJson[0]);
|
|
384
1496
|
} catch (error) {
|
|
385
|
-
|
|
386
|
-
|
|
1497
|
+
throw new MastraError(
|
|
1498
|
+
{
|
|
1499
|
+
id: "CLICKHOUSE_STORAGE_GET_SCORE_BY_ID_FAILED",
|
|
1500
|
+
domain: ErrorDomain.STORAGE,
|
|
1501
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1502
|
+
details: { scoreId: id }
|
|
1503
|
+
},
|
|
1504
|
+
error
|
|
1505
|
+
);
|
|
387
1506
|
}
|
|
388
1507
|
}
|
|
389
|
-
async
|
|
1508
|
+
async saveScore(score) {
|
|
1509
|
+
let parsedScore;
|
|
390
1510
|
try {
|
|
391
|
-
|
|
392
|
-
query: `SELECT
|
|
393
|
-
id,
|
|
394
|
-
"resourceId",
|
|
395
|
-
title,
|
|
396
|
-
metadata,
|
|
397
|
-
toDateTime64(createdAt, 3) as createdAt,
|
|
398
|
-
toDateTime64(updatedAt, 3) as updatedAt
|
|
399
|
-
FROM "${TABLE_THREADS}"
|
|
400
|
-
WHERE "resourceId" = {var_resourceId:String}`,
|
|
401
|
-
query_params: { var_resourceId: resourceId },
|
|
402
|
-
clickhouse_settings: {
|
|
403
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
404
|
-
date_time_input_format: "best_effort",
|
|
405
|
-
date_time_output_format: "iso",
|
|
406
|
-
use_client_time_zone: 1,
|
|
407
|
-
output_format_json_quote_64bit_integers: 0
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
const rows = await result.json();
|
|
411
|
-
const threads = transformRows(rows.data);
|
|
412
|
-
return threads.map((thread) => ({
|
|
413
|
-
...thread,
|
|
414
|
-
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
415
|
-
createdAt: thread.createdAt,
|
|
416
|
-
updatedAt: thread.updatedAt
|
|
417
|
-
}));
|
|
1511
|
+
parsedScore = saveScorePayloadSchema.parse(score);
|
|
418
1512
|
} catch (error) {
|
|
419
|
-
|
|
420
|
-
|
|
1513
|
+
throw new MastraError(
|
|
1514
|
+
{
|
|
1515
|
+
id: "CLICKHOUSE_STORAGE_SAVE_SCORE_FAILED_INVALID_SCORE_PAYLOAD",
|
|
1516
|
+
domain: ErrorDomain.STORAGE,
|
|
1517
|
+
category: ErrorCategory.USER,
|
|
1518
|
+
details: { scoreId: score.id }
|
|
1519
|
+
},
|
|
1520
|
+
error
|
|
1521
|
+
);
|
|
421
1522
|
}
|
|
422
|
-
}
|
|
423
|
-
async saveThread({ thread }) {
|
|
424
1523
|
try {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
updatedAt: thread.updatedAt.toISOString()
|
|
432
|
-
}
|
|
433
|
-
],
|
|
1524
|
+
const record = {
|
|
1525
|
+
...parsedScore
|
|
1526
|
+
};
|
|
1527
|
+
await this.client.insert({
|
|
1528
|
+
table: TABLE_SCORERS,
|
|
1529
|
+
values: [record],
|
|
434
1530
|
format: "JSONEachRow",
|
|
435
1531
|
clickhouse_settings: {
|
|
436
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
437
1532
|
date_time_input_format: "best_effort",
|
|
438
1533
|
use_client_time_zone: 1,
|
|
439
1534
|
output_format_json_quote_64bit_integers: 0
|
|
440
1535
|
}
|
|
441
1536
|
});
|
|
442
|
-
return
|
|
1537
|
+
return { score };
|
|
443
1538
|
} catch (error) {
|
|
444
|
-
|
|
445
|
-
|
|
1539
|
+
throw new MastraError(
|
|
1540
|
+
{
|
|
1541
|
+
id: "CLICKHOUSE_STORAGE_SAVE_SCORE_FAILED",
|
|
1542
|
+
domain: ErrorDomain.STORAGE,
|
|
1543
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1544
|
+
details: { scoreId: score.id }
|
|
1545
|
+
},
|
|
1546
|
+
error
|
|
1547
|
+
);
|
|
446
1548
|
}
|
|
447
1549
|
}
|
|
448
|
-
async
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
metadata
|
|
1550
|
+
async listScoresByRunId({
|
|
1551
|
+
runId,
|
|
1552
|
+
pagination
|
|
452
1553
|
}) {
|
|
453
1554
|
try {
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
1555
|
+
const countResult = await this.client.query({
|
|
1556
|
+
query: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} WHERE runId = {var_runId:String}`,
|
|
1557
|
+
query_params: { var_runId: runId },
|
|
1558
|
+
format: "JSONEachRow"
|
|
1559
|
+
});
|
|
1560
|
+
const countRows = await countResult.json();
|
|
1561
|
+
let total = 0;
|
|
1562
|
+
if (Array.isArray(countRows) && countRows.length > 0 && countRows[0]) {
|
|
1563
|
+
const countObj = countRows[0];
|
|
1564
|
+
total = Number(countObj.count);
|
|
457
1565
|
}
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
1566
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1567
|
+
if (!total) {
|
|
1568
|
+
return {
|
|
1569
|
+
pagination: {
|
|
1570
|
+
total: 0,
|
|
1571
|
+
page,
|
|
1572
|
+
perPage: perPageInput,
|
|
1573
|
+
hasMore: false
|
|
1574
|
+
},
|
|
1575
|
+
scores: []
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1579
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1580
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
1581
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1582
|
+
const result = await this.client.query({
|
|
1583
|
+
query: `SELECT * FROM ${TABLE_SCORERS} WHERE runId = {var_runId:String} ORDER BY createdAt DESC LIMIT {var_limit:Int64} OFFSET {var_offset:Int64}`,
|
|
1584
|
+
query_params: {
|
|
1585
|
+
var_runId: runId,
|
|
1586
|
+
var_limit: limitValue,
|
|
1587
|
+
var_offset: start
|
|
1588
|
+
},
|
|
476
1589
|
format: "JSONEachRow",
|
|
477
1590
|
clickhouse_settings: {
|
|
478
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
479
1591
|
date_time_input_format: "best_effort",
|
|
1592
|
+
date_time_output_format: "iso",
|
|
480
1593
|
use_client_time_zone: 1,
|
|
481
1594
|
output_format_json_quote_64bit_integers: 0
|
|
482
1595
|
}
|
|
483
1596
|
});
|
|
484
|
-
|
|
1597
|
+
const rows = await result.json();
|
|
1598
|
+
const scores = Array.isArray(rows) ? rows.map((row) => this.transformScoreRow(row)) : [];
|
|
1599
|
+
return {
|
|
1600
|
+
pagination: {
|
|
1601
|
+
total,
|
|
1602
|
+
page,
|
|
1603
|
+
perPage: perPageForResponse,
|
|
1604
|
+
hasMore: end < total
|
|
1605
|
+
},
|
|
1606
|
+
scores
|
|
1607
|
+
};
|
|
485
1608
|
} catch (error) {
|
|
486
|
-
|
|
487
|
-
|
|
1609
|
+
throw new MastraError(
|
|
1610
|
+
{
|
|
1611
|
+
id: "CLICKHOUSE_STORAGE_GET_SCORES_BY_RUN_ID_FAILED",
|
|
1612
|
+
domain: ErrorDomain.STORAGE,
|
|
1613
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1614
|
+
details: { runId }
|
|
1615
|
+
},
|
|
1616
|
+
error
|
|
1617
|
+
);
|
|
488
1618
|
}
|
|
489
1619
|
}
|
|
490
|
-
async
|
|
1620
|
+
async listScoresByScorerId({
|
|
1621
|
+
scorerId,
|
|
1622
|
+
entityId,
|
|
1623
|
+
entityType,
|
|
1624
|
+
source,
|
|
1625
|
+
pagination
|
|
1626
|
+
}) {
|
|
1627
|
+
let whereClause = `scorerId = {var_scorerId:String}`;
|
|
1628
|
+
if (entityId) {
|
|
1629
|
+
whereClause += ` AND entityId = {var_entityId:String}`;
|
|
1630
|
+
}
|
|
1631
|
+
if (entityType) {
|
|
1632
|
+
whereClause += ` AND entityType = {var_entityType:String}`;
|
|
1633
|
+
}
|
|
1634
|
+
if (source) {
|
|
1635
|
+
whereClause += ` AND source = {var_source:String}`;
|
|
1636
|
+
}
|
|
491
1637
|
try {
|
|
492
|
-
await this.
|
|
493
|
-
query: `
|
|
494
|
-
query_params: {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
1638
|
+
const countResult = await this.client.query({
|
|
1639
|
+
query: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} WHERE ${whereClause}`,
|
|
1640
|
+
query_params: {
|
|
1641
|
+
var_scorerId: scorerId,
|
|
1642
|
+
var_entityId: entityId,
|
|
1643
|
+
var_entityType: entityType,
|
|
1644
|
+
var_source: source
|
|
1645
|
+
},
|
|
1646
|
+
format: "JSONEachRow"
|
|
498
1647
|
});
|
|
499
|
-
await
|
|
500
|
-
|
|
501
|
-
|
|
1648
|
+
const countRows = await countResult.json();
|
|
1649
|
+
let total = 0;
|
|
1650
|
+
if (Array.isArray(countRows) && countRows.length > 0 && countRows[0]) {
|
|
1651
|
+
const countObj = countRows[0];
|
|
1652
|
+
total = Number(countObj.count);
|
|
1653
|
+
}
|
|
1654
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1655
|
+
if (!total) {
|
|
1656
|
+
return {
|
|
1657
|
+
pagination: {
|
|
1658
|
+
total: 0,
|
|
1659
|
+
page,
|
|
1660
|
+
perPage: perPageInput,
|
|
1661
|
+
hasMore: false
|
|
1662
|
+
},
|
|
1663
|
+
scores: []
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1667
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1668
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
1669
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1670
|
+
const result = await this.client.query({
|
|
1671
|
+
query: `SELECT * FROM ${TABLE_SCORERS} WHERE ${whereClause} ORDER BY createdAt DESC LIMIT {var_limit:Int64} OFFSET {var_offset:Int64}`,
|
|
1672
|
+
query_params: {
|
|
1673
|
+
var_scorerId: scorerId,
|
|
1674
|
+
var_limit: limitValue,
|
|
1675
|
+
var_offset: start,
|
|
1676
|
+
var_entityId: entityId,
|
|
1677
|
+
var_entityType: entityType,
|
|
1678
|
+
var_source: source
|
|
1679
|
+
},
|
|
1680
|
+
format: "JSONEachRow",
|
|
502
1681
|
clickhouse_settings: {
|
|
1682
|
+
date_time_input_format: "best_effort",
|
|
1683
|
+
date_time_output_format: "iso",
|
|
1684
|
+
use_client_time_zone: 1,
|
|
503
1685
|
output_format_json_quote_64bit_integers: 0
|
|
504
1686
|
}
|
|
505
1687
|
});
|
|
1688
|
+
const rows = await result.json();
|
|
1689
|
+
const scores = Array.isArray(rows) ? rows.map((row) => this.transformScoreRow(row)) : [];
|
|
1690
|
+
return {
|
|
1691
|
+
pagination: {
|
|
1692
|
+
total,
|
|
1693
|
+
page,
|
|
1694
|
+
perPage: perPageForResponse,
|
|
1695
|
+
hasMore: end < total
|
|
1696
|
+
},
|
|
1697
|
+
scores
|
|
1698
|
+
};
|
|
506
1699
|
} catch (error) {
|
|
507
|
-
|
|
508
|
-
|
|
1700
|
+
throw new MastraError(
|
|
1701
|
+
{
|
|
1702
|
+
id: "CLICKHOUSE_STORAGE_GET_SCORES_BY_SCORER_ID_FAILED",
|
|
1703
|
+
domain: ErrorDomain.STORAGE,
|
|
1704
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1705
|
+
details: { scorerId }
|
|
1706
|
+
},
|
|
1707
|
+
error
|
|
1708
|
+
);
|
|
509
1709
|
}
|
|
510
1710
|
}
|
|
511
|
-
async
|
|
1711
|
+
async listScoresByEntityId({
|
|
1712
|
+
entityId,
|
|
1713
|
+
entityType,
|
|
1714
|
+
pagination
|
|
1715
|
+
}) {
|
|
512
1716
|
try {
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
m.type as type,
|
|
533
|
-
m.createdAt as createdAt,
|
|
534
|
-
m.updatedAt as updatedAt,
|
|
535
|
-
m.thread_id AS "threadId"
|
|
536
|
-
FROM ordered_messages m
|
|
537
|
-
WHERE m.id = ANY({var_include:Array(String)})
|
|
538
|
-
OR EXISTS (
|
|
539
|
-
SELECT 1 FROM ordered_messages target
|
|
540
|
-
WHERE target.id = ANY({var_include:Array(String)})
|
|
541
|
-
AND (
|
|
542
|
-
-- Get previous messages based on the max withPreviousMessages
|
|
543
|
-
(m.row_num <= target.row_num + {var_withPreviousMessages:Int64} AND m.row_num > target.row_num)
|
|
544
|
-
OR
|
|
545
|
-
-- Get next messages based on the max withNextMessages
|
|
546
|
-
(m.row_num >= target.row_num - {var_withNextMessages:Int64} AND m.row_num < target.row_num)
|
|
547
|
-
)
|
|
548
|
-
)
|
|
549
|
-
ORDER BY m."createdAt" DESC
|
|
550
|
-
`,
|
|
551
|
-
query_params: {
|
|
552
|
-
var_thread_id: threadId,
|
|
553
|
-
var_include: include.map((i) => i.id),
|
|
554
|
-
var_withPreviousMessages: Math.max(...include.map((i) => i.withPreviousMessages || 0)),
|
|
555
|
-
var_withNextMessages: Math.max(...include.map((i) => i.withNextMessages || 0))
|
|
1717
|
+
const countResult = await this.client.query({
|
|
1718
|
+
query: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} WHERE entityId = {var_entityId:String} AND entityType = {var_entityType:String}`,
|
|
1719
|
+
query_params: { var_entityId: entityId, var_entityType: entityType },
|
|
1720
|
+
format: "JSONEachRow"
|
|
1721
|
+
});
|
|
1722
|
+
const countRows = await countResult.json();
|
|
1723
|
+
let total = 0;
|
|
1724
|
+
if (Array.isArray(countRows) && countRows.length > 0 && countRows[0]) {
|
|
1725
|
+
const countObj = countRows[0];
|
|
1726
|
+
total = Number(countObj.count);
|
|
1727
|
+
}
|
|
1728
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1729
|
+
if (!total) {
|
|
1730
|
+
return {
|
|
1731
|
+
pagination: {
|
|
1732
|
+
total: 0,
|
|
1733
|
+
page,
|
|
1734
|
+
perPage: perPageInput,
|
|
1735
|
+
hasMore: false
|
|
556
1736
|
},
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
date_time_input_format: "best_effort",
|
|
560
|
-
date_time_output_format: "iso",
|
|
561
|
-
use_client_time_zone: 1,
|
|
562
|
-
output_format_json_quote_64bit_integers: 0
|
|
563
|
-
}
|
|
564
|
-
});
|
|
565
|
-
const rows2 = await includeResult.json();
|
|
566
|
-
messages.push(...transformRows(rows2.data));
|
|
1737
|
+
scores: []
|
|
1738
|
+
};
|
|
567
1739
|
}
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
type,
|
|
575
|
-
toDateTime64(createdAt, 3) as createdAt,
|
|
576
|
-
thread_id AS "threadId"
|
|
577
|
-
FROM "${TABLE_MESSAGES}"
|
|
578
|
-
WHERE thread_id = {threadId:String}
|
|
579
|
-
AND id NOT IN ({exclude:Array(String)})
|
|
580
|
-
ORDER BY "createdAt" DESC
|
|
581
|
-
LIMIT {limit:Int64}
|
|
582
|
-
`,
|
|
1740
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1741
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1742
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
1743
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1744
|
+
const result = await this.client.query({
|
|
1745
|
+
query: `SELECT * FROM ${TABLE_SCORERS} WHERE entityId = {var_entityId:String} AND entityType = {var_entityType:String} ORDER BY createdAt DESC LIMIT {var_limit:Int64} OFFSET {var_offset:Int64}`,
|
|
583
1746
|
query_params: {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
1747
|
+
var_entityId: entityId,
|
|
1748
|
+
var_entityType: entityType,
|
|
1749
|
+
var_limit: limitValue,
|
|
1750
|
+
var_offset: start
|
|
587
1751
|
},
|
|
1752
|
+
format: "JSONEachRow",
|
|
588
1753
|
clickhouse_settings: {
|
|
589
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
590
1754
|
date_time_input_format: "best_effort",
|
|
591
1755
|
date_time_output_format: "iso",
|
|
592
1756
|
use_client_time_zone: 1,
|
|
@@ -594,80 +1758,154 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
594
1758
|
}
|
|
595
1759
|
});
|
|
596
1760
|
const rows = await result.json();
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
}
|
|
607
|
-
return messages;
|
|
1761
|
+
const scores = Array.isArray(rows) ? rows.map((row) => this.transformScoreRow(row)) : [];
|
|
1762
|
+
return {
|
|
1763
|
+
pagination: {
|
|
1764
|
+
total,
|
|
1765
|
+
page,
|
|
1766
|
+
perPage: perPageForResponse,
|
|
1767
|
+
hasMore: end < total
|
|
1768
|
+
},
|
|
1769
|
+
scores
|
|
1770
|
+
};
|
|
608
1771
|
} catch (error) {
|
|
609
|
-
|
|
610
|
-
|
|
1772
|
+
throw new MastraError(
|
|
1773
|
+
{
|
|
1774
|
+
id: "CLICKHOUSE_STORAGE_GET_SCORES_BY_ENTITY_ID_FAILED",
|
|
1775
|
+
domain: ErrorDomain.STORAGE,
|
|
1776
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1777
|
+
details: { entityId, entityType }
|
|
1778
|
+
},
|
|
1779
|
+
error
|
|
1780
|
+
);
|
|
611
1781
|
}
|
|
612
1782
|
}
|
|
613
|
-
async
|
|
614
|
-
|
|
1783
|
+
async listScoresBySpan({
|
|
1784
|
+
traceId,
|
|
1785
|
+
spanId,
|
|
1786
|
+
pagination
|
|
1787
|
+
}) {
|
|
615
1788
|
try {
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
1789
|
+
const countResult = await this.client.query({
|
|
1790
|
+
query: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} WHERE traceId = {var_traceId:String} AND spanId = {var_spanId:String}`,
|
|
1791
|
+
query_params: {
|
|
1792
|
+
var_traceId: traceId,
|
|
1793
|
+
var_spanId: spanId
|
|
1794
|
+
},
|
|
1795
|
+
format: "JSONEachRow"
|
|
1796
|
+
});
|
|
1797
|
+
const countRows = await countResult.json();
|
|
1798
|
+
let total = 0;
|
|
1799
|
+
if (Array.isArray(countRows) && countRows.length > 0 && countRows[0]) {
|
|
1800
|
+
const countObj = countRows[0];
|
|
1801
|
+
total = Number(countObj.count);
|
|
619
1802
|
}
|
|
620
|
-
const
|
|
621
|
-
if (!
|
|
622
|
-
|
|
1803
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1804
|
+
if (!total) {
|
|
1805
|
+
return {
|
|
1806
|
+
pagination: {
|
|
1807
|
+
total: 0,
|
|
1808
|
+
page,
|
|
1809
|
+
perPage: perPageInput,
|
|
1810
|
+
hasMore: false
|
|
1811
|
+
},
|
|
1812
|
+
scores: []
|
|
1813
|
+
};
|
|
623
1814
|
}
|
|
624
|
-
|
|
625
|
-
|
|
1815
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1816
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1817
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
1818
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1819
|
+
const result = await this.client.query({
|
|
1820
|
+
query: `SELECT * FROM ${TABLE_SCORERS} WHERE traceId = {var_traceId:String} AND spanId = {var_spanId:String} ORDER BY createdAt DESC LIMIT {var_limit:Int64} OFFSET {var_offset:Int64}`,
|
|
1821
|
+
query_params: {
|
|
1822
|
+
var_traceId: traceId,
|
|
1823
|
+
var_spanId: spanId,
|
|
1824
|
+
var_limit: limitValue,
|
|
1825
|
+
var_offset: start
|
|
1826
|
+
},
|
|
626
1827
|
format: "JSONEachRow",
|
|
627
|
-
values: messages.map((message) => ({
|
|
628
|
-
id: message.id,
|
|
629
|
-
thread_id: threadId,
|
|
630
|
-
content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
631
|
-
createdAt: message.createdAt.toISOString(),
|
|
632
|
-
role: message.role,
|
|
633
|
-
type: message.type
|
|
634
|
-
})),
|
|
635
1828
|
clickhouse_settings: {
|
|
636
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
637
1829
|
date_time_input_format: "best_effort",
|
|
1830
|
+
date_time_output_format: "iso",
|
|
638
1831
|
use_client_time_zone: 1,
|
|
639
1832
|
output_format_json_quote_64bit_integers: 0
|
|
640
1833
|
}
|
|
641
1834
|
});
|
|
642
|
-
|
|
1835
|
+
const rows = await result.json();
|
|
1836
|
+
const scores = Array.isArray(rows) ? rows.map((row) => this.transformScoreRow(row)) : [];
|
|
1837
|
+
return {
|
|
1838
|
+
pagination: {
|
|
1839
|
+
total,
|
|
1840
|
+
page,
|
|
1841
|
+
perPage: perPageForResponse,
|
|
1842
|
+
hasMore: end < total
|
|
1843
|
+
},
|
|
1844
|
+
scores
|
|
1845
|
+
};
|
|
643
1846
|
} catch (error) {
|
|
644
|
-
|
|
645
|
-
|
|
1847
|
+
throw new MastraError(
|
|
1848
|
+
{
|
|
1849
|
+
id: "CLICKHOUSE_STORAGE_GET_SCORES_BY_SPAN_FAILED",
|
|
1850
|
+
domain: ErrorDomain.STORAGE,
|
|
1851
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1852
|
+
details: { traceId, spanId }
|
|
1853
|
+
},
|
|
1854
|
+
error
|
|
1855
|
+
);
|
|
646
1856
|
}
|
|
647
1857
|
}
|
|
1858
|
+
};
|
|
1859
|
+
var WorkflowsStorageClickhouse = class extends WorkflowsStorage {
|
|
1860
|
+
client;
|
|
1861
|
+
operations;
|
|
1862
|
+
constructor({ client, operations }) {
|
|
1863
|
+
super();
|
|
1864
|
+
this.operations = operations;
|
|
1865
|
+
this.client = client;
|
|
1866
|
+
}
|
|
1867
|
+
updateWorkflowResults({
|
|
1868
|
+
// workflowName,
|
|
1869
|
+
// runId,
|
|
1870
|
+
// stepId,
|
|
1871
|
+
// result,
|
|
1872
|
+
// requestContext,
|
|
1873
|
+
}) {
|
|
1874
|
+
throw new Error("Method not implemented.");
|
|
1875
|
+
}
|
|
1876
|
+
updateWorkflowState({
|
|
1877
|
+
// workflowName,
|
|
1878
|
+
// runId,
|
|
1879
|
+
// opts,
|
|
1880
|
+
}) {
|
|
1881
|
+
throw new Error("Method not implemented.");
|
|
1882
|
+
}
|
|
648
1883
|
async persistWorkflowSnapshot({
|
|
649
1884
|
workflowName,
|
|
650
1885
|
runId,
|
|
1886
|
+
resourceId,
|
|
651
1887
|
snapshot
|
|
652
1888
|
}) {
|
|
653
1889
|
try {
|
|
654
|
-
const currentSnapshot = await this.load({
|
|
1890
|
+
const currentSnapshot = await this.operations.load({
|
|
655
1891
|
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
656
1892
|
keys: { workflow_name: workflowName, run_id: runId }
|
|
657
1893
|
});
|
|
658
1894
|
const now = /* @__PURE__ */ new Date();
|
|
659
1895
|
const persisting = currentSnapshot ? {
|
|
660
1896
|
...currentSnapshot,
|
|
1897
|
+
resourceId,
|
|
661
1898
|
snapshot: JSON.stringify(snapshot),
|
|
662
1899
|
updatedAt: now.toISOString()
|
|
663
1900
|
} : {
|
|
664
1901
|
workflow_name: workflowName,
|
|
665
1902
|
run_id: runId,
|
|
1903
|
+
resourceId,
|
|
666
1904
|
snapshot: JSON.stringify(snapshot),
|
|
667
1905
|
createdAt: now.toISOString(),
|
|
668
1906
|
updatedAt: now.toISOString()
|
|
669
1907
|
};
|
|
670
|
-
await this.
|
|
1908
|
+
await this.client.insert({
|
|
671
1909
|
table: TABLE_WORKFLOW_SNAPSHOT,
|
|
672
1910
|
format: "JSONEachRow",
|
|
673
1911
|
values: [persisting],
|
|
@@ -679,8 +1917,15 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
679
1917
|
}
|
|
680
1918
|
});
|
|
681
1919
|
} catch (error) {
|
|
682
|
-
|
|
683
|
-
|
|
1920
|
+
throw new MastraError(
|
|
1921
|
+
{
|
|
1922
|
+
id: "CLICKHOUSE_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
|
|
1923
|
+
domain: ErrorDomain.STORAGE,
|
|
1924
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1925
|
+
details: { workflowName, runId }
|
|
1926
|
+
},
|
|
1927
|
+
error
|
|
1928
|
+
);
|
|
684
1929
|
}
|
|
685
1930
|
}
|
|
686
1931
|
async loadWorkflowSnapshot({
|
|
@@ -688,7 +1933,7 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
688
1933
|
runId
|
|
689
1934
|
}) {
|
|
690
1935
|
try {
|
|
691
|
-
const result = await this.load({
|
|
1936
|
+
const result = await this.operations.load({
|
|
692
1937
|
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
693
1938
|
keys: {
|
|
694
1939
|
workflow_name: workflowName,
|
|
@@ -700,8 +1945,15 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
700
1945
|
}
|
|
701
1946
|
return result.snapshot;
|
|
702
1947
|
} catch (error) {
|
|
703
|
-
|
|
704
|
-
|
|
1948
|
+
throw new MastraError(
|
|
1949
|
+
{
|
|
1950
|
+
id: "CLICKHOUSE_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
|
|
1951
|
+
domain: ErrorDomain.STORAGE,
|
|
1952
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1953
|
+
details: { workflowName, runId }
|
|
1954
|
+
},
|
|
1955
|
+
error
|
|
1956
|
+
);
|
|
705
1957
|
}
|
|
706
1958
|
}
|
|
707
1959
|
parseWorkflowRun(row) {
|
|
@@ -722,13 +1974,14 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
722
1974
|
resourceId: row.resourceId
|
|
723
1975
|
};
|
|
724
1976
|
}
|
|
725
|
-
async
|
|
1977
|
+
async listWorkflowRuns({
|
|
726
1978
|
workflowName,
|
|
727
1979
|
fromDate,
|
|
728
1980
|
toDate,
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
resourceId
|
|
1981
|
+
page,
|
|
1982
|
+
perPage,
|
|
1983
|
+
resourceId,
|
|
1984
|
+
status
|
|
732
1985
|
} = {}) {
|
|
733
1986
|
try {
|
|
734
1987
|
const conditions = [];
|
|
@@ -737,8 +1990,12 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
737
1990
|
conditions.push(`workflow_name = {var_workflow_name:String}`);
|
|
738
1991
|
values.var_workflow_name = workflowName;
|
|
739
1992
|
}
|
|
1993
|
+
if (status) {
|
|
1994
|
+
conditions.push(`JSONExtractString(snapshot, 'status') = {var_status:String}`);
|
|
1995
|
+
values.var_status = status;
|
|
1996
|
+
}
|
|
740
1997
|
if (resourceId) {
|
|
741
|
-
const hasResourceId = await this.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
|
|
1998
|
+
const hasResourceId = await this.operations.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
|
|
742
1999
|
if (hasResourceId) {
|
|
743
2000
|
conditions.push(`resourceId = {var_resourceId:String}`);
|
|
744
2001
|
values.var_resourceId = resourceId;
|
|
@@ -755,11 +2012,14 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
755
2012
|
values.var_to_date = toDate.getTime() / 1e3;
|
|
756
2013
|
}
|
|
757
2014
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
758
|
-
const
|
|
759
|
-
const
|
|
2015
|
+
const usePagination = perPage !== void 0 && page !== void 0;
|
|
2016
|
+
const normalizedPerPage = usePagination ? normalizePerPage(perPage, Number.MAX_SAFE_INTEGER) : 0;
|
|
2017
|
+
const offset = usePagination ? page * normalizedPerPage : 0;
|
|
2018
|
+
const limitClause = usePagination ? `LIMIT ${normalizedPerPage}` : "";
|
|
2019
|
+
const offsetClause = usePagination ? `OFFSET ${offset}` : "";
|
|
760
2020
|
let total = 0;
|
|
761
|
-
if (
|
|
762
|
-
const countResult = await this.
|
|
2021
|
+
if (usePagination) {
|
|
2022
|
+
const countResult = await this.client.query({
|
|
763
2023
|
query: `SELECT COUNT(*) as count FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith("ReplacingMergeTree") ? "FINAL" : ""} ${whereClause}`,
|
|
764
2024
|
query_params: values,
|
|
765
2025
|
format: "JSONEachRow"
|
|
@@ -767,21 +2027,21 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
767
2027
|
const countRows = await countResult.json();
|
|
768
2028
|
total = Number(countRows[0]?.count ?? 0);
|
|
769
2029
|
}
|
|
770
|
-
const result = await this.
|
|
2030
|
+
const result = await this.client.query({
|
|
771
2031
|
query: `
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
2032
|
+
SELECT
|
|
2033
|
+
workflow_name,
|
|
2034
|
+
run_id,
|
|
2035
|
+
snapshot,
|
|
2036
|
+
toDateTime64(createdAt, 3) as createdAt,
|
|
2037
|
+
toDateTime64(updatedAt, 3) as updatedAt,
|
|
2038
|
+
resourceId
|
|
2039
|
+
FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith("ReplacingMergeTree") ? "FINAL" : ""}
|
|
2040
|
+
${whereClause}
|
|
2041
|
+
ORDER BY createdAt DESC
|
|
2042
|
+
${limitClause}
|
|
2043
|
+
${offsetClause}
|
|
2044
|
+
`,
|
|
785
2045
|
query_params: values,
|
|
786
2046
|
format: "JSONEachRow"
|
|
787
2047
|
});
|
|
@@ -792,8 +2052,15 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
792
2052
|
});
|
|
793
2053
|
return { runs, total: total || runs.length };
|
|
794
2054
|
} catch (error) {
|
|
795
|
-
|
|
796
|
-
|
|
2055
|
+
throw new MastraError(
|
|
2056
|
+
{
|
|
2057
|
+
id: "CLICKHOUSE_STORAGE_LIST_WORKFLOW_RUNS_FAILED",
|
|
2058
|
+
domain: ErrorDomain.STORAGE,
|
|
2059
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2060
|
+
details: { workflowName: workflowName ?? "", resourceId: resourceId ?? "" }
|
|
2061
|
+
},
|
|
2062
|
+
error
|
|
2063
|
+
);
|
|
797
2064
|
}
|
|
798
2065
|
}
|
|
799
2066
|
async getWorkflowRunById({
|
|
@@ -812,18 +2079,19 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
812
2079
|
values.var_workflow_name = workflowName;
|
|
813
2080
|
}
|
|
814
2081
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
815
|
-
const result = await this.
|
|
2082
|
+
const result = await this.client.query({
|
|
816
2083
|
query: `
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
2084
|
+
SELECT
|
|
2085
|
+
workflow_name,
|
|
2086
|
+
run_id,
|
|
2087
|
+
snapshot,
|
|
2088
|
+
toDateTime64(createdAt, 3) as createdAt,
|
|
2089
|
+
toDateTime64(updatedAt, 3) as updatedAt,
|
|
2090
|
+
resourceId
|
|
2091
|
+
FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith("ReplacingMergeTree") ? "FINAL" : ""}
|
|
2092
|
+
${whereClause}
|
|
2093
|
+
ORDER BY createdAt DESC LIMIT 1
|
|
2094
|
+
`,
|
|
827
2095
|
query_params: values,
|
|
828
2096
|
format: "JSONEachRow"
|
|
829
2097
|
});
|
|
@@ -833,17 +2101,230 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
833
2101
|
}
|
|
834
2102
|
return this.parseWorkflowRun(resultJson[0]);
|
|
835
2103
|
} catch (error) {
|
|
836
|
-
|
|
837
|
-
|
|
2104
|
+
throw new MastraError(
|
|
2105
|
+
{
|
|
2106
|
+
id: "CLICKHOUSE_STORAGE_GET_WORKFLOW_RUN_BY_ID_FAILED",
|
|
2107
|
+
domain: ErrorDomain.STORAGE,
|
|
2108
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2109
|
+
details: { runId: runId ?? "", workflowName: workflowName ?? "" }
|
|
2110
|
+
},
|
|
2111
|
+
error
|
|
2112
|
+
);
|
|
838
2113
|
}
|
|
839
2114
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
2115
|
+
};
|
|
2116
|
+
|
|
2117
|
+
// src/storage/index.ts
|
|
2118
|
+
var ClickhouseStore = class extends MastraStorage {
|
|
2119
|
+
db;
|
|
2120
|
+
ttl = {};
|
|
2121
|
+
stores;
|
|
2122
|
+
constructor(config) {
|
|
2123
|
+
super({ id: config.id, name: "ClickhouseStore" });
|
|
2124
|
+
this.db = createClient({
|
|
2125
|
+
url: config.url,
|
|
2126
|
+
username: config.username,
|
|
2127
|
+
password: config.password,
|
|
2128
|
+
clickhouse_settings: {
|
|
2129
|
+
date_time_input_format: "best_effort",
|
|
2130
|
+
date_time_output_format: "iso",
|
|
2131
|
+
// This is crucial
|
|
2132
|
+
use_client_time_zone: 1,
|
|
2133
|
+
output_format_json_quote_64bit_integers: 0
|
|
2134
|
+
}
|
|
844
2135
|
});
|
|
845
|
-
|
|
846
|
-
|
|
2136
|
+
this.ttl = config.ttl;
|
|
2137
|
+
const operations = new StoreOperationsClickhouse({ client: this.db, ttl: this.ttl });
|
|
2138
|
+
const workflows = new WorkflowsStorageClickhouse({ client: this.db, operations });
|
|
2139
|
+
const scores = new ScoresStorageClickhouse({ client: this.db, operations });
|
|
2140
|
+
const memory = new MemoryStorageClickhouse({ client: this.db, operations });
|
|
2141
|
+
this.stores = {
|
|
2142
|
+
operations,
|
|
2143
|
+
workflows,
|
|
2144
|
+
scores,
|
|
2145
|
+
memory
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
get supports() {
|
|
2149
|
+
return {
|
|
2150
|
+
selectByIncludeResourceScope: true,
|
|
2151
|
+
resourceWorkingMemory: true,
|
|
2152
|
+
hasColumn: true,
|
|
2153
|
+
createTable: true,
|
|
2154
|
+
deleteMessages: false,
|
|
2155
|
+
listScoresBySpan: true
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
async batchInsert({ tableName, records }) {
|
|
2159
|
+
await this.stores.operations.batchInsert({ tableName, records });
|
|
2160
|
+
}
|
|
2161
|
+
async optimizeTable({ tableName }) {
|
|
2162
|
+
try {
|
|
2163
|
+
await this.db.command({
|
|
2164
|
+
query: `OPTIMIZE TABLE ${tableName} FINAL`
|
|
2165
|
+
});
|
|
2166
|
+
} catch (error) {
|
|
2167
|
+
throw new MastraError(
|
|
2168
|
+
{
|
|
2169
|
+
id: "CLICKHOUSE_STORAGE_OPTIMIZE_TABLE_FAILED",
|
|
2170
|
+
domain: ErrorDomain.STORAGE,
|
|
2171
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2172
|
+
details: { tableName }
|
|
2173
|
+
},
|
|
2174
|
+
error
|
|
2175
|
+
);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
async materializeTtl({ tableName }) {
|
|
2179
|
+
try {
|
|
2180
|
+
await this.db.command({
|
|
2181
|
+
query: `ALTER TABLE ${tableName} MATERIALIZE TTL;`
|
|
2182
|
+
});
|
|
2183
|
+
} catch (error) {
|
|
2184
|
+
throw new MastraError(
|
|
2185
|
+
{
|
|
2186
|
+
id: "CLICKHOUSE_STORAGE_MATERIALIZE_TTL_FAILED",
|
|
2187
|
+
domain: ErrorDomain.STORAGE,
|
|
2188
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2189
|
+
details: { tableName }
|
|
2190
|
+
},
|
|
2191
|
+
error
|
|
2192
|
+
);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
async createTable({
|
|
2196
|
+
tableName,
|
|
2197
|
+
schema
|
|
2198
|
+
}) {
|
|
2199
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
2200
|
+
}
|
|
2201
|
+
async dropTable({ tableName }) {
|
|
2202
|
+
return this.stores.operations.dropTable({ tableName });
|
|
2203
|
+
}
|
|
2204
|
+
async alterTable({
|
|
2205
|
+
tableName,
|
|
2206
|
+
schema,
|
|
2207
|
+
ifNotExists
|
|
2208
|
+
}) {
|
|
2209
|
+
return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
2210
|
+
}
|
|
2211
|
+
async clearTable({ tableName }) {
|
|
2212
|
+
return this.stores.operations.clearTable({ tableName });
|
|
2213
|
+
}
|
|
2214
|
+
async insert({ tableName, record }) {
|
|
2215
|
+
return this.stores.operations.insert({ tableName, record });
|
|
2216
|
+
}
|
|
2217
|
+
async load({ tableName, keys }) {
|
|
2218
|
+
return this.stores.operations.load({ tableName, keys });
|
|
2219
|
+
}
|
|
2220
|
+
async updateWorkflowResults({
|
|
2221
|
+
workflowName,
|
|
2222
|
+
runId,
|
|
2223
|
+
stepId,
|
|
2224
|
+
result,
|
|
2225
|
+
requestContext
|
|
2226
|
+
}) {
|
|
2227
|
+
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
|
|
2228
|
+
}
|
|
2229
|
+
async updateWorkflowState({
|
|
2230
|
+
workflowName,
|
|
2231
|
+
runId,
|
|
2232
|
+
opts
|
|
2233
|
+
}) {
|
|
2234
|
+
return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
|
|
2235
|
+
}
|
|
2236
|
+
async persistWorkflowSnapshot({
|
|
2237
|
+
workflowName,
|
|
2238
|
+
runId,
|
|
2239
|
+
resourceId,
|
|
2240
|
+
snapshot
|
|
2241
|
+
}) {
|
|
2242
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
|
|
2243
|
+
}
|
|
2244
|
+
async loadWorkflowSnapshot({
|
|
2245
|
+
workflowName,
|
|
2246
|
+
runId
|
|
2247
|
+
}) {
|
|
2248
|
+
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
2249
|
+
}
|
|
2250
|
+
async listWorkflowRuns(args = {}) {
|
|
2251
|
+
return this.stores.workflows.listWorkflowRuns(args);
|
|
2252
|
+
}
|
|
2253
|
+
async getWorkflowRunById({
|
|
2254
|
+
runId,
|
|
2255
|
+
workflowName
|
|
2256
|
+
}) {
|
|
2257
|
+
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
2258
|
+
}
|
|
2259
|
+
async getThreadById({ threadId }) {
|
|
2260
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
2261
|
+
}
|
|
2262
|
+
async saveThread({ thread }) {
|
|
2263
|
+
return this.stores.memory.saveThread({ thread });
|
|
2264
|
+
}
|
|
2265
|
+
async updateThread({
|
|
2266
|
+
id,
|
|
2267
|
+
title,
|
|
2268
|
+
metadata
|
|
2269
|
+
}) {
|
|
2270
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
2271
|
+
}
|
|
2272
|
+
async deleteThread({ threadId }) {
|
|
2273
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
2274
|
+
}
|
|
2275
|
+
async saveMessages(args) {
|
|
2276
|
+
return this.stores.memory.saveMessages(args);
|
|
2277
|
+
}
|
|
2278
|
+
async updateMessages(args) {
|
|
2279
|
+
return this.stores.memory.updateMessages(args);
|
|
2280
|
+
}
|
|
2281
|
+
async getResourceById({ resourceId }) {
|
|
2282
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
2283
|
+
}
|
|
2284
|
+
async saveResource({ resource }) {
|
|
2285
|
+
return this.stores.memory.saveResource({ resource });
|
|
2286
|
+
}
|
|
2287
|
+
async updateResource({
|
|
2288
|
+
resourceId,
|
|
2289
|
+
workingMemory,
|
|
2290
|
+
metadata
|
|
2291
|
+
}) {
|
|
2292
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
2293
|
+
}
|
|
2294
|
+
async getScoreById({ id }) {
|
|
2295
|
+
return this.stores.scores.getScoreById({ id });
|
|
2296
|
+
}
|
|
2297
|
+
async saveScore(_score) {
|
|
2298
|
+
return this.stores.scores.saveScore(_score);
|
|
2299
|
+
}
|
|
2300
|
+
async listScoresByRunId({
|
|
2301
|
+
runId,
|
|
2302
|
+
pagination
|
|
2303
|
+
}) {
|
|
2304
|
+
return this.stores.scores.listScoresByRunId({ runId, pagination });
|
|
2305
|
+
}
|
|
2306
|
+
async listScoresByEntityId({
|
|
2307
|
+
entityId,
|
|
2308
|
+
entityType,
|
|
2309
|
+
pagination
|
|
2310
|
+
}) {
|
|
2311
|
+
return this.stores.scores.listScoresByEntityId({ entityId, entityType, pagination });
|
|
2312
|
+
}
|
|
2313
|
+
async listScoresByScorerId({
|
|
2314
|
+
scorerId,
|
|
2315
|
+
pagination,
|
|
2316
|
+
entityId,
|
|
2317
|
+
entityType,
|
|
2318
|
+
source
|
|
2319
|
+
}) {
|
|
2320
|
+
return this.stores.scores.listScoresByScorerId({ scorerId, pagination, entityId, entityType, source });
|
|
2321
|
+
}
|
|
2322
|
+
async listScoresBySpan({
|
|
2323
|
+
traceId,
|
|
2324
|
+
spanId,
|
|
2325
|
+
pagination
|
|
2326
|
+
}) {
|
|
2327
|
+
return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
|
|
847
2328
|
}
|
|
848
2329
|
async close() {
|
|
849
2330
|
await this.db.close();
|
|
@@ -851,3 +2332,5 @@ var ClickhouseStore = class extends MastraStorage {
|
|
|
851
2332
|
};
|
|
852
2333
|
|
|
853
2334
|
export { COLUMN_TYPES, ClickhouseStore, TABLE_ENGINES };
|
|
2335
|
+
//# sourceMappingURL=index.js.map
|
|
2336
|
+
//# sourceMappingURL=index.js.map
|