@mastra/clickhouse 0.12.0 → 0.12.1

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