@mastra/clickhouse 1.25.2-alpha.0 → 1.26.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.25.2-alpha.0"
6
+ version: "1.26.0-alpha.2"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.25.2-alpha.0",
2
+ "version": "1.26.0-alpha.2",
3
3
  "package": "@mastra/clickhouse",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -2743,6 +2743,7 @@ CREATE TABLE IF NOT EXISTS ${TABLE_SPAN_EVENTS} (
2743
2743
  isEvent Bool DEFAULT false,
2744
2744
  startedAt DateTime64(3, 'UTC'),
2745
2745
  endedAt DateTime64(3, 'UTC'),
2746
+ timeToFirstChunkMs Nullable(Float64),
2746
2747
 
2747
2748
  -- Query-relevant flexible fields
2748
2749
  tags Array(LowCardinality(String)) DEFAULT [],
@@ -2816,6 +2817,7 @@ CREATE TABLE IF NOT EXISTS ${TABLE_TRACE_ROOTS} (
2816
2817
  isEvent Bool DEFAULT false,
2817
2818
  startedAt DateTime64(3, 'UTC'),
2818
2819
  endedAt DateTime64(3, 'UTC'),
2820
+ timeToFirstChunkMs Nullable(Float64),
2819
2821
 
2820
2822
  -- Query-relevant flexible fields
2821
2823
  tags Array(LowCardinality(String)) DEFAULT [],
@@ -2901,6 +2903,7 @@ CREATE TABLE IF NOT EXISTS ${TABLE_TRACE_BRANCHES} (
2901
2903
  isEvent Bool DEFAULT false,
2902
2904
  startedAt DateTime64(3, 'UTC'),
2903
2905
  endedAt DateTime64(3, 'UTC'),
2906
+ timeToFirstChunkMs Nullable(Float64),
2904
2907
 
2905
2908
  -- Query-relevant flexible fields
2906
2909
  tags Array(LowCardinality(String)) DEFAULT [],
@@ -3706,7 +3709,10 @@ const ALL_MIGRATIONS = [
3706
3709
  addBloomIndex(TABLE_METRIC_EVENTS, "idx_sessionId", "sessionId"),
3707
3710
  addBloomIndex(TABLE_METRIC_EVENTS, "idx_requestId", "requestId"),
3708
3711
  ...LOG_SKIP_INDEX_COLUMNS.map((column) => addBloomIndex(TABLE_LOG_EVENTS, `idx_${column}`, column)),
3709
- addBloomIndex(TABLE_DELETION_REQUESTS, "idx_predicateValues", "predicateValues")
3712
+ addBloomIndex(TABLE_DELETION_REQUESTS, "idx_predicateValues", "predicateValues"),
3713
+ addColumn(TABLE_TRACE_ROOTS, "timeToFirstChunkMs", "Nullable(Float64)"),
3714
+ addColumn(TABLE_TRACE_BRANCHES, "timeToFirstChunkMs", "Nullable(Float64)"),
3715
+ addColumn(TABLE_SPAN_EVENTS, "timeToFirstChunkMs", "Nullable(Float64)")
3710
3716
  ];
3711
3717
  const ALL_TABLE_NAMES = [
3712
3718
  TABLE_SPAN_EVENTS,
@@ -3838,9 +3844,27 @@ function readPreview(preview, payload, jsonEncoded = false) {
3838
3844
  }
3839
3845
  /** Older response builders may shorten previews or turn empty strings into null. */
3840
3846
  function buildSpanQueryResponse(...args) {
3841
- if (canGeneratePreviews) return _mastra_core_storage.buildSpanQueryResponse(...args);
3842
- const [plan, selected, payloads, metrics] = args;
3843
- const response = _mastra_core_storage.buildSpanQueryResponse(plan, selected, [], metrics);
3847
+ const [plan, selected, payloads, metrics = []] = args;
3848
+ const compatibleSelected = _mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ? selected : selected.map((row) => ({
3849
+ organizationId: row.organizationId,
3850
+ resourceId: row.resourceId,
3851
+ traceId: row.traceId,
3852
+ spanId: row.spanId,
3853
+ parentSpanId: row.parentSpanId,
3854
+ name: row.name,
3855
+ spanType: row.spanType,
3856
+ status: row.status,
3857
+ startedAt: row.startedAt,
3858
+ endedAt: row.endedAt,
3859
+ durationMs: row.durationMs,
3860
+ entityType: row.entityType,
3861
+ entityId: row.entityId,
3862
+ entityName: row.entityName,
3863
+ model: row.model,
3864
+ provider: row.provider
3865
+ }));
3866
+ if (canGeneratePreviews) return _mastra_core_storage.buildSpanQueryResponse(plan, compatibleSelected, payloads, metrics);
3867
+ const response = _mastra_core_storage.buildSpanQueryResponse(plan, compatibleSelected, [], metrics);
3844
3868
  const byIdentity = new Map(payloads.map((payload) => [_mastra_core_storage.spanQueryIdentityKey(payload), payload]));
3845
3869
  for (const span of response.spans) {
3846
3870
  const payload = byIdentity.get(_mastra_core_storage.spanQueryIdentityKey(span));
@@ -4137,6 +4161,7 @@ function spanRecordToRow(span) {
4137
4161
  isEvent: span.isEvent,
4138
4162
  startedAt: toISOString(span.startedAt),
4139
4163
  endedAt: toISOString(endedAt),
4164
+ timeToFirstChunkMs: span.timeToFirstChunkMs ?? null,
4140
4165
  tags: normalizeTags(span.tags),
4141
4166
  metadataSearch: buildMetadataSearch(metadata),
4142
4167
  metadataRaw: jsonEncode(metadata),
@@ -4792,6 +4817,7 @@ function buildMetricsFilterConditions(filters, tableAlias) {
4792
4817
  assertNoDeprecatedSourceFilter(filters.source, "executionSource", "metrics");
4793
4818
  addCommonFilterFields(filters, tableAlias, out);
4794
4819
  addIn(col("traceId"), filters.traceIds, "traceIds", out);
4820
+ addIn(col("experimentId"), filters.experimentIds, "experimentIds", out);
4795
4821
  addIn(col("name"), filters.name, "metricNames", out);
4796
4822
  addEq(col("provider"), filters.provider, "provider", "String", out);
4797
4823
  addEq(col("model"), filters.model, "model", "String", out);
@@ -6985,6 +7011,73 @@ const SPAN_FIELDS = {
6985
7011
  parameterType: "String"
6986
7012
  }
6987
7013
  };
7014
+ const SPAN_QUERY_FIELDS = {
7015
+ ...SPAN_FIELDS,
7016
+ traceId: {
7017
+ sql: "s.traceId",
7018
+ parameterType: "String"
7019
+ },
7020
+ spanId: {
7021
+ sql: "s.spanId",
7022
+ parameterType: "String"
7023
+ },
7024
+ parentSpanId: {
7025
+ sql: "s.parentSpanId",
7026
+ parameterType: "String"
7027
+ },
7028
+ threadId: {
7029
+ sql: "s.threadId",
7030
+ parameterType: "String"
7031
+ },
7032
+ resourceId: {
7033
+ sql: "s.resourceId",
7034
+ parameterType: "String"
7035
+ },
7036
+ requestId: {
7037
+ sql: "s.requestId",
7038
+ parameterType: "String"
7039
+ },
7040
+ serviceName: {
7041
+ sql: "s.serviceName",
7042
+ parameterType: "String"
7043
+ },
7044
+ experimentId: {
7045
+ sql: "s.experimentId",
7046
+ parameterType: "String"
7047
+ },
7048
+ environment: {
7049
+ sql: "s.environment",
7050
+ parameterType: "String"
7051
+ },
7052
+ executionSource: {
7053
+ sql: "s.executionSource",
7054
+ parameterType: "String"
7055
+ },
7056
+ parentEntityType: {
7057
+ sql: "s.parentEntityType",
7058
+ parameterType: "String"
7059
+ },
7060
+ parentEntityId: {
7061
+ sql: "s.parentEntityId",
7062
+ parameterType: "String"
7063
+ },
7064
+ parentEntityName: {
7065
+ sql: "s.parentEntityName",
7066
+ parameterType: "String"
7067
+ },
7068
+ rootEntityType: {
7069
+ sql: "s.rootEntityType",
7070
+ parameterType: "String"
7071
+ },
7072
+ rootEntityId: {
7073
+ sql: "s.rootEntityId",
7074
+ parameterType: "String"
7075
+ },
7076
+ rootEntityName: {
7077
+ sql: "s.rootEntityName",
7078
+ parameterType: "String"
7079
+ }
7080
+ };
6988
7081
  const SCORE_FIELDS = {
6989
7082
  scorerId: {
6990
7083
  sql: "s.scorerId",
@@ -7856,11 +7949,11 @@ async function queryThreads(client, plan, timeoutMs) {
7856
7949
  }) : null }
7857
7950
  });
7858
7951
  }
7859
- /** Compile a span-row filter with exactly the same rules as trace span predicates. */
7952
+ /** Compile a direct span-query filter using its dedicated field mapping. */
7860
7953
  function compileSpanQueryPredicate(predicate) {
7861
7954
  const parameters = new ParameterBuilder();
7862
7955
  return {
7863
- sql: compileScalarPredicate(predicate, SPAN_FIELDS, parameters),
7956
+ sql: compileScalarPredicate(predicate, SPAN_QUERY_FIELDS, parameters),
7864
7957
  params: parameters.params
7865
7958
  };
7866
7959
  }
@@ -7874,7 +7967,6 @@ const identityFields = [
7874
7967
  ];
7875
7968
  const identities = identityFields.join(", ");
7876
7969
  const identitySetColumns = "isNull(organizationId), ifNull(organizationId, ''), isNull(resourceId), ifNull(resourceId, ''), traceId, spanId";
7877
- const identitySetKey = `tuple(${identitySetColumns})`;
7878
7970
  const displayFields = [
7879
7971
  "parentSpanId",
7880
7972
  "name",
@@ -7882,6 +7974,24 @@ const displayFields = [
7882
7974
  "entityType",
7883
7975
  "entityId",
7884
7976
  "entityName",
7977
+ "entityVersionId",
7978
+ "parentEntityType",
7979
+ "parentEntityId",
7980
+ "parentEntityName",
7981
+ "parentEntityVersionId",
7982
+ "rootEntityType",
7983
+ "rootEntityId",
7984
+ "rootEntityName",
7985
+ "rootEntityVersionId",
7986
+ "threadId",
7987
+ "runId",
7988
+ "sessionId",
7989
+ "userId",
7990
+ "requestId",
7991
+ "serviceName",
7992
+ "experimentId",
7993
+ "environment",
7994
+ "executionSource",
7885
7995
  "startedAt",
7886
7996
  "endedAt",
7887
7997
  "model",
@@ -7960,15 +8070,11 @@ function compileClickHouseSpanQuery(plan) {
7960
8070
  const expressions = selectionFields.map(fieldExpression);
7961
8071
  const after = afterCursor(plan, parameters);
7962
8072
  return {
7963
- query: `WITH candidate_ids AS (
7964
- SELECT ${identitySetKey} AS identity FROM ${TABLE_SPAN_EVENTS}
7965
- PREWHERE endedAt >= ${from}
7966
- WHERE startedAt >= ${from} AND startedAt < ${to} ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
7967
- ), candidates AS (
8073
+ query: `WITH candidates AS (
7968
8074
  SELECT ${identities}, ${selectionFields.map((field, index) => `${expressions[index]} AS ${field}`).join(", ")}
7969
8075
  FROM ${TABLE_SPAN_EVENTS} e
7970
8076
  PREWHERE e.endedAt >= ${from}
7971
- WHERE ${identitySetKey} IN (SELECT identity FROM candidate_ids) ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
8077
+ WHERE e.startedAt >= ${from} AND e.startedAt < ${to} ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
7972
8078
  ORDER BY ${identities}, e.endedAt DESC
7973
8079
  LIMIT 1 BY ${identities}
7974
8080
  )
@@ -7986,6 +8092,7 @@ function compileClickHouseSpanQuery(plan) {
7986
8092
  }
7987
8093
  };
7988
8094
  }
8095
+ const LEGACY_PAYLOAD_BATCH_ROWS = 32;
7989
8096
  function iso(value) {
7990
8097
  return new Date(value.includes("T") ? value : `${value.replace(" ", "T")}Z`).toISOString();
7991
8098
  }
@@ -8019,34 +8126,25 @@ async function querySpans(client, plan, timeoutMs) {
8019
8126
  });
8020
8127
  const missingKeys = new Set(rowsWithPreviews.filter(needsPreviewPayloads).map(_mastra_core_storage.spanQueryIdentityKey));
8021
8128
  const missing = page.filter((row) => missingKeys.has(_mastra_core_storage.spanQueryIdentityKey(row)));
8022
- const previewPayloads = /* @__PURE__ */ new Map();
8023
- if (missing.length) {
8129
+ const storedByIdentity = new Map(rowsWithPreviews.map((row) => [_mastra_core_storage.spanQueryIdentityKey(row), row]));
8130
+ for (let start = 0; start < missing.length; start += LEGACY_PAYLOAD_BATCH_ROWS) {
8024
8131
  const rows = await run({
8025
8132
  query: `SELECT ${identities}, data.1 AS input, data.2 AS output, data.3 AS error
8026
8133
  FROM (SELECT ${identities}, any(tuple(input, output, error)) AS data
8027
8134
  FROM ${TABLE_SPAN_EVENTS} e WHERE ${payloadWhere} GROUP BY ${identities})`,
8028
- query_params: { page: pageParameter(missing) }
8135
+ query_params: { page: pageParameter(missing.slice(start, start + LEGACY_PAYLOAD_BATCH_ROWS)) }
8029
8136
  });
8030
- for (const row of rows) previewPayloads.set(_mastra_core_storage.spanQueryIdentityKey(row), row);
8137
+ for (const row of rows) {
8138
+ const stored = storedByIdentity.get(_mastra_core_storage.spanQueryIdentityKey(row));
8139
+ if (stored) Object.assign(stored, resolveStoredPreviews(stored, row));
8140
+ }
8031
8141
  }
8032
8142
  const payloads = rowsWithPreviews.map((row) => ({
8033
8143
  ...row,
8034
- ...resolveStoredPreviews(row, previewPayloads.get(_mastra_core_storage.spanQueryIdentityKey(row)))
8144
+ ...resolveStoredPreviews(row)
8035
8145
  }));
8036
- const metricWhere = `${identitySetKey} IN {page:Array(Tuple(UInt8, String, UInt8, String, String, String))}`;
8037
- const metrics = await run({
8038
- query: `SELECT ${identities}, data.1 AS name, data.2 AS estimatedCost,
8039
- data.3 AS costUnit, data.4 AS allocation, data.5 AS costError
8040
- FROM (SELECT ${identities}, metricId, argMax(tuple(name, estimatedCost, costUnit,
8041
- JSONExtractString(costMetadata, 'allocation'), JSONHas(costMetadata, 'error')), timestamp) AS data
8042
- FROM ${TABLE_METRIC_EVENTS} WHERE (${metricWhere}) AND name LIKE 'mastra_model_%'
8043
- GROUP BY ${identities}, metricId)
8044
- LIMIT ${_mastra_core_storage.SPAN_QUERY_MAX_COST_METRICS + 1}`,
8045
- query_params: { page: page.map((row) => new _clickhouse_client.TupleParam(identityValues(row))) }
8046
- });
8047
- if (metrics.length > _mastra_core_storage.SPAN_QUERY_MAX_COST_METRICS) throw new _mastra_core_storage.TraceQueryResourceLimitError();
8048
8146
  const byIdentity = new Map(payloads.map((row) => [_mastra_core_storage.spanQueryIdentityKey(row), row]));
8049
- const result = buildSpanQueryResponse(plan, page.flatMap((identity) => {
8147
+ const hydrated = page.flatMap((identity) => {
8050
8148
  const row = byIdentity.get(_mastra_core_storage.spanQueryIdentityKey(identity));
8051
8149
  if (!row) return [];
8052
8150
  const { inputPreview: _input, outputPreview: _output, errorPreview: _error, ...metadata } = row;
@@ -8055,7 +8153,22 @@ async function querySpans(client, plan, timeoutMs) {
8055
8153
  ...identity,
8056
8154
  durationMs: Number(row.durationMs)
8057
8155
  }];
8058
- }), payloads, metrics);
8156
+ });
8157
+ let metrics = [];
8158
+ if (!_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY) {
8159
+ metrics = await run({
8160
+ query: `SELECT ${identities}, data.1 AS name, data.2 AS estimatedCost,
8161
+ data.3 AS costUnit, data.4 AS allocation, data.5 AS costError
8162
+ FROM (SELECT ${identities}, metricId, argMax(tuple(name, estimatedCost, costUnit,
8163
+ JSONExtractString(costMetadata, 'allocation'), JSONHas(costMetadata, 'error')), timestamp) AS data
8164
+ FROM ${TABLE_METRIC_EVENTS} WHERE (${`tuple(${identitySetColumns}) IN {page:Array(Tuple(UInt8, String, UInt8, String, String, String))}`}) AND name LIKE 'mastra_model_%'
8165
+ GROUP BY ${identities}, metricId)
8166
+ LIMIT ${_mastra_core_storage.SPAN_QUERY_MAX_COST_METRICS + 1}`,
8167
+ query_params: { page: page.map((row) => new _clickhouse_client.TupleParam(identityValues(row))) }
8168
+ });
8169
+ if (metrics.length > _mastra_core_storage.SPAN_QUERY_MAX_COST_METRICS) throw new _mastra_core_storage.TraceQueryResourceLimitError();
8170
+ }
8171
+ const result = buildSpanQueryResponse(plan, hydrated, payloads, metrics);
8059
8172
  const last = page.at(-1);
8060
8173
  result.page.next = selected.length > plan.limit ? _mastra_core_storage.encodeSpanQueryCursor(plan, {
8061
8174
  organizationId: last.organizationId,
@@ -9214,7 +9327,7 @@ function buildBranchCursor(row) {
9214
9327
  }
9215
9328
  //#endregion
9216
9329
  //#region src/storage/domains/observability/v-next/index.ts
9217
- const spanQueryFeatures = typeof _mastra_core_storage.planSpanQuery === "function" ? ["span-query"] : [];
9330
+ const spanQueryFeatures = typeof _mastra_core_storage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
9218
9331
  function buildSignalMigrationRequiredMessage(args) {
9219
9332
  const tableList = args.tables.map((table) => ` - ${table.table} (${table.engine})`).join("\n");
9220
9333
  return `