@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.
package/dist/index.js CHANGED
@@ -2720,6 +2720,7 @@ CREATE TABLE IF NOT EXISTS ${TABLE_SPAN_EVENTS} (
2720
2720
  isEvent Bool DEFAULT false,
2721
2721
  startedAt DateTime64(3, 'UTC'),
2722
2722
  endedAt DateTime64(3, 'UTC'),
2723
+ timeToFirstChunkMs Nullable(Float64),
2723
2724
 
2724
2725
  -- Query-relevant flexible fields
2725
2726
  tags Array(LowCardinality(String)) DEFAULT [],
@@ -2793,6 +2794,7 @@ CREATE TABLE IF NOT EXISTS ${TABLE_TRACE_ROOTS} (
2793
2794
  isEvent Bool DEFAULT false,
2794
2795
  startedAt DateTime64(3, 'UTC'),
2795
2796
  endedAt DateTime64(3, 'UTC'),
2797
+ timeToFirstChunkMs Nullable(Float64),
2796
2798
 
2797
2799
  -- Query-relevant flexible fields
2798
2800
  tags Array(LowCardinality(String)) DEFAULT [],
@@ -2878,6 +2880,7 @@ CREATE TABLE IF NOT EXISTS ${TABLE_TRACE_BRANCHES} (
2878
2880
  isEvent Bool DEFAULT false,
2879
2881
  startedAt DateTime64(3, 'UTC'),
2880
2882
  endedAt DateTime64(3, 'UTC'),
2883
+ timeToFirstChunkMs Nullable(Float64),
2881
2884
 
2882
2885
  -- Query-relevant flexible fields
2883
2886
  tags Array(LowCardinality(String)) DEFAULT [],
@@ -3683,7 +3686,10 @@ const ALL_MIGRATIONS = [
3683
3686
  addBloomIndex(TABLE_METRIC_EVENTS, "idx_sessionId", "sessionId"),
3684
3687
  addBloomIndex(TABLE_METRIC_EVENTS, "idx_requestId", "requestId"),
3685
3688
  ...LOG_SKIP_INDEX_COLUMNS.map((column) => addBloomIndex(TABLE_LOG_EVENTS, `idx_${column}`, column)),
3686
- addBloomIndex(TABLE_DELETION_REQUESTS, "idx_predicateValues", "predicateValues")
3689
+ addBloomIndex(TABLE_DELETION_REQUESTS, "idx_predicateValues", "predicateValues"),
3690
+ addColumn(TABLE_TRACE_ROOTS, "timeToFirstChunkMs", "Nullable(Float64)"),
3691
+ addColumn(TABLE_TRACE_BRANCHES, "timeToFirstChunkMs", "Nullable(Float64)"),
3692
+ addColumn(TABLE_SPAN_EVENTS, "timeToFirstChunkMs", "Nullable(Float64)")
3687
3693
  ];
3688
3694
  const ALL_TABLE_NAMES = [
3689
3695
  TABLE_SPAN_EVENTS,
@@ -3815,9 +3821,27 @@ function readPreview(preview, payload, jsonEncoded = false) {
3815
3821
  }
3816
3822
  /** Older response builders may shorten previews or turn empty strings into null. */
3817
3823
  function buildSpanQueryResponse(...args) {
3818
- if (canGeneratePreviews) return coreStorage.buildSpanQueryResponse(...args);
3819
- const [plan, selected, payloads, metrics] = args;
3820
- const response = coreStorage.buildSpanQueryResponse(plan, selected, [], metrics);
3824
+ const [plan, selected, payloads, metrics = []] = args;
3825
+ const compatibleSelected = coreStorage.SPAN_QUERY_FIELD_REGISTRY ? selected : selected.map((row) => ({
3826
+ organizationId: row.organizationId,
3827
+ resourceId: row.resourceId,
3828
+ traceId: row.traceId,
3829
+ spanId: row.spanId,
3830
+ parentSpanId: row.parentSpanId,
3831
+ name: row.name,
3832
+ spanType: row.spanType,
3833
+ status: row.status,
3834
+ startedAt: row.startedAt,
3835
+ endedAt: row.endedAt,
3836
+ durationMs: row.durationMs,
3837
+ entityType: row.entityType,
3838
+ entityId: row.entityId,
3839
+ entityName: row.entityName,
3840
+ model: row.model,
3841
+ provider: row.provider
3842
+ }));
3843
+ if (canGeneratePreviews) return coreStorage.buildSpanQueryResponse(plan, compatibleSelected, payloads, metrics);
3844
+ const response = coreStorage.buildSpanQueryResponse(plan, compatibleSelected, [], metrics);
3821
3845
  const byIdentity = new Map(payloads.map((payload) => [coreStorage.spanQueryIdentityKey(payload), payload]));
3822
3846
  for (const span of response.spans) {
3823
3847
  const payload = byIdentity.get(coreStorage.spanQueryIdentityKey(span));
@@ -4114,6 +4138,7 @@ function spanRecordToRow(span) {
4114
4138
  isEvent: span.isEvent,
4115
4139
  startedAt: toISOString(span.startedAt),
4116
4140
  endedAt: toISOString(endedAt),
4141
+ timeToFirstChunkMs: span.timeToFirstChunkMs ?? null,
4117
4142
  tags: normalizeTags(span.tags),
4118
4143
  metadataSearch: buildMetadataSearch(metadata),
4119
4144
  metadataRaw: jsonEncode(metadata),
@@ -4769,6 +4794,7 @@ function buildMetricsFilterConditions(filters, tableAlias) {
4769
4794
  assertNoDeprecatedSourceFilter(filters.source, "executionSource", "metrics");
4770
4795
  addCommonFilterFields(filters, tableAlias, out);
4771
4796
  addIn(col("traceId"), filters.traceIds, "traceIds", out);
4797
+ addIn(col("experimentId"), filters.experimentIds, "experimentIds", out);
4772
4798
  addIn(col("name"), filters.name, "metricNames", out);
4773
4799
  addEq(col("provider"), filters.provider, "provider", "String", out);
4774
4800
  addEq(col("model"), filters.model, "model", "String", out);
@@ -6962,6 +6988,73 @@ const SPAN_FIELDS = {
6962
6988
  parameterType: "String"
6963
6989
  }
6964
6990
  };
6991
+ const SPAN_QUERY_FIELDS = {
6992
+ ...SPAN_FIELDS,
6993
+ traceId: {
6994
+ sql: "s.traceId",
6995
+ parameterType: "String"
6996
+ },
6997
+ spanId: {
6998
+ sql: "s.spanId",
6999
+ parameterType: "String"
7000
+ },
7001
+ parentSpanId: {
7002
+ sql: "s.parentSpanId",
7003
+ parameterType: "String"
7004
+ },
7005
+ threadId: {
7006
+ sql: "s.threadId",
7007
+ parameterType: "String"
7008
+ },
7009
+ resourceId: {
7010
+ sql: "s.resourceId",
7011
+ parameterType: "String"
7012
+ },
7013
+ requestId: {
7014
+ sql: "s.requestId",
7015
+ parameterType: "String"
7016
+ },
7017
+ serviceName: {
7018
+ sql: "s.serviceName",
7019
+ parameterType: "String"
7020
+ },
7021
+ experimentId: {
7022
+ sql: "s.experimentId",
7023
+ parameterType: "String"
7024
+ },
7025
+ environment: {
7026
+ sql: "s.environment",
7027
+ parameterType: "String"
7028
+ },
7029
+ executionSource: {
7030
+ sql: "s.executionSource",
7031
+ parameterType: "String"
7032
+ },
7033
+ parentEntityType: {
7034
+ sql: "s.parentEntityType",
7035
+ parameterType: "String"
7036
+ },
7037
+ parentEntityId: {
7038
+ sql: "s.parentEntityId",
7039
+ parameterType: "String"
7040
+ },
7041
+ parentEntityName: {
7042
+ sql: "s.parentEntityName",
7043
+ parameterType: "String"
7044
+ },
7045
+ rootEntityType: {
7046
+ sql: "s.rootEntityType",
7047
+ parameterType: "String"
7048
+ },
7049
+ rootEntityId: {
7050
+ sql: "s.rootEntityId",
7051
+ parameterType: "String"
7052
+ },
7053
+ rootEntityName: {
7054
+ sql: "s.rootEntityName",
7055
+ parameterType: "String"
7056
+ }
7057
+ };
6965
7058
  const SCORE_FIELDS = {
6966
7059
  scorerId: {
6967
7060
  sql: "s.scorerId",
@@ -7833,11 +7926,11 @@ async function queryThreads(client, plan, timeoutMs) {
7833
7926
  }) : null }
7834
7927
  });
7835
7928
  }
7836
- /** Compile a span-row filter with exactly the same rules as trace span predicates. */
7929
+ /** Compile a direct span-query filter using its dedicated field mapping. */
7837
7930
  function compileSpanQueryPredicate(predicate) {
7838
7931
  const parameters = new ParameterBuilder();
7839
7932
  return {
7840
- sql: compileScalarPredicate(predicate, SPAN_FIELDS, parameters),
7933
+ sql: compileScalarPredicate(predicate, SPAN_QUERY_FIELDS, parameters),
7841
7934
  params: parameters.params
7842
7935
  };
7843
7936
  }
@@ -7851,7 +7944,6 @@ const identityFields = [
7851
7944
  ];
7852
7945
  const identities = identityFields.join(", ");
7853
7946
  const identitySetColumns = "isNull(organizationId), ifNull(organizationId, ''), isNull(resourceId), ifNull(resourceId, ''), traceId, spanId";
7854
- const identitySetKey = `tuple(${identitySetColumns})`;
7855
7947
  const displayFields = [
7856
7948
  "parentSpanId",
7857
7949
  "name",
@@ -7859,6 +7951,24 @@ const displayFields = [
7859
7951
  "entityType",
7860
7952
  "entityId",
7861
7953
  "entityName",
7954
+ "entityVersionId",
7955
+ "parentEntityType",
7956
+ "parentEntityId",
7957
+ "parentEntityName",
7958
+ "parentEntityVersionId",
7959
+ "rootEntityType",
7960
+ "rootEntityId",
7961
+ "rootEntityName",
7962
+ "rootEntityVersionId",
7963
+ "threadId",
7964
+ "runId",
7965
+ "sessionId",
7966
+ "userId",
7967
+ "requestId",
7968
+ "serviceName",
7969
+ "experimentId",
7970
+ "environment",
7971
+ "executionSource",
7862
7972
  "startedAt",
7863
7973
  "endedAt",
7864
7974
  "model",
@@ -7937,15 +8047,11 @@ function compileClickHouseSpanQuery(plan) {
7937
8047
  const expressions = selectionFields.map(fieldExpression);
7938
8048
  const after = afterCursor(plan, parameters);
7939
8049
  return {
7940
- query: `WITH candidate_ids AS (
7941
- SELECT ${identitySetKey} AS identity FROM ${TABLE_SPAN_EVENTS}
7942
- PREWHERE endedAt >= ${from}
7943
- WHERE startedAt >= ${from} AND startedAt < ${to} ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
7944
- ), candidates AS (
8050
+ query: `WITH candidates AS (
7945
8051
  SELECT ${identities}, ${selectionFields.map((field, index) => `${expressions[index]} AS ${field}`).join(", ")}
7946
8052
  FROM ${TABLE_SPAN_EVENTS} e
7947
8053
  PREWHERE e.endedAt >= ${from}
7948
- WHERE ${identitySetKey} IN (SELECT identity FROM candidate_ids) ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
8054
+ WHERE e.startedAt >= ${from} AND e.startedAt < ${to} ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
7949
8055
  ORDER BY ${identities}, e.endedAt DESC
7950
8056
  LIMIT 1 BY ${identities}
7951
8057
  )
@@ -7963,6 +8069,7 @@ function compileClickHouseSpanQuery(plan) {
7963
8069
  }
7964
8070
  };
7965
8071
  }
8072
+ const LEGACY_PAYLOAD_BATCH_ROWS = 32;
7966
8073
  function iso(value) {
7967
8074
  return new Date(value.includes("T") ? value : `${value.replace(" ", "T")}Z`).toISOString();
7968
8075
  }
@@ -7996,34 +8103,25 @@ async function querySpans(client, plan, timeoutMs) {
7996
8103
  });
7997
8104
  const missingKeys = new Set(rowsWithPreviews.filter(needsPreviewPayloads).map(coreStorage.spanQueryIdentityKey));
7998
8105
  const missing = page.filter((row) => missingKeys.has(coreStorage.spanQueryIdentityKey(row)));
7999
- const previewPayloads = /* @__PURE__ */ new Map();
8000
- if (missing.length) {
8106
+ const storedByIdentity = new Map(rowsWithPreviews.map((row) => [coreStorage.spanQueryIdentityKey(row), row]));
8107
+ for (let start = 0; start < missing.length; start += LEGACY_PAYLOAD_BATCH_ROWS) {
8001
8108
  const rows = await run({
8002
8109
  query: `SELECT ${identities}, data.1 AS input, data.2 AS output, data.3 AS error
8003
8110
  FROM (SELECT ${identities}, any(tuple(input, output, error)) AS data
8004
8111
  FROM ${TABLE_SPAN_EVENTS} e WHERE ${payloadWhere} GROUP BY ${identities})`,
8005
- query_params: { page: pageParameter(missing) }
8112
+ query_params: { page: pageParameter(missing.slice(start, start + LEGACY_PAYLOAD_BATCH_ROWS)) }
8006
8113
  });
8007
- for (const row of rows) previewPayloads.set(coreStorage.spanQueryIdentityKey(row), row);
8114
+ for (const row of rows) {
8115
+ const stored = storedByIdentity.get(coreStorage.spanQueryIdentityKey(row));
8116
+ if (stored) Object.assign(stored, resolveStoredPreviews(stored, row));
8117
+ }
8008
8118
  }
8009
8119
  const payloads = rowsWithPreviews.map((row) => ({
8010
8120
  ...row,
8011
- ...resolveStoredPreviews(row, previewPayloads.get(coreStorage.spanQueryIdentityKey(row)))
8121
+ ...resolveStoredPreviews(row)
8012
8122
  }));
8013
- const metricWhere = `${identitySetKey} IN {page:Array(Tuple(UInt8, String, UInt8, String, String, String))}`;
8014
- const metrics = await run({
8015
- query: `SELECT ${identities}, data.1 AS name, data.2 AS estimatedCost,
8016
- data.3 AS costUnit, data.4 AS allocation, data.5 AS costError
8017
- FROM (SELECT ${identities}, metricId, argMax(tuple(name, estimatedCost, costUnit,
8018
- JSONExtractString(costMetadata, 'allocation'), JSONHas(costMetadata, 'error')), timestamp) AS data
8019
- FROM ${TABLE_METRIC_EVENTS} WHERE (${metricWhere}) AND name LIKE 'mastra_model_%'
8020
- GROUP BY ${identities}, metricId)
8021
- LIMIT ${coreStorage.SPAN_QUERY_MAX_COST_METRICS + 1}`,
8022
- query_params: { page: page.map((row) => new TupleParam(identityValues(row))) }
8023
- });
8024
- if (metrics.length > coreStorage.SPAN_QUERY_MAX_COST_METRICS) throw new coreStorage.TraceQueryResourceLimitError();
8025
8123
  const byIdentity = new Map(payloads.map((row) => [coreStorage.spanQueryIdentityKey(row), row]));
8026
- const result = buildSpanQueryResponse(plan, page.flatMap((identity) => {
8124
+ const hydrated = page.flatMap((identity) => {
8027
8125
  const row = byIdentity.get(coreStorage.spanQueryIdentityKey(identity));
8028
8126
  if (!row) return [];
8029
8127
  const { inputPreview: _input, outputPreview: _output, errorPreview: _error, ...metadata } = row;
@@ -8032,7 +8130,22 @@ async function querySpans(client, plan, timeoutMs) {
8032
8130
  ...identity,
8033
8131
  durationMs: Number(row.durationMs)
8034
8132
  }];
8035
- }), payloads, metrics);
8133
+ });
8134
+ let metrics = [];
8135
+ if (!coreStorage.SPAN_QUERY_FIELD_REGISTRY) {
8136
+ metrics = await run({
8137
+ query: `SELECT ${identities}, data.1 AS name, data.2 AS estimatedCost,
8138
+ data.3 AS costUnit, data.4 AS allocation, data.5 AS costError
8139
+ FROM (SELECT ${identities}, metricId, argMax(tuple(name, estimatedCost, costUnit,
8140
+ JSONExtractString(costMetadata, 'allocation'), JSONHas(costMetadata, 'error')), timestamp) AS data
8141
+ FROM ${TABLE_METRIC_EVENTS} WHERE (${`tuple(${identitySetColumns}) IN {page:Array(Tuple(UInt8, String, UInt8, String, String, String))}`}) AND name LIKE 'mastra_model_%'
8142
+ GROUP BY ${identities}, metricId)
8143
+ LIMIT ${coreStorage.SPAN_QUERY_MAX_COST_METRICS + 1}`,
8144
+ query_params: { page: page.map((row) => new TupleParam(identityValues(row))) }
8145
+ });
8146
+ if (metrics.length > coreStorage.SPAN_QUERY_MAX_COST_METRICS) throw new coreStorage.TraceQueryResourceLimitError();
8147
+ }
8148
+ const result = buildSpanQueryResponse(plan, hydrated, payloads, metrics);
8036
8149
  const last = page.at(-1);
8037
8150
  result.page.next = selected.length > plan.limit ? coreStorage.encodeSpanQueryCursor(plan, {
8038
8151
  organizationId: last.organizationId,
@@ -9191,7 +9304,7 @@ function buildBranchCursor(row) {
9191
9304
  }
9192
9305
  //#endregion
9193
9306
  //#region src/storage/domains/observability/v-next/index.ts
9194
- const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? ["span-query"] : [];
9307
+ const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
9195
9308
  function buildSignalMigrationRequiredMessage(args) {
9196
9309
  const tableList = args.tables.map((table) => ` - ${table.table} (${table.engine})`).join("\n");
9197
9310
  return `