@mastra/clickhouse 1.0.0-beta.0 → 1.0.0-beta.2

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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createClient } from '@clickhouse/client';
2
2
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
3
- import { TABLE_SPANS, TABLE_RESOURCES, TABLE_SCORERS, TABLE_THREADS, TABLE_TRACES, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, MastraStorage, StoreOperations, TABLE_SCHEMAS, WorkflowsStorage, normalizePerPage, ScoresStorage, safelyParseJSON, calculatePagination, MemoryStorage } from '@mastra/core/storage';
3
+ import { TABLE_SPANS, TABLE_RESOURCES, TABLE_SCORERS, TABLE_THREADS, TABLE_TRACES, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, MastraStorage, StoreOperations, TABLE_SCHEMAS, WorkflowsStorage, normalizePerPage, ScoresStorage, transformScoreRow, SCORERS_SCHEMA, calculatePagination, MemoryStorage, safelyParseJSON } from '@mastra/core/storage';
4
4
  import { MessageList } from '@mastra/core/agent';
5
5
  import { saveScorePayloadSchema } from '@mastra/core/evals';
6
6
 
@@ -125,6 +125,8 @@ var MemoryStorageClickhouse = class extends MemoryStorage {
125
125
  }
126
126
  async listMessages(args) {
127
127
  const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
128
+ const rawThreadIds = Array.isArray(threadId) ? threadId : [threadId];
129
+ const threadIds = rawThreadIds.filter((id) => id !== void 0 && id !== null).map((id) => (typeof id === "string" ? id : String(id)).trim()).filter((id) => id.length > 0);
128
130
  if (page < 0) {
129
131
  throw new MastraError(
130
132
  {
@@ -136,20 +138,21 @@ var MemoryStorageClickhouse = class extends MemoryStorage {
136
138
  new Error("page must be >= 0")
137
139
  );
138
140
  }
139
- if (!threadId.trim()) {
141
+ if (threadIds.length === 0) {
140
142
  throw new MastraError(
141
143
  {
142
144
  id: "STORAGE_CLICKHOUSE_LIST_MESSAGES_INVALID_THREAD_ID",
143
145
  domain: ErrorDomain.STORAGE,
144
146
  category: ErrorCategory.THIRD_PARTY,
145
- details: { threadId }
147
+ details: { threadId: Array.isArray(threadId) ? JSON.stringify(threadId) : String(threadId) }
146
148
  },
147
- new Error("threadId must be a non-empty string")
149
+ new Error("threadId must be a non-empty string or array of non-empty strings")
148
150
  );
149
151
  }
150
152
  const perPageForQuery = normalizePerPage(perPageInput, 40);
151
153
  const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPageForQuery);
152
154
  try {
155
+ const threadCondition = threadIds.length === 1 ? `thread_id = {threadId0:String}` : `thread_id IN (${threadIds.map((_, i) => `{threadId${i}:String}`).join(", ")})`;
153
156
  let dataQuery = `
154
157
  SELECT
155
158
  id,
@@ -160,9 +163,12 @@ var MemoryStorageClickhouse = class extends MemoryStorage {
160
163
  thread_id AS "threadId",
161
164
  resourceId
162
165
  FROM ${TABLE_MESSAGES}
163
- WHERE thread_id = {threadId:String}
166
+ WHERE ${threadCondition}
164
167
  `;
165
- const dataParams = { threadId };
168
+ const dataParams = {};
169
+ threadIds.forEach((tid, i) => {
170
+ dataParams[`threadId${i}`] = tid;
171
+ });
166
172
  if (resourceId) {
167
173
  dataQuery += ` AND resourceId = {resourceId:String}`;
168
174
  dataParams.resourceId = resourceId;
@@ -197,8 +203,11 @@ var MemoryStorageClickhouse = class extends MemoryStorage {
197
203
  const rows = await result.json();
198
204
  const paginatedMessages = transformRows(rows.data);
199
205
  const paginatedCount = paginatedMessages.length;
200
- let countQuery = `SELECT count() as total FROM ${TABLE_MESSAGES} WHERE thread_id = {threadId:String}`;
201
- const countParams = { threadId };
206
+ let countQuery = `SELECT count() as total FROM ${TABLE_MESSAGES} WHERE ${threadCondition}`;
207
+ const countParams = {};
208
+ threadIds.forEach((tid, i) => {
209
+ countParams[`threadId${i}`] = tid;
210
+ });
202
211
  if (resourceId) {
203
212
  countQuery += ` AND resourceId = {resourceId:String}`;
204
213
  countParams.resourceId = resourceId;
@@ -237,12 +246,25 @@ var MemoryStorageClickhouse = class extends MemoryStorage {
237
246
  const messageIds = new Set(paginatedMessages.map((m) => m.id));
238
247
  let includeMessages = [];
239
248
  if (include && include.length > 0) {
249
+ const includesNeedingThread = include.filter((inc) => !inc.threadId);
250
+ const threadByMessageId = /* @__PURE__ */ new Map();
251
+ if (includesNeedingThread.length > 0) {
252
+ const { messages: includeLookup } = await this.listMessagesById({
253
+ messageIds: includesNeedingThread.map((inc) => inc.id)
254
+ });
255
+ for (const msg of includeLookup) {
256
+ if (msg.threadId) {
257
+ threadByMessageId.set(msg.id, msg.threadId);
258
+ }
259
+ }
260
+ }
240
261
  const unionQueries = [];
241
262
  const params = [];
242
263
  let paramIdx = 1;
243
264
  for (const inc of include) {
244
265
  const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
245
- const searchId = inc.threadId || threadId;
266
+ const searchThreadId = inc.threadId ?? threadByMessageId.get(id);
267
+ if (!searchThreadId) continue;
246
268
  unionQueries.push(`
247
269
  SELECT * FROM (
248
270
  WITH numbered_messages AS (
@@ -264,31 +286,33 @@ var MemoryStorageClickhouse = class extends MemoryStorage {
264
286
  ) AS query_${paramIdx}
265
287
  `);
266
288
  params.push(
267
- { [`var_thread_id_${paramIdx}`]: searchId },
289
+ { [`var_thread_id_${paramIdx}`]: searchThreadId },
268
290
  { [`var_include_id_${paramIdx}`]: id },
269
291
  { [`var_withPreviousMessages_${paramIdx}`]: withPreviousMessages },
270
292
  { [`var_withNextMessages_${paramIdx}`]: withNextMessages }
271
293
  );
272
294
  paramIdx++;
273
295
  }
274
- const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
275
- const mergedParams = params.reduce((acc, paramObj) => ({ ...acc, ...paramObj }), {});
276
- const includeResult = await this.client.query({
277
- query: finalQuery,
278
- query_params: mergedParams,
279
- clickhouse_settings: {
280
- date_time_input_format: "best_effort",
281
- date_time_output_format: "iso",
282
- use_client_time_zone: 1,
283
- output_format_json_quote_64bit_integers: 0
284
- }
285
- });
286
- const includeRows = await includeResult.json();
287
- includeMessages = transformRows(includeRows.data);
288
- for (const includeMsg of includeMessages) {
289
- if (!messageIds.has(includeMsg.id)) {
290
- paginatedMessages.push(includeMsg);
291
- messageIds.add(includeMsg.id);
296
+ if (unionQueries.length > 0) {
297
+ const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
298
+ const mergedParams = params.reduce((acc, paramObj) => ({ ...acc, ...paramObj }), {});
299
+ const includeResult = await this.client.query({
300
+ query: finalQuery,
301
+ query_params: mergedParams,
302
+ clickhouse_settings: {
303
+ date_time_input_format: "best_effort",
304
+ date_time_output_format: "iso",
305
+ use_client_time_zone: 1,
306
+ output_format_json_quote_64bit_integers: 0
307
+ }
308
+ });
309
+ const includeRows = await includeResult.json();
310
+ includeMessages = transformRows(includeRows.data);
311
+ for (const includeMsg of includeMessages) {
312
+ if (!messageIds.has(includeMsg.id)) {
313
+ paginatedMessages.push(includeMsg);
314
+ messageIds.add(includeMsg.id);
315
+ }
292
316
  }
293
317
  }
294
318
  }
@@ -306,7 +330,10 @@ var MemoryStorageClickhouse = class extends MemoryStorage {
306
330
  }
307
331
  return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
308
332
  });
309
- const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
333
+ const threadIdSet = new Set(threadIds);
334
+ const returnedThreadMessageIds = new Set(
335
+ finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
336
+ );
310
337
  const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
311
338
  const hasMore = perPageForResponse === false ? false : allThreadMessagesReturned ? false : offset + paginatedCount < total;
312
339
  return {
@@ -323,7 +350,7 @@ var MemoryStorageClickhouse = class extends MemoryStorage {
323
350
  domain: ErrorDomain.STORAGE,
324
351
  category: ErrorCategory.THIRD_PARTY,
325
352
  details: {
326
- threadId,
353
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
327
354
  resourceId: resourceId ?? ""
328
355
  }
329
356
  },
@@ -1449,30 +1476,15 @@ var ScoresStorageClickhouse = class extends ScoresStorage {
1449
1476
  this.client = client;
1450
1477
  this.operations = operations;
1451
1478
  }
1479
+ /**
1480
+ * ClickHouse-specific score row transformation.
1481
+ * Converts timestamps to Date objects and filters out '_null_' values.
1482
+ */
1452
1483
  transformScoreRow(row) {
1453
- const scorer = safelyParseJSON(row.scorer);
1454
- const preprocessStepResult = safelyParseJSON(row.preprocessStepResult);
1455
- const analyzeStepResult = safelyParseJSON(row.analyzeStepResult);
1456
- const metadata = safelyParseJSON(row.metadata);
1457
- const input = safelyParseJSON(row.input);
1458
- const output = safelyParseJSON(row.output);
1459
- const additionalContext = safelyParseJSON(row.additionalContext);
1460
- const requestContext = safelyParseJSON(row.requestContext);
1461
- const entity = safelyParseJSON(row.entity);
1462
- return {
1463
- ...row,
1464
- scorer,
1465
- preprocessStepResult,
1466
- analyzeStepResult,
1467
- metadata,
1468
- input,
1469
- output,
1470
- additionalContext,
1471
- requestContext,
1472
- entity,
1473
- createdAt: new Date(row.createdAt),
1474
- updatedAt: new Date(row.updatedAt)
1475
- };
1484
+ return transformScoreRow(row, {
1485
+ convertTimestamps: true,
1486
+ nullValuePattern: "_null_"
1487
+ });
1476
1488
  }
1477
1489
  async getScoreById({ id }) {
1478
1490
  try {
@@ -1521,9 +1533,15 @@ var ScoresStorageClickhouse = class extends ScoresStorage {
1521
1533
  );
1522
1534
  }
1523
1535
  try {
1524
- const record = {
1525
- ...parsedScore
1526
- };
1536
+ const record = {};
1537
+ for (const key of Object.keys(SCORERS_SCHEMA)) {
1538
+ const value = parsedScore[key];
1539
+ if (key === "createdAt" || key === "updatedAt") {
1540
+ record[key] = (/* @__PURE__ */ new Date()).toISOString();
1541
+ continue;
1542
+ }
1543
+ record[key] = value === void 0 || value === null ? "_null_" : value;
1544
+ }
1527
1545
  await this.client.insert({
1528
1546
  table: TABLE_SCORERS,
1529
1547
  values: [record],
@@ -1980,7 +1998,8 @@ var WorkflowsStorageClickhouse = class extends WorkflowsStorage {
1980
1998
  toDate,
1981
1999
  page,
1982
2000
  perPage,
1983
- resourceId
2001
+ resourceId,
2002
+ status
1984
2003
  } = {}) {
1985
2004
  try {
1986
2005
  const conditions = [];
@@ -1989,6 +2008,10 @@ var WorkflowsStorageClickhouse = class extends WorkflowsStorage {
1989
2008
  conditions.push(`workflow_name = {var_workflow_name:String}`);
1990
2009
  values.var_workflow_name = workflowName;
1991
2010
  }
2011
+ if (status) {
2012
+ conditions.push(`JSONExtractString(snapshot, 'status') = {var_status:String}`);
2013
+ values.var_status = status;
2014
+ }
1992
2015
  if (resourceId) {
1993
2016
  const hasResourceId = await this.operations.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
1994
2017
  if (hasResourceId) {
@@ -2242,15 +2265,8 @@ var ClickhouseStore = class extends MastraStorage {
2242
2265
  }) {
2243
2266
  return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
2244
2267
  }
2245
- async listWorkflowRuns({
2246
- workflowName,
2247
- fromDate,
2248
- toDate,
2249
- perPage,
2250
- page,
2251
- resourceId
2252
- } = {}) {
2253
- return this.stores.workflows.listWorkflowRuns({ workflowName, fromDate, toDate, perPage, page, resourceId });
2268
+ async listWorkflowRuns(args = {}) {
2269
+ return this.stores.workflows.listWorkflowRuns(args);
2254
2270
  }
2255
2271
  async getWorkflowRunById({
2256
2272
  runId,