@mastra/clickhouse 0.0.0-trigger-playground-ui-package-20250506151043 → 0.0.0-unified-sidebar-20251010130811

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