@mastra/clickhouse 0.0.0-vector-query-sources-20250516172905 → 0.0.0-vector-query-tool-provider-options-20250828222356

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