@mastra/clickhouse 1.28.0-alpha.0 → 1.28.0-alpha.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.
@@ -3,7 +3,7 @@ name: mastra-clickhouse
3
3
  description: Documentation for @mastra/clickhouse. Use when working with @mastra/clickhouse APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/clickhouse"
6
- version: "1.28.0-alpha.0"
6
+ version: "1.28.0-alpha.2"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.28.0-alpha.0",
2
+ "version": "1.28.0-alpha.2",
3
3
  "package": "@mastra/clickhouse",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -781,6 +781,7 @@ var ClickhouseDB = class extends _mastra_core_base.MastraBase {
781
781
  const rawUpdatedAt = filteredRecord.updatedAt || /* @__PURE__ */ new Date();
782
782
  if ("createdAt" in filteredRecord || (await this.getTableColumns(tableName)).has("createdAt")) filteredRecord.createdAt = rawCreatedAt instanceof Date ? rawCreatedAt.toISOString() : rawCreatedAt;
783
783
  if ("updatedAt" in filteredRecord || (await this.getTableColumns(tableName)).has("updatedAt")) filteredRecord.updatedAt = rawUpdatedAt instanceof Date ? rawUpdatedAt.toISOString() : rawUpdatedAt;
784
+ for (const [key, value] of Object.entries(filteredRecord)) if (_mastra_core_storage.TABLE_SCHEMAS[tableName]?.[key]?.type === "timestamp" && typeof value === "number") filteredRecord[key] = new Date(value).toISOString();
784
785
  await this.client.insert({
785
786
  table: tableName,
786
787
  values: [filteredRecord],
@@ -2293,12 +2294,12 @@ var ObservabilityStorageClickhouse = class extends _mastra_core_storage.Observab
2293
2294
  }
2294
2295
  });
2295
2296
  const data = { ...updates };
2296
- if (data.endedAt instanceof Date) data.endedAt = data.endedAt.getTime();
2297
- if (data.startedAt instanceof Date) data.startedAt = data.startedAt.getTime();
2297
+ if (data.endedAt instanceof Date) data.endedAt = data.endedAt.toISOString();
2298
+ if (data.startedAt instanceof Date) data.startedAt = data.startedAt.toISOString();
2298
2299
  const updated = {
2299
2300
  ...existing,
2300
2301
  ...data,
2301
- updatedAt: Date.now()
2302
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2302
2303
  };
2303
2304
  await this.client.insert({
2304
2305
  table: _mastra_core_storage.TABLE_SPANS,
@@ -2536,7 +2537,7 @@ var ObservabilityStorageClickhouse = class extends _mastra_core_storage.Observab
2536
2537
  }
2537
2538
  async batchUpdateSpans(args) {
2538
2539
  try {
2539
- const now = Date.now();
2540
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2540
2541
  if (args.records.length === 0) return;
2541
2542
  const keyParams = {};
2542
2543
  const keyTuples = args.records.map((record, i) => {
@@ -2569,8 +2570,8 @@ var ObservabilityStorageClickhouse = class extends _mastra_core_storage.Observab
2569
2570
  const existing = current.get(key);
2570
2571
  if (!existing) continue;
2571
2572
  const updates = { ...record.updates };
2572
- if (updates.startedAt instanceof Date) updates.startedAt = updates.startedAt.getTime();
2573
- if (updates.endedAt instanceof Date) updates.endedAt = updates.endedAt.getTime();
2573
+ if (updates.startedAt instanceof Date) updates.startedAt = updates.startedAt.toISOString();
2574
+ if (updates.endedAt instanceof Date) updates.endedAt = updates.endedAt.toISOString();
2574
2575
  current.set(key, {
2575
2576
  ...existing,
2576
2577
  ...updates,
@@ -6937,6 +6938,13 @@ const timingFeatures = [
6937
6938
  ...timeToFirstTokenSupported ? ["trace-query-time-to-first-token"] : [],
6938
6939
  ...timeToFirstTokenSupported && timeToFirstTokenSortSupported ? ["trace-query-time-to-first-token-sort"] : []
6939
6940
  ];
6941
+ /**
6942
+ * Outcome fields follow the same rule: core before them rejects their predicates and strict rows,
6943
+ * so they are only returned and declared when core knows them.
6944
+ */
6945
+ const spanQueryOutcomeFieldsSupported = "outcome" in (_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ?? {});
6946
+ const traceQueryOutcomeFieldsSupported = "outcome" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
6947
+ const outcomeFeatures = [...spanQueryOutcomeFieldsSupported ? ["span-query-outcome-fields"] : [], ...traceQueryOutcomeFieldsSupported ? ["trace-query-outcome-fields"] : []];
6940
6948
  /** Order fields holding numbers that can be empty; the rest are timestamps. */
6941
6949
  const NUMERIC_ORDER_FIELDS = /* @__PURE__ */ new Set(["timeToFirstChunkMs", "timeToFirstTokenMs"]);
6942
6950
  function durationMsSql(startedAt, endedAt) {
@@ -7023,6 +7031,14 @@ const TRACE_FIELDS = {
7023
7031
  sql: TRACE_STATUS_SQL,
7024
7032
  parameterType: "String"
7025
7033
  },
7034
+ outcome: {
7035
+ sql: "r.outcome",
7036
+ parameterType: "String"
7037
+ },
7038
+ stopReason: {
7039
+ sql: "r.stopReason",
7040
+ parameterType: "String"
7041
+ },
7026
7042
  tags: {
7027
7043
  sql: "r.tags",
7028
7044
  parameterType: "String"
@@ -7069,6 +7085,14 @@ const SPAN_FIELDS = {
7069
7085
  sql: "s.status",
7070
7086
  parameterType: "String"
7071
7087
  },
7088
+ outcome: {
7089
+ sql: "s.outcome",
7090
+ parameterType: "String"
7091
+ },
7092
+ stopReason: {
7093
+ sql: "s.stopReason",
7094
+ parameterType: "String"
7095
+ },
7072
7096
  error: {
7073
7097
  sql: "s.error",
7074
7098
  parameterType: "String"
@@ -7278,6 +7302,9 @@ const TRACE_SELECT = `
7278
7302
  r.environment AS environment,
7279
7303
  r.timeToFirstChunkMs AS timeToFirstChunkMs,
7280
7304
  r.timeToFirstTokenMs AS timeToFirstTokenMs,
7305
+ r.outcome AS outcome,
7306
+ r.stopReason AS stopReason,
7307
+ r.stopReasonContext AS stopReasonContext,
7281
7308
  ${TRACE_STATUS_SQL} AS status`;
7282
7309
  var ParameterBuilder = class {
7283
7310
  params = {};
@@ -7478,14 +7505,16 @@ function compileClickHouseTraceScope(selection, relationCollections, parameters,
7478
7505
  traceId,
7479
7506
  name,
7480
7507
  spanType,
7481
- if(JSONType(attributes, 'model') = 'String', JSONExtractString(attributes, 'model'), NULL) AS model,
7482
- if(JSONType(attributes, 'provider') = 'String', JSONExtractString(attributes, 'provider'), NULL) AS provider,
7508
+ coalesce(model, if(JSONType(attributes, 'model') = 'String', JSONExtractString(attributes, 'model'), NULL)) AS model,
7509
+ coalesce(provider, if(JSONType(attributes, 'provider') = 'String', JSONExtractString(attributes, 'provider'), NULL)) AS provider,
7483
7510
  startedAt,
7484
7511
  endedAt,
7485
7512
  ${durationMsSql("startedAt", "endedAt")} AS durationMs,
7486
7513
  timeToFirstChunkMs,
7487
7514
  timeToFirstTokenMs,
7488
7515
  if(isNotNull(error), 'error', 'success') AS status,
7516
+ outcome,
7517
+ stopReason,
7489
7518
  error,
7490
7519
  entityType,
7491
7520
  entityId,
@@ -7589,7 +7618,7 @@ const QUERY_TRACES_SCOPE = {
7589
7618
  oneRootPerTrace: true,
7590
7619
  spanExistence: true
7591
7620
  };
7592
- const TRACE_LIST_SELECT = TRACE_SELECT.split("\n").filter((line) => !/ AS (metadata|inputPreview|outputPreview|errorPreview),$/.test(line)).join("\n");
7621
+ const TRACE_LIST_SELECT = TRACE_SELECT.split("\n").filter((line) => !/ AS (metadata|inputPreview|outputPreview|errorPreview|stopReasonContext),$/.test(line)).join("\n");
7593
7622
  function traceOrder(plan) {
7594
7623
  return {
7595
7624
  field: resolveOrderField(plan.orderBy.field),
@@ -7749,7 +7778,7 @@ function compileClickHouseTraceRootPayloads(keys, scope, columns = "payload") {
7749
7778
  const tenant = (value) => `${parameters.add(value ?? "", "String")}, ${value === null ? 1 : 0}`;
7750
7779
  const tuples = keys.map((key) => `(${parameters.add(key.startedAt, "DateTime64(3, 'UTC')")}, ${parameters.add(key.traceId, "String")}, ${parameters.add(key.dedupeKey, "String")}, ${parameters.add(key.endedAt, "DateTime64(3, 'UTC')")}, ${tenant(key.organizationId)}, ${tenant(key.resourceId)})`);
7751
7780
  return {
7752
- query: `SELECT traceId, spanId AS rootSpanId, dedupeKey, organizationId, resourceId, ${columns === "payload" ? `metadataRaw AS metadata, ${PREVIEW_COLUMNS.join(", ")}` : "input, output, error"}
7781
+ query: `SELECT traceId, spanId AS rootSpanId, dedupeKey, organizationId, resourceId, ${columns === "payload" ? `metadataRaw AS metadata, stopReasonContext, ${PREVIEW_COLUMNS.join(", ")}` : "input, output, error"}
7753
7782
  FROM ${TABLE_TRACE_ROOTS}
7754
7783
  WHERE (startedAt, traceId, dedupeKey, endedAt, ifNull(organizationId, ''), isNull(organizationId), ifNull(resourceId, ''), isNull(resourceId)) IN (${tuples.join(", ")})${compileTenantScope(scope, parameters)}
7755
7784
  ORDER BY ${ROOT_VERSION_ORDER}
@@ -8016,7 +8045,12 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
8016
8045
  environment: row.environment == null ? null : String(row.environment),
8017
8046
  status: row.status,
8018
8047
  ...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) },
8019
- ...timeToFirstTokenSupported && { timeToFirstTokenMs: row.timeToFirstTokenMs == null ? null : Number(row.timeToFirstTokenMs) }
8048
+ ...timeToFirstTokenSupported && { timeToFirstTokenMs: row.timeToFirstTokenMs == null ? null : Number(row.timeToFirstTokenMs) },
8049
+ ...traceQueryOutcomeFieldsSupported && {
8050
+ outcome: row.outcome == null ? null : String(row.outcome),
8051
+ stopReason: row.stopReason == null ? null : String(row.stopReason),
8052
+ stopReasonContext: parseJson(row.stopReasonContext) ?? null
8053
+ }
8020
8054
  });
8021
8055
  if (plan.paginationMode === "delta") {
8022
8056
  const rows = await run(compileClickHouseTraceQuery(plan, deltaHead));
@@ -8143,7 +8177,10 @@ const displayFields = [
8143
8177
  "durationMs",
8144
8178
  "timeToFirstChunkMs",
8145
8179
  "timeToFirstTokenMs",
8146
- "status"
8180
+ "status",
8181
+ "outcome",
8182
+ "stopReason",
8183
+ "stopReasonContext"
8147
8184
  ];
8148
8185
  var Parameters = class {
8149
8186
  values = {};
@@ -8195,13 +8232,18 @@ function predicateFields$1(predicate, fields = /* @__PURE__ */ new Set()) {
8195
8232
  else fields.add(predicate.field);
8196
8233
  return fields;
8197
8234
  }
8235
+ /** Selection and filter expressions read only columns; `model`/`provider` read their dedicated columns. */
8198
8236
  function fieldExpression(field) {
8199
- if (field === "model" || field === "provider") return `if(JSONType(e.attributes, '${field}') = 'String', JSONExtractString(e.attributes, '${field}'), NULL)`;
8200
8237
  if (field === "durationMs") return "dateDiff('millisecond', e.startedAt, e.endedAt)";
8201
8238
  if (field === "status") return "if(isNull(e.error), 'success', 'error')";
8202
8239
  if (field === "error") return "if(isNull(e.error), NULL, 'error')";
8203
8240
  return `e.${field}`;
8204
8241
  }
8242
+ /** Returned values fall back to span attributes for rows written before the dedicated columns existed. */
8243
+ function displayExpression(field) {
8244
+ if (field === "model" || field === "provider") return `coalesce(e.${field}, if(JSONType(e.attributes, '${field}') = 'String', JSONExtractString(e.attributes, '${field}'), NULL))`;
8245
+ return fieldExpression(field);
8246
+ }
8205
8247
  function compileClickHouseSpanQuery(plan) {
8206
8248
  const parameters = new Parameters();
8207
8249
  const from = parameters.add(plan.timeRange.from, "DateTime64(3, 'UTC')");
@@ -8288,7 +8330,11 @@ const TRACE_BRANCHES_SPAN_FIELD_COLUMNS = {
8288
8330
  "rootEntityType",
8289
8331
  "rootEntityId",
8290
8332
  "rootEntityName",
8291
- "rootEntityVersionId"
8333
+ "rootEntityVersionId",
8334
+ "model",
8335
+ "provider",
8336
+ "outcome",
8337
+ "stopReason"
8292
8338
  ].map((field) => [field, sameColumn(field)])),
8293
8339
  status: ["error"],
8294
8340
  error: ["error"],
@@ -8401,7 +8447,7 @@ async function querySpans(client, plan, timeoutMs) {
8401
8447
  const pageParameter = (rows) => rows.map((row) => new _clickhouse_client.TupleParam([...identityValues(row), row.endedAt.replace("T", " ").replace(/Z$/, "")]));
8402
8448
  const rowsWithPreviews = await run({
8403
8449
  query: `SELECT ${identities}, ${[...displayFields, ...PREVIEW_COLUMNS].map((field, index) => `data.${index + 1} AS ${field}`).join(", ")}
8404
- FROM (SELECT ${identities}, any(tuple(${[...displayFields.map(fieldExpression), ...PREVIEW_COLUMNS].join(", ")})) AS data
8450
+ FROM (SELECT ${identities}, any(tuple(${[...displayFields.map(displayExpression), ...PREVIEW_COLUMNS].join(", ")})) AS data
8405
8451
  FROM ${TABLE_SPAN_EVENTS} e WHERE ${payloadWhere} GROUP BY ${identities})`,
8406
8452
  query_params: { page: pageParameter(page) }
8407
8453
  });
@@ -8428,13 +8474,18 @@ async function querySpans(client, plan, timeoutMs) {
8428
8474
  const hydrated = page.flatMap((identity) => {
8429
8475
  const row = byIdentity.get(_mastra_core_storage.spanQueryIdentityKey(identity));
8430
8476
  if (!row) return [];
8431
- const { inputPreview: _input, outputPreview: _output, errorPreview: _error, timeToFirstChunkMs, timeToFirstTokenMs, ...metadata } = row;
8477
+ const { inputPreview: _input, outputPreview: _output, errorPreview: _error, timeToFirstChunkMs, timeToFirstTokenMs, outcome, stopReason, stopReasonContext, ...metadata } = row;
8432
8478
  return [{
8433
8479
  ...metadata,
8434
8480
  ...identity,
8435
8481
  durationMs: Number(row.durationMs),
8436
8482
  ...timeToFirstChunkSupported && { timeToFirstChunkMs: timeToFirstChunkMs == null ? null : Number(timeToFirstChunkMs) },
8437
- ...timeToFirstTokenSupported && { timeToFirstTokenMs: timeToFirstTokenMs == null ? null : Number(timeToFirstTokenMs) }
8483
+ ...timeToFirstTokenSupported && { timeToFirstTokenMs: timeToFirstTokenMs == null ? null : Number(timeToFirstTokenMs) },
8484
+ ...spanQueryOutcomeFieldsSupported && {
8485
+ outcome: outcome ?? null,
8486
+ stopReason: stopReason ?? null,
8487
+ stopReasonContext: parseJson(stopReasonContext) ?? null
8488
+ }
8438
8489
  }];
8439
8490
  });
8440
8491
  let metrics = [];
@@ -8749,6 +8800,8 @@ const DIMENSION_SQL = {
8749
8800
  entityId: "r.entityId",
8750
8801
  environment: "r.environment",
8751
8802
  status: TRACE_STATUS_SQL,
8803
+ outcome: "r.outcome",
8804
+ stopReason: "r.stopReason",
8752
8805
  serviceName: "r.serviceName",
8753
8806
  executionSource: "r.executionSource",
8754
8807
  threadId: "r.threadId",
@@ -8771,6 +8824,8 @@ const SPAN_DIMENSION_SQL = Object.fromEntries([
8771
8824
  "rootEntityName",
8772
8825
  "environment",
8773
8826
  "status",
8827
+ "outcome",
8828
+ "stopReason",
8774
8829
  "serviceName",
8775
8830
  "executionSource",
8776
8831
  "threadId",
@@ -8778,7 +8833,9 @@ const SPAN_DIMENSION_SQL = Object.fromEntries([
8778
8833
  "userId",
8779
8834
  "sessionId",
8780
8835
  "organizationId",
8781
- "experimentId"
8836
+ "experimentId",
8837
+ "model",
8838
+ "provider"
8782
8839
  ].map((field) => [field, `r.${field}`]));
8783
8840
  const PERCENTILES = {
8784
8841
  "duration.p50": .5,
@@ -9940,8 +9997,12 @@ function buildBranchCursor(row) {
9940
9997
  return row.cursorId ?? "0";
9941
9998
  }
9942
9999
  // src/storage/domains/observability/v-next/index.ts
9943
- const spanQueryFeatures = typeof _mastra_core_storage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
9944
- const spanAggregateFeatures = typeof _mastra_core_storage.planSpanAggregate === "function" ? ["span-aggregate"] : [];
10000
+ const spanQueryFeatures = typeof _mastra_core_storage.planSpanQuery === "function" ? [
10001
+ "span-query",
10002
+ "span-query-context-fields",
10003
+ ..."model" in (_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? ["span-query-model-fields"] : []
10004
+ ] : [];
10005
+ const spanAggregateFeatures = typeof _mastra_core_storage.planSpanAggregate === "function" ? ["span-aggregate", ..."model" in (_mastra_core_storage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {}) ? ["span-aggregate-model-dimensions"] : []] : [];
9945
10006
  const threadQueryFeatures = typeof _mastra_core_storage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [];
9946
10007
  function buildSignalMigrationRequiredMessage(args) {
9947
10008
  const tableList = args.tables.map((table) => ` - ${table.table} (${table.engine})`).join("\n");
@@ -10381,6 +10442,7 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
10381
10442
  "feedback",
10382
10443
  "trace-query-context-ids",
10383
10444
  ...timingFeatures,
10445
+ ...outcomeFeatures,
10384
10446
  "trace-query-root-span-fields",
10385
10447
  "trace-query-root-entity-id",
10386
10448
  "trace-aggregate",
@@ -10406,6 +10468,7 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
10406
10468
  "feedback",
10407
10469
  "trace-query-context-ids",
10408
10470
  ...timingFeatures,
10471
+ ...outcomeFeatures,
10409
10472
  "trace-query-root-span-fields",
10410
10473
  "trace-query-root-entity-id",
10411
10474
  "trace-aggregate",