@mastra/clickhouse 1.14.0 → 1.15.0-alpha.0

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 { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
3
- import { BRANCH_SPAN_TYPES, BackgroundTasksStorage, EntityType, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, ObservabilityStorage, SCORERS_SCHEMA, SPAN_SCHEMA, ScoresStorage, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_HARNESS_SESSIONS, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_TRACES, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, TraceStatus, WorkflowsStorage, calculatePagination, createStorageErrorId, getDefaultValue, getSqlType, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, normalizePerPage, safelyParseJSON, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
3
+ import { BRANCH_SPAN_TYPES, BackgroundTasksStorage, EntityType, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, ObservabilityStorage, SCORERS_SCHEMA, SPAN_SCHEMA, ScoresStorage, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_HARNESS_SESSIONS, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_TRACES, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, TraceStatus, WorkflowsStorage, buildInputPreview, calculatePagination, computeTraceStatus, createStorageErrorId, getDefaultValue, getSqlType, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, normalizePerPage, safelyParseJSON, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
4
4
  import { MastraBase } from "@mastra/core/base";
5
5
  import { MessageList } from "@mastra/core/agent";
6
6
  import { parseFieldKey } from "@mastra/core/utils";
@@ -1269,6 +1269,7 @@ var MemoryStorageClickhouse = class extends MemoryStorage {
1269
1269
  hasMore: metadataFilter && perPageForResponse !== false ? offset + paginatedCount < total : perPageForResponse === false ? false : allThreadMessagesReturned ? false : offset + paginatedCount < total
1270
1270
  };
1271
1271
  } catch (error) {
1272
+ if (error instanceof MastraError && error.category === ErrorCategory.USER) throw error;
1272
1273
  const mastraError = new MastraError({
1273
1274
  id: createStorageErrorId("CLICKHOUSE", "LIST_MESSAGES", "FAILED"),
1274
1275
  domain: ErrorDomain.STORAGE,
@@ -1280,13 +1281,7 @@ var MemoryStorageClickhouse = class extends MemoryStorage {
1280
1281
  }, error);
1281
1282
  this.logger?.error?.(mastraError.toString());
1282
1283
  this.logger?.trackException?.(mastraError);
1283
- return {
1284
- messages: [],
1285
- total: 0,
1286
- page,
1287
- perPage: perPageForResponse,
1288
- hasMore: false
1289
- };
1284
+ throw mastraError;
1290
1285
  }
1291
1286
  }
1292
1287
  _sortMessages(messages, field, direction) {
@@ -3872,6 +3867,7 @@ function rowToSpanRecord(row) {
3872
3867
  function rowToLightSpanRecord(row) {
3873
3868
  const startedAt = toDate(row.startedAt);
3874
3869
  const endedAt = row.isEvent ? startedAt : toDateOrNull(row.endedAt);
3870
+ const error = parseJson(row.error) ?? void 0;
3875
3871
  return {
3876
3872
  traceId: row.traceId,
3877
3873
  spanId: row.spanId,
@@ -3884,7 +3880,13 @@ function rowToLightSpanRecord(row) {
3884
3880
  entityType: nullableEntityType(row.entityType),
3885
3881
  entityId: nullableString(row.entityId),
3886
3882
  entityName: nullableString(row.entityName),
3887
- error: parseJson(row.error) ?? void 0,
3883
+ error,
3884
+ status: computeTraceStatus({
3885
+ error,
3886
+ endedAt
3887
+ }),
3888
+ metadata: parseJson(row.metadataRaw) ?? void 0,
3889
+ inputPreview: buildInputPreview(row.input ?? null),
3888
3890
  createdAt: startedAt,
3889
3891
  updatedAt: null
3890
3892
  };
@@ -6207,93 +6209,48 @@ async function getRootSpan(client, args) {
6207
6209
  return { span: rowToSpanRecord(rows[0]) };
6208
6210
  }
6209
6211
  /**
6210
- * Shared page-mode helper used by listTracesLight.
6212
+ * Columns a trace list renders. `input` is selected only so the row mapper can
6213
+ * derive a short `inputPreview` at read time (see `rowToLightSpanRecord`); the
6214
+ * raw blob itself never leaves the store.
6211
6215
  */
6212
- async function listTraceRows(client, args, selectClause, mapRows) {
6213
- const { filters, pagination, orderBy } = listTracesArgsSchema.parse(args);
6214
- const page = pagination?.page ?? 0;
6215
- const perPage = pagination?.perPage ?? 10;
6216
- const { conditions, params } = buildTraceFilterConditions(filters, "r");
6217
- if (filters?.hasChildError != null) if (filters.hasChildError) conditions.push(`EXISTS (
6218
- SELECT 1 FROM ${TABLE_SPAN_EVENTS} c
6219
- WHERE c.traceId = r.traceId
6220
- AND c.parentSpanId IS NOT NULL
6221
- AND c.error IS NOT NULL
6222
- )`);
6223
- else conditions.push(`NOT EXISTS (
6224
- SELECT 1 FROM ${TABLE_SPAN_EVENTS} c
6225
- WHERE c.traceId = r.traceId
6226
- AND c.parentSpanId IS NOT NULL
6227
- AND c.error IS NOT NULL
6228
- )`);
6229
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
6230
- const orderClause = buildTraceOrderByClause(orderBy);
6231
- const countRows = await (await client.query({
6232
- query: `
6233
- SELECT count() as cnt FROM (
6234
- SELECT dedupeKey
6235
- FROM ${TABLE_TRACE_ROOTS} r
6236
- ${whereClause}
6237
- ORDER BY dedupeKey
6238
- LIMIT 1 BY dedupeKey
6239
- )
6240
- `,
6241
- query_params: params,
6242
- format: "JSONEachRow",
6243
- clickhouse_settings: CH_SETTINGS
6244
- })).json();
6245
- const total = Number(countRows[0]?.cnt ?? 0);
6246
- if (total === 0) return {
6247
- pagination: {
6248
- total: 0,
6249
- page,
6250
- perPage,
6251
- hasMore: false
6252
- },
6253
- spans: []
6254
- };
6255
- const rows = await (await client.query({
6256
- query: `
6257
- SELECT ${selectClause} FROM (
6258
- SELECT ${selectClause}
6259
- FROM ${TABLE_TRACE_ROOTS} r
6260
- ${whereClause}
6261
- ORDER BY dedupeKey
6262
- LIMIT 1 BY dedupeKey
6263
- )
6264
- ORDER BY ${orderClause}
6265
- LIMIT {limit:UInt32}
6266
- OFFSET {offset:UInt32}
6267
- `,
6268
- query_params: {
6269
- ...params,
6270
- limit: perPage,
6271
- offset: page * perPage
6272
- },
6273
- format: "JSONEachRow",
6274
- clickhouse_settings: CH_SETTINGS
6275
- })).json();
6276
- return {
6277
- pagination: {
6278
- total,
6279
- page,
6280
- perPage,
6281
- hasMore: (page + 1) * perPage < total
6282
- },
6283
- spans: mapRows(rows)
6284
- };
6285
- }
6216
+ const LIGHT_TRACE_ROOT_FIELDS = [
6217
+ "traceId",
6218
+ "spanId",
6219
+ "parentSpanId",
6220
+ "name",
6221
+ "spanType",
6222
+ "isEvent",
6223
+ "startedAt",
6224
+ "endedAt",
6225
+ "entityType",
6226
+ "entityId",
6227
+ "entityName",
6228
+ "error",
6229
+ "metadataRaw",
6230
+ "input"
6231
+ ];
6232
+ const FULL_PROJECTION = {
6233
+ innerSelect: "*",
6234
+ outerSelect: "*",
6235
+ deltaSelect: "r.* EXCEPT(startedAt, traceId, dedupeKey)"
6236
+ };
6237
+ const LIGHT_PROJECTION = {
6238
+ innerSelect: ["dedupeKey", ...LIGHT_TRACE_ROOT_FIELDS].join(", "),
6239
+ outerSelect: LIGHT_TRACE_ROOT_FIELDS.join(", "),
6240
+ deltaSelect: LIGHT_TRACE_ROOT_FIELDS.filter((field) => field !== "startedAt" && field !== "traceId").map((field) => `r.${field}`).join(", ")
6241
+ };
6286
6242
  /**
6287
- * List traces with optional filtering, pagination, and ordering.
6243
+ * Shared implementation behind listTraces and listTracesLight.
6288
6244
  *
6289
6245
  * Reads from trace_roots (root spans only).
6290
- * Uses two-stage query for ReplacingMergeTree deduplication:
6246
+ * Page mode uses a two-stage query for ReplacingMergeTree deduplication:
6291
6247
  * Inner: filter + deterministic ORDER BY + LIMIT 1 BY dedupeKey
6292
6248
  * Outer: final ordering + pagination
6249
+ * Delta mode joins the append-only delta table and returns only rows past the cursor.
6293
6250
  *
6294
6251
  * hasChildError is handled via EXISTS subquery against span_events.
6295
6252
  */
6296
- async function listTraces(client, args, strategy) {
6253
+ async function listTraceRows(client, args, strategy, projection, mapRows) {
6297
6254
  const { mode, filters, pagination, orderBy, after, limit } = listTracesArgsSchema.parse(args);
6298
6255
  const page = pagination?.page ?? 0;
6299
6256
  const perPage = pagination?.perPage ?? 10;
@@ -6323,10 +6280,11 @@ async function listTraces(client, args, strategy) {
6323
6280
  },
6324
6281
  deltaCursor: streamHeadCursor
6325
6282
  };
6326
- const rows = await queryTracesAfterCursor(client, whereClause, params, limit, validateCursorId(after));
6283
+ const afterCursor = validateCursorId(after);
6284
+ const rows = await queryTracesAfterCursor(client, projection.deltaSelect, whereClause, params, limit, afterCursor);
6327
6285
  const visibleRows = rows.slice(0, limit);
6328
6286
  return {
6329
- spans: toTraceSpans(visibleRows.map(rowToSpanRecord)),
6287
+ spans: mapRows(visibleRows),
6330
6288
  delta: {
6331
6289
  limit,
6332
6290
  hasMore: rows.length > limit
@@ -6361,10 +6319,10 @@ async function listTraces(client, args, strategy) {
6361
6319
  spans: [],
6362
6320
  ...deltaCursorEnabled ? { deltaCursor: currentDeltaCursor } : {}
6363
6321
  };
6364
- const spans = (await (await client.query({
6322
+ const rows = await (await client.query({
6365
6323
  query: `
6366
- SELECT * FROM (
6367
- SELECT *
6324
+ SELECT ${projection.outerSelect} FROM (
6325
+ SELECT ${projection.innerSelect}
6368
6326
  FROM ${TABLE_TRACE_ROOTS} r
6369
6327
  ${whereClause}
6370
6328
  ORDER BY dedupeKey
@@ -6381,7 +6339,7 @@ async function listTraces(client, args, strategy) {
6381
6339
  },
6382
6340
  format: "JSONEachRow",
6383
6341
  clickhouse_settings: CH_SETTINGS
6384
- })).json()).map(rowToSpanRecord);
6342
+ })).json();
6385
6343
  return {
6386
6344
  pagination: {
6387
6345
  total,
@@ -6389,32 +6347,27 @@ async function listTraces(client, args, strategy) {
6389
6347
  perPage,
6390
6348
  hasMore: (page + 1) * perPage < total
6391
6349
  },
6392
- spans: toTraceSpans(spans),
6350
+ spans: mapRows(rows),
6393
6351
  ...deltaCursorEnabled ? { deltaCursor: currentDeltaCursor } : {}
6394
6352
  };
6395
6353
  }
6396
- const LIGHT_TRACE_ROOT_COLUMNS = [
6397
- "traceId",
6398
- "spanId",
6399
- "parentSpanId",
6400
- "name",
6401
- "spanType",
6402
- "isEvent",
6403
- "startedAt",
6404
- "endedAt",
6405
- "entityType",
6406
- "entityId",
6407
- "entityName",
6408
- "error"
6409
- ].join(", ");
6410
- async function listTracesLight(client, args) {
6411
- return listTraceRows(client, args, LIGHT_TRACE_ROOT_COLUMNS, (rows) => rows.map(rowToLightSpanRecord));
6354
+ /** List traces with optional filtering, pagination, and ordering. */
6355
+ async function listTraces(client, args, strategy) {
6356
+ return listTraceRows(client, args, strategy, FULL_PROJECTION, (rows) => toTraceSpans(rows.map(rowToSpanRecord)));
6357
+ }
6358
+ /**
6359
+ * List traces projecting only the columns a trace list renders.
6360
+ * Skips the attributes/output blobs and reduces `input` to a short preview in
6361
+ * the mapper, so the response payload stays flat as traces grow.
6362
+ */
6363
+ async function listTracesLight(client, args, strategy) {
6364
+ return listTraceRows(client, args, strategy, LIGHT_PROJECTION, (rows) => rows.map(rowToLightSpanRecord));
6412
6365
  }
6413
- async function queryTracesAfterCursor(client, whereClause, params, limit, cursorId) {
6366
+ async function queryTracesAfterCursor(client, deltaSelect, whereClause, params, limit, cursorId) {
6414
6367
  return await (await client.query({
6415
6368
  query: `
6416
6369
  SELECT
6417
- r.* EXCEPT(startedAt, traceId, dedupeKey),
6370
+ ${deltaSelect},
6418
6371
  r.startedAt AS startedAt,
6419
6372
  r.traceId AS traceId,
6420
6373
  r.dedupeKey AS dedupeKey,
@@ -7391,7 +7344,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
7391
7344
  }
7392
7345
  async listTracesLight(args) {
7393
7346
  try {
7394
- return await listTracesLight(this.#client, args);
7347
+ return await listTracesLight(this.#client, args, this.#deltaCursorStrategy);
7395
7348
  } catch (error) {
7396
7349
  if (error instanceof MastraError) throw error;
7397
7350
  throw new MastraError({