@mastra/clickhouse 0.0.0-vnext-inngest-20250508122351 → 0.0.0-vnext-20251104230439

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