@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.
package/dist/index.js CHANGED
@@ -759,6 +759,7 @@ var ClickhouseDB = class extends MastraBase {
759
759
  const rawUpdatedAt = filteredRecord.updatedAt || /* @__PURE__ */ new Date();
760
760
  if ("createdAt" in filteredRecord || (await this.getTableColumns(tableName)).has("createdAt")) filteredRecord.createdAt = rawCreatedAt instanceof Date ? rawCreatedAt.toISOString() : rawCreatedAt;
761
761
  if ("updatedAt" in filteredRecord || (await this.getTableColumns(tableName)).has("updatedAt")) filteredRecord.updatedAt = rawUpdatedAt instanceof Date ? rawUpdatedAt.toISOString() : rawUpdatedAt;
762
+ for (const [key, value] of Object.entries(filteredRecord)) if (TABLE_SCHEMAS[tableName]?.[key]?.type === "timestamp" && typeof value === "number") filteredRecord[key] = new Date(value).toISOString();
762
763
  await this.client.insert({
763
764
  table: tableName,
764
765
  values: [filteredRecord],
@@ -2271,12 +2272,12 @@ var ObservabilityStorageClickhouse = class extends ObservabilityStorage {
2271
2272
  }
2272
2273
  });
2273
2274
  const data = { ...updates };
2274
- if (data.endedAt instanceof Date) data.endedAt = data.endedAt.getTime();
2275
- if (data.startedAt instanceof Date) data.startedAt = data.startedAt.getTime();
2275
+ if (data.endedAt instanceof Date) data.endedAt = data.endedAt.toISOString();
2276
+ if (data.startedAt instanceof Date) data.startedAt = data.startedAt.toISOString();
2276
2277
  const updated = {
2277
2278
  ...existing,
2278
2279
  ...data,
2279
- updatedAt: Date.now()
2280
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2280
2281
  };
2281
2282
  await this.client.insert({
2282
2283
  table: TABLE_SPANS,
@@ -2514,7 +2515,7 @@ var ObservabilityStorageClickhouse = class extends ObservabilityStorage {
2514
2515
  }
2515
2516
  async batchUpdateSpans(args) {
2516
2517
  try {
2517
- const now = Date.now();
2518
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2518
2519
  if (args.records.length === 0) return;
2519
2520
  const keyParams = {};
2520
2521
  const keyTuples = args.records.map((record, i) => {
@@ -2547,8 +2548,8 @@ var ObservabilityStorageClickhouse = class extends ObservabilityStorage {
2547
2548
  const existing = current.get(key);
2548
2549
  if (!existing) continue;
2549
2550
  const updates = { ...record.updates };
2550
- if (updates.startedAt instanceof Date) updates.startedAt = updates.startedAt.getTime();
2551
- if (updates.endedAt instanceof Date) updates.endedAt = updates.endedAt.getTime();
2551
+ if (updates.startedAt instanceof Date) updates.startedAt = updates.startedAt.toISOString();
2552
+ if (updates.endedAt instanceof Date) updates.endedAt = updates.endedAt.toISOString();
2552
2553
  current.set(key, {
2553
2554
  ...existing,
2554
2555
  ...updates,
@@ -6915,6 +6916,13 @@ const timingFeatures = [
6915
6916
  ...timeToFirstTokenSupported ? ["trace-query-time-to-first-token"] : [],
6916
6917
  ...timeToFirstTokenSupported && timeToFirstTokenSortSupported ? ["trace-query-time-to-first-token-sort"] : []
6917
6918
  ];
6919
+ /**
6920
+ * Outcome fields follow the same rule: core before them rejects their predicates and strict rows,
6921
+ * so they are only returned and declared when core knows them.
6922
+ */
6923
+ const spanQueryOutcomeFieldsSupported = "outcome" in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {});
6924
+ const traceQueryOutcomeFieldsSupported = "outcome" in (coreStorage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
6925
+ const outcomeFeatures = [...spanQueryOutcomeFieldsSupported ? ["span-query-outcome-fields"] : [], ...traceQueryOutcomeFieldsSupported ? ["trace-query-outcome-fields"] : []];
6918
6926
  /** Order fields holding numbers that can be empty; the rest are timestamps. */
6919
6927
  const NUMERIC_ORDER_FIELDS = /* @__PURE__ */ new Set(["timeToFirstChunkMs", "timeToFirstTokenMs"]);
6920
6928
  function durationMsSql(startedAt, endedAt) {
@@ -7001,6 +7009,14 @@ const TRACE_FIELDS = {
7001
7009
  sql: TRACE_STATUS_SQL,
7002
7010
  parameterType: "String"
7003
7011
  },
7012
+ outcome: {
7013
+ sql: "r.outcome",
7014
+ parameterType: "String"
7015
+ },
7016
+ stopReason: {
7017
+ sql: "r.stopReason",
7018
+ parameterType: "String"
7019
+ },
7004
7020
  tags: {
7005
7021
  sql: "r.tags",
7006
7022
  parameterType: "String"
@@ -7047,6 +7063,14 @@ const SPAN_FIELDS = {
7047
7063
  sql: "s.status",
7048
7064
  parameterType: "String"
7049
7065
  },
7066
+ outcome: {
7067
+ sql: "s.outcome",
7068
+ parameterType: "String"
7069
+ },
7070
+ stopReason: {
7071
+ sql: "s.stopReason",
7072
+ parameterType: "String"
7073
+ },
7050
7074
  error: {
7051
7075
  sql: "s.error",
7052
7076
  parameterType: "String"
@@ -7256,6 +7280,9 @@ const TRACE_SELECT = `
7256
7280
  r.environment AS environment,
7257
7281
  r.timeToFirstChunkMs AS timeToFirstChunkMs,
7258
7282
  r.timeToFirstTokenMs AS timeToFirstTokenMs,
7283
+ r.outcome AS outcome,
7284
+ r.stopReason AS stopReason,
7285
+ r.stopReasonContext AS stopReasonContext,
7259
7286
  ${TRACE_STATUS_SQL} AS status`;
7260
7287
  var ParameterBuilder = class {
7261
7288
  params = {};
@@ -7456,14 +7483,16 @@ function compileClickHouseTraceScope(selection, relationCollections, parameters,
7456
7483
  traceId,
7457
7484
  name,
7458
7485
  spanType,
7459
- if(JSONType(attributes, 'model') = 'String', JSONExtractString(attributes, 'model'), NULL) AS model,
7460
- if(JSONType(attributes, 'provider') = 'String', JSONExtractString(attributes, 'provider'), NULL) AS provider,
7486
+ coalesce(model, if(JSONType(attributes, 'model') = 'String', JSONExtractString(attributes, 'model'), NULL)) AS model,
7487
+ coalesce(provider, if(JSONType(attributes, 'provider') = 'String', JSONExtractString(attributes, 'provider'), NULL)) AS provider,
7461
7488
  startedAt,
7462
7489
  endedAt,
7463
7490
  ${durationMsSql("startedAt", "endedAt")} AS durationMs,
7464
7491
  timeToFirstChunkMs,
7465
7492
  timeToFirstTokenMs,
7466
7493
  if(isNotNull(error), 'error', 'success') AS status,
7494
+ outcome,
7495
+ stopReason,
7467
7496
  error,
7468
7497
  entityType,
7469
7498
  entityId,
@@ -7567,7 +7596,7 @@ const QUERY_TRACES_SCOPE = {
7567
7596
  oneRootPerTrace: true,
7568
7597
  spanExistence: true
7569
7598
  };
7570
- const TRACE_LIST_SELECT = TRACE_SELECT.split("\n").filter((line) => !/ AS (metadata|inputPreview|outputPreview|errorPreview),$/.test(line)).join("\n");
7599
+ const TRACE_LIST_SELECT = TRACE_SELECT.split("\n").filter((line) => !/ AS (metadata|inputPreview|outputPreview|errorPreview|stopReasonContext),$/.test(line)).join("\n");
7571
7600
  function traceOrder(plan) {
7572
7601
  return {
7573
7602
  field: resolveOrderField(plan.orderBy.field),
@@ -7727,7 +7756,7 @@ function compileClickHouseTraceRootPayloads(keys, scope, columns = "payload") {
7727
7756
  const tenant = (value) => `${parameters.add(value ?? "", "String")}, ${value === null ? 1 : 0}`;
7728
7757
  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)})`);
7729
7758
  return {
7730
- query: `SELECT traceId, spanId AS rootSpanId, dedupeKey, organizationId, resourceId, ${columns === "payload" ? `metadataRaw AS metadata, ${PREVIEW_COLUMNS.join(", ")}` : "input, output, error"}
7759
+ query: `SELECT traceId, spanId AS rootSpanId, dedupeKey, organizationId, resourceId, ${columns === "payload" ? `metadataRaw AS metadata, stopReasonContext, ${PREVIEW_COLUMNS.join(", ")}` : "input, output, error"}
7731
7760
  FROM ${TABLE_TRACE_ROOTS}
7732
7761
  WHERE (startedAt, traceId, dedupeKey, endedAt, ifNull(organizationId, ''), isNull(organizationId), ifNull(resourceId, ''), isNull(resourceId)) IN (${tuples.join(", ")})${compileTenantScope(scope, parameters)}
7733
7762
  ORDER BY ${ROOT_VERSION_ORDER}
@@ -7994,7 +8023,12 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
7994
8023
  environment: row.environment == null ? null : String(row.environment),
7995
8024
  status: row.status,
7996
8025
  ...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) },
7997
- ...timeToFirstTokenSupported && { timeToFirstTokenMs: row.timeToFirstTokenMs == null ? null : Number(row.timeToFirstTokenMs) }
8026
+ ...timeToFirstTokenSupported && { timeToFirstTokenMs: row.timeToFirstTokenMs == null ? null : Number(row.timeToFirstTokenMs) },
8027
+ ...traceQueryOutcomeFieldsSupported && {
8028
+ outcome: row.outcome == null ? null : String(row.outcome),
8029
+ stopReason: row.stopReason == null ? null : String(row.stopReason),
8030
+ stopReasonContext: parseJson(row.stopReasonContext) ?? null
8031
+ }
7998
8032
  });
7999
8033
  if (plan.paginationMode === "delta") {
8000
8034
  const rows = await run(compileClickHouseTraceQuery(plan, deltaHead));
@@ -8121,7 +8155,10 @@ const displayFields = [
8121
8155
  "durationMs",
8122
8156
  "timeToFirstChunkMs",
8123
8157
  "timeToFirstTokenMs",
8124
- "status"
8158
+ "status",
8159
+ "outcome",
8160
+ "stopReason",
8161
+ "stopReasonContext"
8125
8162
  ];
8126
8163
  var Parameters = class {
8127
8164
  values = {};
@@ -8173,13 +8210,18 @@ function predicateFields$1(predicate, fields = /* @__PURE__ */ new Set()) {
8173
8210
  else fields.add(predicate.field);
8174
8211
  return fields;
8175
8212
  }
8213
+ /** Selection and filter expressions read only columns; `model`/`provider` read their dedicated columns. */
8176
8214
  function fieldExpression(field) {
8177
- if (field === "model" || field === "provider") return `if(JSONType(e.attributes, '${field}') = 'String', JSONExtractString(e.attributes, '${field}'), NULL)`;
8178
8215
  if (field === "durationMs") return "dateDiff('millisecond', e.startedAt, e.endedAt)";
8179
8216
  if (field === "status") return "if(isNull(e.error), 'success', 'error')";
8180
8217
  if (field === "error") return "if(isNull(e.error), NULL, 'error')";
8181
8218
  return `e.${field}`;
8182
8219
  }
8220
+ /** Returned values fall back to span attributes for rows written before the dedicated columns existed. */
8221
+ function displayExpression(field) {
8222
+ if (field === "model" || field === "provider") return `coalesce(e.${field}, if(JSONType(e.attributes, '${field}') = 'String', JSONExtractString(e.attributes, '${field}'), NULL))`;
8223
+ return fieldExpression(field);
8224
+ }
8183
8225
  function compileClickHouseSpanQuery(plan) {
8184
8226
  const parameters = new Parameters();
8185
8227
  const from = parameters.add(plan.timeRange.from, "DateTime64(3, 'UTC')");
@@ -8266,7 +8308,11 @@ const TRACE_BRANCHES_SPAN_FIELD_COLUMNS = {
8266
8308
  "rootEntityType",
8267
8309
  "rootEntityId",
8268
8310
  "rootEntityName",
8269
- "rootEntityVersionId"
8311
+ "rootEntityVersionId",
8312
+ "model",
8313
+ "provider",
8314
+ "outcome",
8315
+ "stopReason"
8270
8316
  ].map((field) => [field, sameColumn(field)])),
8271
8317
  status: ["error"],
8272
8318
  error: ["error"],
@@ -8379,7 +8425,7 @@ async function querySpans(client, plan, timeoutMs) {
8379
8425
  const pageParameter = (rows) => rows.map((row) => new TupleParam([...identityValues(row), row.endedAt.replace("T", " ").replace(/Z$/, "")]));
8380
8426
  const rowsWithPreviews = await run({
8381
8427
  query: `SELECT ${identities}, ${[...displayFields, ...PREVIEW_COLUMNS].map((field, index) => `data.${index + 1} AS ${field}`).join(", ")}
8382
- FROM (SELECT ${identities}, any(tuple(${[...displayFields.map(fieldExpression), ...PREVIEW_COLUMNS].join(", ")})) AS data
8428
+ FROM (SELECT ${identities}, any(tuple(${[...displayFields.map(displayExpression), ...PREVIEW_COLUMNS].join(", ")})) AS data
8383
8429
  FROM ${TABLE_SPAN_EVENTS} e WHERE ${payloadWhere} GROUP BY ${identities})`,
8384
8430
  query_params: { page: pageParameter(page) }
8385
8431
  });
@@ -8406,13 +8452,18 @@ async function querySpans(client, plan, timeoutMs) {
8406
8452
  const hydrated = page.flatMap((identity) => {
8407
8453
  const row = byIdentity.get(coreStorage.spanQueryIdentityKey(identity));
8408
8454
  if (!row) return [];
8409
- const { inputPreview: _input, outputPreview: _output, errorPreview: _error, timeToFirstChunkMs, timeToFirstTokenMs, ...metadata } = row;
8455
+ const { inputPreview: _input, outputPreview: _output, errorPreview: _error, timeToFirstChunkMs, timeToFirstTokenMs, outcome, stopReason, stopReasonContext, ...metadata } = row;
8410
8456
  return [{
8411
8457
  ...metadata,
8412
8458
  ...identity,
8413
8459
  durationMs: Number(row.durationMs),
8414
8460
  ...timeToFirstChunkSupported && { timeToFirstChunkMs: timeToFirstChunkMs == null ? null : Number(timeToFirstChunkMs) },
8415
- ...timeToFirstTokenSupported && { timeToFirstTokenMs: timeToFirstTokenMs == null ? null : Number(timeToFirstTokenMs) }
8461
+ ...timeToFirstTokenSupported && { timeToFirstTokenMs: timeToFirstTokenMs == null ? null : Number(timeToFirstTokenMs) },
8462
+ ...spanQueryOutcomeFieldsSupported && {
8463
+ outcome: outcome ?? null,
8464
+ stopReason: stopReason ?? null,
8465
+ stopReasonContext: parseJson(stopReasonContext) ?? null
8466
+ }
8416
8467
  }];
8417
8468
  });
8418
8469
  let metrics = [];
@@ -8727,6 +8778,8 @@ const DIMENSION_SQL = {
8727
8778
  entityId: "r.entityId",
8728
8779
  environment: "r.environment",
8729
8780
  status: TRACE_STATUS_SQL,
8781
+ outcome: "r.outcome",
8782
+ stopReason: "r.stopReason",
8730
8783
  serviceName: "r.serviceName",
8731
8784
  executionSource: "r.executionSource",
8732
8785
  threadId: "r.threadId",
@@ -8749,6 +8802,8 @@ const SPAN_DIMENSION_SQL = Object.fromEntries([
8749
8802
  "rootEntityName",
8750
8803
  "environment",
8751
8804
  "status",
8805
+ "outcome",
8806
+ "stopReason",
8752
8807
  "serviceName",
8753
8808
  "executionSource",
8754
8809
  "threadId",
@@ -8756,7 +8811,9 @@ const SPAN_DIMENSION_SQL = Object.fromEntries([
8756
8811
  "userId",
8757
8812
  "sessionId",
8758
8813
  "organizationId",
8759
- "experimentId"
8814
+ "experimentId",
8815
+ "model",
8816
+ "provider"
8760
8817
  ].map((field) => [field, `r.${field}`]));
8761
8818
  const PERCENTILES = {
8762
8819
  "duration.p50": .5,
@@ -9918,8 +9975,12 @@ function buildBranchCursor(row) {
9918
9975
  return row.cursorId ?? "0";
9919
9976
  }
9920
9977
  // src/storage/domains/observability/v-next/index.ts
9921
- const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
9922
- const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate"] : [];
9978
+ const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? [
9979
+ "span-query",
9980
+ "span-query-context-fields",
9981
+ ..."model" in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? ["span-query-model-fields"] : []
9982
+ ] : [];
9983
+ const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate", ..."model" in (coreStorage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {}) ? ["span-aggregate-model-dimensions"] : []] : [];
9923
9984
  const threadQueryFeatures = typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [];
9924
9985
  function buildSignalMigrationRequiredMessage(args) {
9925
9986
  const tableList = args.tables.map((table) => ` - ${table.table} (${table.engine})`).join("\n");
@@ -10359,6 +10420,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
10359
10420
  "feedback",
10360
10421
  "trace-query-context-ids",
10361
10422
  ...timingFeatures,
10423
+ ...outcomeFeatures,
10362
10424
  "trace-query-root-span-fields",
10363
10425
  "trace-query-root-entity-id",
10364
10426
  "trace-aggregate",
@@ -10384,6 +10446,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
10384
10446
  "feedback",
10385
10447
  "trace-query-context-ids",
10386
10448
  ...timingFeatures,
10449
+ ...outcomeFeatures,
10387
10450
  "trace-query-root-span-fields",
10388
10451
  "trace-query-root-entity-id",
10389
10452
  "trace-aggregate",